一、EF更新數據庫字段的三種方法
實體類
public class TestDbContext : DbContext { public DbSet<Test> Tests { get; set; } public TestDbContext() : base() { } } public class Test { public long ID { get; set; } public string Name { get; set; } public string Email { get; set; } public string Remarks { get; set; } }
創建數據
TestDbContext db = new TestDbContext(); db.Tests.Add(new Test() { Name = "測試1", Email= @"123@abc.com", Remarks = "測試1備注"}); db.Tests.Add(new Test() { Name = "測試2", Email = @"456@abc.com", Remarks = "測試2備注"}); db.SaveChanges();
第一種方法:
先查詢記錄,然后修改相應的屬性。此方法雖然多了一個查詢步驟,但是也由此利用了EF的自動跟蹤功能,后續操作比較方便。
比如,生成的SQL語句只會去修改相應的修改過的字段。
而且經測試發現,如果實體屬性值沒有改變,不會生成SQL語句,比如將下面的代碼執行兩次,第二次 SaveChanges() 方法不會執行SQL更新語句(注:由於還是會執行一次查詢,所以運行效率並沒有顯著提升)
TestDbContext db = new TestDbContext(); var test = db.Tests.Find(1); test.Remarks = "更新字段方法1"; db.SaveChanges();
第二種方法:
直接創建一個新的實體類,然后修改實體對象的狀態。此方法雖然少了一個查詢步驟,但生成的SQL語句將會修改全部字段,而且還要確保 ID 值存在。(注意:由於會修改全部字段,沒有設置的字段會被設置為null)
TestDbContext db = new TestDbContext(); Test test = new Test() { ID = 1, Remarks = "更新字段方法2" }; db.Entry(test).State = System.Data.Entity.EntityState.Modified; db.SaveChanges();
第三種方法:
創建一個實體類,將實體附加到數據庫上下文,然后修改相應屬性值的修改標識為 true ,此方法生成的SQL語句與第一種方法一樣,比較簡潔,而且還少了查詢步驟,只是后續的操作比較麻煩,因為要手動設置各個屬性的修改標識,還要確保 ID 值存在。
TestDbContext db = new TestDbContext(); Test test = new Test() { ID = 1, Remarks = "更新字段方法3" }; db.Tests.Attach(test); db.Entry(test).Property("Remarks").IsModified = true; db.SaveChanges();
生成的SQL語句(使用 SQL Server Profiler 跟蹤數據庫所得):
第一種方法:(注:第一種方法的代碼執行一次后再次執行沒有跟蹤到更新數據庫的SQL語句)
exec sp_executesql N‘UPDATE [dbo].[Tests] SET [Remarks] = @0 WHERE ([ID] = @1) ‘,N‘@0 nvarchar(max) ,@1 bigint‘,@0=N‘更新字段方法1‘,@1=1
第二種方法:(注:查看SQL語句可知,使用此方法如果不注意可能會導致數據被錯誤修改)
exec sp_executesql N‘UPDATE [dbo].[Tests] SET [Name] = NULL, [Email] = NULL, [Remarks] = @0 WHERE ([ID] = @1) ‘,N‘@0 nvarchar(max) ,@1 bigint‘,@0=N‘更新字段方法2‘,@1=1
第三種方法
exec sp_executesql N‘UPDATE [dbo].[Tests] SET [Remarks] = @0 WHERE ([ID] = @1) ‘,N‘@0 nvarchar(max) ,@1 bigint‘,@0=N‘更新字段方法3‘,@1=1
二、EF中添加記錄時獲取自增ID值
Entity Framework在將數據插入數據庫時,如果主鍵字段是自增標識列,會將該自增值返回給實體對象對應的屬性。
比如下面添加博客隨筆至數據庫的示例代碼:

var blogPost = new BlogPost() { Author = "博客園", Title = "程序員的網上家園" }; using (BlogDbContext context = new BlogDbContext()) { context.BlogPosts.Add(blogPost); context.SaveChanges(); return blogPost.ID; }
SaveChanges()之后,blogPost.ID的值就是數據庫中對應自增標識列的值。
看一下Entity Framework生成的SQL語句:
exec sp_executesql N'insert [dbo].[blog_Content]([Title],[Author])
values (@0, @1)
select [ID]
from [dbo].[blog_Content]
where @@ROWCOUNT > 0 and [ID] = scope_identity()',
N'@0 nvarchar(128),@1 nvarchar(128),',@0=N'程序員的網上家園',@1=N'博客園'
EF通過scope_identity()獲取自增列的值,而且我們沒有對BlogPost的ID屬性進行任何設置,是EF智能地判斷出ID就是自增標識列。
在以前沒有使用Entity Framework的時代,用的是存儲過程,存儲過程有一堆參數,實體對象的屬性值要一一對應地賦值給這些參數,執行存儲過程之后,還要通過ParameterDirection.Output的參數獲取自增ID的值。
三、EF中 一例對一個或多個實體的驗證失敗。有關詳細信息,請參閱“EntityValidationErrors”屬性的解決
注:add數據時 最好加異常捕獲(以防一些必填字段沒有賦值)
這個問題相信只要是做MVC的,都碰到過,也都知道錯誤的原因,就是觸發了定義的實例字段校驗規則。比如定義的不為空,但是為空了,或者定義的字段長度為50,但是超過50了。
可是有時雖然知道是這樣,但是具體問題解決的時候還是無從下手。我最近就碰到一個,知道是在更新某個表的時候出現的這個問題。可是在本地無法具現此錯誤,這個錯誤是在特定條件發生的,我並不知道此特定發生條件是什么,很郁悶。
在網上找了下,知道發生這個錯誤會觸發DbEntityValidationException異常,這個異常會有詳細的異常信息說明是哪個字段,出現了什么錯誤,只不過需要循環輸出。打算輸出到日志文件中看是什么錯誤。代碼如下:

try { es2.Update(examList); } catch (DbEntityValidationException dbEx) { foreach (var validationErrors in dbEx.EntityValidationErrors) { foreach (var validationError in validationErrors.ValidationErrors) { EventLog.Log(string.Format("Class: {0}, Property: {1}, Error: {2}", validationErrors.Entry.Entity.GetType().FullName, validationError.PropertyName, validationError.ErrorMessage), "error"); } } } catch (Exception ex) { throw; } finally { }
之所以用2個catch,是為了防止有其他非DbEntityValidationException 錯誤時,沒有錯誤日志。
DbEntityValidationException所在命名空間:System.Data.Entity.Validation
編譯,上傳到服務器,過了一會,查看日志文件,找到錯誤原因了。
Class: System.Data.Entity.DynamicProxies.ExamList_839A196D8FC4CF7E8A791B7F29782BA535E73532A1C3C2C00FD6EF30B6C4A660, Property: StudentAnswer, Error: 字段 StudentAnswer 必須是最大長度為 50 的字符串。
是StudentAnswer 字段長度不夠了。