一. 基本數據 每個EF上下文實例都有一個 ChangeTracker(更改跟蹤器),它負責跟蹤需要寫入資料庫的更改。 當更改實體類的實例時(修改屬性,刪除實例,新建實例等),這些更改會記錄在 ChangeTracker 中,然後在調用 SaveChanges 時被寫入資料庫。 此資料庫提供程式負責 ...
一. 基本數據
每個EF上下文實例都有一個 ChangeTracker
(更改跟蹤器),它負責跟蹤需要寫入資料庫的更改。 當更改實體類的實例時(修改屬性,刪除實例,新建實例等),這些更改會記錄在 ChangeTracker
中,然後在調用 SaveChanges
時被寫入資料庫。 此資料庫提供程式負責將更改,轉換為特定的資料庫操作(例如,關係資料庫的 INSERT
、UPDATE
和 DELETE
命令)。
1.1 添加數據
使用 DbSet.Add 方法添加實體類的新實例。 調用 SaveChanges 時,數據將插入到資料庫中。
var blog = new Blog { Url = "http://sample.com" }; context.Blogs.Add(blog); context.SaveChanges();
1.2 更新數據
EF 將通過ChangeTracker
自動檢測上下文對現有實體所做的更改。 檢測包括從資料庫查詢的實體,以及之前添加並保存到資料庫的實體。例如下麵示例,從資料庫查詢的實體,只需通過賦值來修改屬性,然後調用 SaveChanges 即可
var blog = context.Blogs.First(); blog.Url = "http://sample.com/blog"; context.SaveChanges();
1.3 刪除數據
使用 DbSet.Remove 方法刪除實體類的實例。
(1)如果實體已存在於資料庫中,則將在“SaveChanges”期間刪除該實體,並且刪除資料庫中的數據。
(2)如果實體尚未保存到資料庫(即跟蹤為“已添加”),則在調用SaveChanges時,該實體會從上下文中移除且不再插入。
//第一種情況 var blog = context.Blogs.First(); context.Blogs.Remove(blog); context.SaveChanges();
//第二種情況 Blog blog = new Blog() { Url = "www.baidu.com" }; //添加後該實體為已添加,尚未保存到資料庫 BloggingContext.Blogs.Add(blog); //刪除實體類的實例 BloggingContext.Blogs.Remove(blog); //從上下文中移除blog實體 BloggingContext.SaveChanges();
1.4 單個 SaveChanges 中的多個操作
可以將多個添加/更新/刪除操作合併到對“SaveChanges”的單個調用。
// add context.Blogs.Add(new Blog { Url = "http://sample.com/blog_one" }); context.Blogs.Add(new Blog { Url = "http://sample.com/blog_two" }); // update var firstBlog = context.Blogs.First(); firstBlog.Url = ""; // remove var lastBlog = context.Blogs.Last(); context.Blogs.Remove(lastBlog); context.SaveChanges();
二.關聯數據
除了上面對獨立實體進行保存外,還可以保存模型中定義的關係(主體實體和依賴實體)
1.1 添加關係數據
如果創建多個新的依賴實體,在添加主體到上下文時,也會添加其他依賴實體。在下麵的示例中,blog博客和三篇相關文章post將全部被插入資料庫中。 由於可通過 Blog.Posts
導航屬性訪問這些文章,因此可發現並添加它們(Posts三條信息到資料庫)。
var blog = new Blog { Url = "http://blogs.msdn.com/dotnet", Posts = new List<Post> { new Post { Title = "Intro to C#" }, new Post { Title = "Intro to VB.NET" }, new Post { Title = "Intro to F#" } } }; context.Blogs.Add(blog); context.SaveChanges();
1.2 添加相關實體
如果從上下文跟蹤的實體的導航屬性中,引用新實體。將發現該新實體並將其插入到資料庫中。在下麵的示例中,會保存 post 實體,因為該實體會添加到已從資料庫中提取的 blog 實體的 Posts 屬性。
var blog = context.Blogs.Include(b => b.Posts).First(); var post = new Post { Title = "Intro to EF Core" }; //引用新實體, blog.Posts.Add(post);
context.SaveChanges();
1.3 更改關係
如果更改實體的導航屬性,則將對資料庫中的外鍵列進行相應的更改。在下麵的示例中,post
依賴實體更新關係,關聯新的 blog
主體
實體,新 blog
會插入到資料庫中, post
的導航屬性引用的新實體blog。
//新增一個主體實體 var blog = new Blog { Url = "http://blogs.msdn.com/visualstudio" }; var post = context.Posts.First(); //post更新關係 post.Blog = blog; context.SaveChanges();
1.4 刪除關係
在下麵的示例中,對 Blog
和 Post
之間的關係配置了級聯刪除,因此將從資料庫中刪除 post
實體。
var blog = context.Blogs.Include(b => b.Posts).First(); var post = blog.Posts.First(); //刪除一條post依賴實體 blog.Posts.Remove(post); context.SaveChanges();
三.聯級刪除
級聯刪除是指:允許在刪除某行時,自動觸發刪除相關的行。 即依賴實體與主實體的關係已斷開時,自動刪除該子實體,這通常稱為“刪除孤立項”。
3.1 實體刪除示例
var blog = context.Blogs.Include(b => b.Posts).First(); var posts = blog.Posts.ToList(); context.Remove(blog); context.SaveChanges();
-- 解發SaveChanges後,刪除主體時,先自動刪除子實體(具有必選或可選關係的 DeleteBehavior.Cascade級聯) DELETE FROM [Posts] WHERE [PostId] = 1 DELETE FROM [Posts] WHERE [PostId] = 2 DELETE FROM [Blogs] WHERE [BlogId] = 1
關於聯級刪除,涉及到的外鍵約束包括:可選關係(可以為 null 的外鍵) 和 必須關係(不可為 null 的外鍵),參考官網
四. 事務
事務是指:允許以原子方式處理多個資料庫操作。 如果已提交事務,則所有操作都會成功應用到資料庫。 如果已回滾事務,則所有操作都不會應用到資料庫。關於數據事務的詳細介紹可網上查看。
4.1 預設事務行為(隱式事務)
預設情況下,如果資料庫提供程式支持事務,則會在事務中應用對 SaveChanges() 的單一調用中的所有更改。 這意味著SaveChanges() 可保證提交到資料庫完全成功或在出現錯誤時全部回滾。
下麵來演示預設事務行為,建立blogs表的url欄位唯一約束。
--創建唯一索引 CREATE UNIQUE INDEX IX_Url ON Blogs(Url)
//修改數據 var blog = BloggingContext.Blogs.First(); blog.Title = "123"; //插入數據,再次插入違反約束 BloggingContext.Blogs.Add(new Blog() { Url = "www.baidu.com" }); BloggingContext.SaveChanges();
檢查資料庫,該表數據沒有做任何更改,通過sql 監聽如下所示:
4.2 控制事務(顯示事務)
對於大多數應用程式,上面4.1 預設事務已足夠。 如果應用程式要求被視為有必要,則應該僅手動控制事務。
可以使用 DbContext.Database
API 開始、提交和回滾事務。 以下示例顯示了兩個 SaveChanges()
操作以及正在單個事務中執行的 LINQ 查詢。並非所有資料庫提供程式都支持事務。 調用事務 API 時,某些提供程式可能會引發異常或不執行任何操作。
using (var transaction = context.Database.BeginTransaction()) { try { context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" }); context.SaveChanges(); context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" }); context.SaveChanges(); var blogs = context.Blogs .OrderBy(b => b.Url) .ToList(); // Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails transaction.Commit(); } catch (Exception) { // TODO: Handle failure } }
4.3 跨上下文事務(僅限關係資料庫)
可以跨多個EF上下文實例,共用一個事務。 此功能僅在使用關係資料庫提供程式時才可用,因為該提供程式需要使用特定於關係資料庫的 DbTransaction
和 DbConnection
。
(1) 允許在外部提供連接
是指共用 DbConnection
時,需要在DbContext構造上下文時向其中傳入連接的功能。最簡單方式是停止使用預設的 DbContext.OnConfiguring
方法來配置上下文,在外部創建 DbContextOptions
,然後將其傳遞到上下文構造函數。如下所示:
public class BloggingContext : DbContext { private DbConnection _connection; public BloggingContext(DbConnection connection) { _connection = connection; } public DbSet<Blog> Blogs { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_connection); } }
(2)共用連接和跨上下文的事務
//創建共用連接 var options = new DbContextOptionsBuilder<BloggingContext>() .UseSqlServer(new SqlConnection(connectionString)) .Options; using (var context1 = new BloggingContext(options)) { using (var transaction = context1.Database.BeginTransaction()) { try { context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" }); context1.SaveChanges(); using (var context2 = new BloggingContext(options)) { context2.Database.UseTransaction(transaction.GetDbTransaction()); var blogs = context2.Blogs .OrderBy(b => b.Url) .ToList(); } // Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails transaction.Commit(); } catch (Exception) { // TODO: Handle failure } } }
未完...
參考文獻: