02-EF Core笔记之保存数据

时间:2021-07-09 04:56:57

EF Core通过ChangeTracker跟踪需要写入数据库的更改,当需要保存数据时,调用DbContext的SaveChanges方法完成保存。

基本的添加、更新、删除操作示例如下:

using (var context = new BloggingContext())
{
// seeding database
context.Blogs.Add(new Blog { Url = "http://sample.com/blog" });
context.Blogs.Add(new Blog { Url = "http://sample.com/another_blog" });
context.SaveChanges();
} using (var context = new BloggingContext())
{
// 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();
}

关联数据

在EF Core中,除了独立的模型外,还有与模型关联的数据,这部分数据通过独立模型添加到模型中,在SaveChanges时将会持久化到数据库中。例如:

using (var context = new BloggingContext())
{
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();
}

在这段代码中,Blog对象和三个Post对象将会被持久化。

如果要更改关系的引用,可将Post对象中的Blog引用设置为其它Blog对象即可:

using (var context = new BloggingContext())
{
var blog = new Blog { Url = "http://blogs.msdn.com/visualstudio" };
var post = context.Posts.First(); post.Blog = blog;
context.SaveChanges();
}

如果要删除关系,只需将Post对象中的Blog引用设置为null即可,此时EF Core将判断是否为必须关系,如果为必须关系,则从数据库中删除Post对象,如果为非必须关系,则将数据库中对应的外键设置为null。

级联删除

级联删除是数据库的概念,意思是当主体被删除时,所有依赖该主体的项(通过外键关联)也会被自动删除。

EF Core对于提供了更细粒度的管理,它允许我们定义删除行为,来控制依赖关系被移除时,如何处理关系的子实体。需要注意的是,EF Core的删除行为仅对已加载的数据生效,如果关系未加载到内存中,则超出了EF Core的管控范围。

事务

事务允许以原子方式处理多个数据库操作。 如果已提交事务,则所有操作都会成功应用到数据库。 如果已回滚事务,则所有操作都不会应用到数据库。

默认情况下,每次SaveChanges方法的所保存的所有更改都将在一个事务中,要么全部保存成功,要么全部保存失败。此种情况已能满足大多数应用的需要。

共享事务(通过共享连接实现)

共享事务仅对关系型数据库有效,因为此机制用到了DbConnection和DbTransaction。要实现该机制,首先要在多个DbContext之间共享数据库连接。

以下代码演示了如何共享数据库连接:

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);
}
}

对于上面代码中的BloggingContext,可以先创建DbConnection来进行实例化,也可以通过DbTransaction获取DbConnection来实例化。随后即可在同一个DbConnection上共享事务了。

使用 System.Transactions(环境事物)

如果需要跨较大作用域进行协调,则可以使用环境事务。例如:

using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open(); try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery(); // Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options; using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
} // Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
scope.Complete();
}
catch (System.Exception)
{
// TODO: Handle failure
}
}
}

显示登记到环境事务中:

context.Database.EnlistTransaction(transaction);

使用环境事务前,需验证使用的提供程序是否支持环境事务。

并发控制

数据库并发指多个进程或用户同时访问或更改数据库中的相同数据的情况。 并发控制指的是用于在发生并发更改时确保数据一致性的特定机制。

EF Core采用乐观并发控制来解决并发冲突问题。工作原理:每当在 SaveChanges 期间执行更新或删除操作时,会将数据库上的并发令牌值与通过 EF Core 读取的原始值进行比较。如果一致则可以完成操作,如果不一致,则终止事务。

在关系数据库上,EF Core 会对任何 UPDATE 或 DELETE 语句的 WHERE 子句中的并发令牌值进行检查。 执行这些语句后,EF Core 会读取受影响的行数。如果未影响任何行,将检测到并发冲突,并且 EF Core 会引发 DbUpdateConcurrencyException。

在检测到并发冲突后,EF Core会引发DbUpdateConcurrencyException异常,该异常中提供了一些有用的参数来帮助我们解决冲突:

  • “当前值”是应用程序尝试写入数据库的值。
  • “原始值”是在进行任何编辑之前最初从数据库中检索的值。
  • “数据库值”是当前存储在数据库中的值。

此处可进行数据合并或用户选择等方式决策如何解决冲突。

状态断开对象的处理

EF Core判断更新或添加数据是通过ChangeTrancker来进行的,这个操作需要在同一个DbContext中进行,而web应用通常先查询到数据,然后将数据发送到客户端进行相应的操作,随后再由客户端提交到服务器端,此时实体所在的DbContext已发生变化,如何判断对实体进行更新或添加就成了一个问题。

解决这个问题最简单的方法是,更新和添加使用不同的web路径,服务器端通过提供Add方法和Update方法来区分操作。

除此之外,如果实体使用自动生成的主键,EF Core则可以通过判断主键是否为默认值(null、0)来判断是新增或更新。并且,对于这种情况,可直接使用DbContext的Update操作进行,在Update操作内部会完成该判断。

如果实体的主键不是自动生成的,则需要手工判断实体是否存在。下面的代码提供了一种添加或更新的思路:

public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs.Find(blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
} context.SaveChanges();
}

SetValues方法将比较两个实体的值,并对发生改变的属性进行重新赋值,未发生改变的值保持不变,生成更新数据库语句时也仅更新改变的字段。

对于依赖关系的操作,同样遵循以上几种方式。

删除操作

对于删除操作,如果是删除一个对象,则可以明确该对象的主键,并从数据库中移除,此种情况不进行探讨。

