asp.net core系列 34 EF保存数据(1)
一. 基本数据
每个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 } } }
未完...
参考文献:
上一篇: 详解C#异常处理
推荐阅读
-
asp.net core 系列 20 EF基于数据模型创建数据库
-
asp.net core系列 29 EF模型配置(查询类型,关系数据库建模)
-
asp.net core系列 35 EF保存数据(2) -- EF系列结束
-
asp.net core系列 32 EF查询数据 必备知识(1)
-
asp.net core 系列 21 EF现有数据库进行反向工程
-
asp.net core系列 31 EF管理数据库架构--必备知识 反向工程
-
asp.net core系列 33 EF查询数据 (2)
-
asp.net core系列 30 EF管理数据库架构--必备知识 迁移
-
(26)ASP.NET Core EF保存(基本保存、保存相关数据、级联删除、使用事务)
-
asp.net core 系列 20 EF基于数据模型创建数据库