这里需要探讨的是,当对依赖关系中的列表进行部分删除,如何进行更新的问题。例如Blog对象中有多个Post对象,如果从Blog中删除部分Post,则意味着直接移除了Post对象,此时如果是断开连接的情况,则EF Core无法跟踪到Post实体列表的变更,从而导致无法正确的处理删除。

一种可用的方案是采用软删除,将数据标记为已删除,此时的操作与更新相同。然后在查询数据时,使用查询筛选器,将标记为已删除的数据过滤掉,从而达到删除的效果。

对于物理删除,一种可用的方案是对Post列表进行对比,相应的代码如下:

public static void InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId); if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId); if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
} foreach (var post in existingBlog.Posts)
{
if (!blog.Posts.Any(p => p.PostId == post.PostId))
{
context.Remove(post);
}
}
} context.SaveChanges();
}

02-EF Core笔记之保存数据的更多相关文章

  1. EntityFramework Core笔记:保存数据(4)

    1. 基本保存 每个DBContext实例都有一个ChangeTracker,负责跟踪需要写入数据库的更改.当实例发生更改时,更改会被记录在ChangeTracker中,在调用 SaveChanges ...

  2. EF Core利用Transaction对数据进行回滚保护

    What? 首先,说一下什么是EF Core中的Transaction Transaction允许以原子方式处理多个数据库操作,如果事务已提交,则所有操作都应用于数据库,如果事务回滚,则没有任何操作应 ...

  3. 03-EF Core笔记之查询数据

    EF Core使用Linq进行数据查询. 基本查询 微软提供了一百多个示例来演示查询,地址:https://code.msdn.microsoft.com/101-LINQ-Samples-3fb98 ...

  4. Dapper&comma; Ef core&comma; Freesql 插入大量数据性能比较(一)

    需求:导入9999行数据时Dapper, Ef core, Freesql 谁的性能更优,是如何执行的,级联增加谁性能更佳. 确认方法:sql server 的 sys.dm_exec_query_s ...

  5. EntityFramework Core笔记:查询数据(3)

    1. 基本查询 1.1 加载全部数据 using System.Linq; using (var context = new LibingContext()) { var roles = contex ...

  6. Dapper&comma; Ef core&comma; Freesql 插入大量数据性能比较(二)

    在上一篇文章中,我们比较出单表插入9999行数据,Dapper > EfCore > Freesql.在本文中,我们来看看级联插入 构建9999行数据 List<Entity> ...

  7. 深入理解 EF Core:EF Core 写入数据时发生了什么?

    阅读本文大概需要 14 分钟. 原文:https://bit.ly/2C67m1C 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能 ...

  8. 深入理解 EF Core:使用查询过滤器实现数据软删除

    原文:https://bit.ly/2Cy3J5f 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能会去除一些本人实在不知道如何组织 ...

  9. ef core数据迁移的一点小感悟

    ef core在针对mysql数据迁移的时候,有些时候没法迁移...有两种情况没法迁移,一种是因为efcore的bug问题导致没法迁移,这个在github上有个问题集,另外一种是对数据表进行较大幅度的 ...

随机推荐

  1. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  2. 学习笔记--Git安装 创建版本库 图文详解

    一.Git下载 在Windows上安装git,一般为msysgit,官网地址:http://git-scm.com/ 我下载的是Git-1.9.2-preview20140411.exe 二.Git安 ...

  3. ORA-16018&colon; cannot use LOG&lowbar;ARCHIVE&lowbar;DEST with LOG&lowbar;ARCHIVE&lowbar;DEST&lowbar;n or DB&lowbar;RECOVERY&lowbar;FILE&lowbar;DEST【error收集】

    之前一直没有注意一个事情, 关于设置archive归档路径设置的问题. 设置数据库为归档模式的命令: 1.首先要切换到mount状态: 2.执行alter system archivelog; 3.查 ...

  4. phpcms v9附件上传后显示链接名称如何改为附件名称?

    使用phpcms v9的朋友都知道,v9在后台添加内容的时候上传附件显示的是一个链接,这样太不人性化了,那怎么显示文件名称呢 ?小编以前发布文章的时候都是上传后复制链接在给文字加上超链接的,这样非常的 ...

  5. c&num;拷贝

    话谈c#拷贝 c#中类型分为值类型和引用类型,值类型对象赋值是本身就是赋的自身的一个副本,而引用类型赋值时则赋的是指向堆上的内存,假如我们不想赋这个地址而想将对象赋过去要怎么做呢?首先要知道拷贝分为浅 ...

  6. 最短路 spfa 算法 &amp&semi;&amp&semi; 链式前向星存图

    推荐博客  https://i.cnblogs.com/EditPosts.aspx?opt=1 http://blog.csdn.net/mcdonnell_douglas/article/deta ...

  7. async &comma;await 有图有真相

    1.async返回的一定是promise对象 2.await确实可以同步:

  8. 20165214 2018-2019-2 《网络对抗技术》Exp1 PC平台逆向破解 Week3

    <网络对抗技术>Exp1 PC平台逆向破解之"逆向及Bof基础实践说明" Week3 一. 实验预习 1.什么是漏洞?漏洞有什么危害? 漏洞就是在计算机硬件.软件.协议 ...

  9. cisco ospf

    ospf配置:(Open Shortest Path First)PC0 and PC1 via Ospf to communicate. Network map as below.Environme ...

  10. BZOJ4764弹飞大爷——LCT

    题目描述 自从WC退役以来,大爷是越来越懒惰了.为了帮助他活动筋骨,也是受到了弹飞绵羊一题的启发,机房的小伙伴们 决定齐心合力构造一个下面这样的序列.这个序列共有N项,每项都代表了一个小伙伴的力量值, ...