EF Core中怎么实现自动更新实体的属性值到数据库
我们在开发系统的时候,经常会遇到这种需求数据库表中的行被更新时需要自动更新某些列。
数据库
比如下面的Person表有一列UpdateTime,这列数据要求在行被更新后自动更新为系统的当前时间。
Person表:
CREATE TABLE [dbo].[Person](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Age] [int] NULL,
[CreateTime] [datetime] NULL,
[UpdateTime] [datetime] NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO ALTER TABLE [dbo].[Person] ADD CONSTRAINT [DF_Person_CreateTime] DEFAULT (getdate()) FOR [CreateTime]
GO
我们还有一个Book表,它没有UpdateTime列,那么这个表的数据在行更新时不要求自动更新任何列
Book表:
CREATE TABLE [dbo].[Book](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[BookDescription] [nvarchar](100) NULL,
[ISBN] [nvarchar](50) NULL,
[CreateTime] [datetime] NULL,
CONSTRAINT [PK_Book] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO ALTER TABLE [dbo].[Book] ADD CONSTRAINT [DF_Book_CreateTime] DEFAULT (getdate()) FOR [CreateTime]
GO
那么Person表的UpdateTime列如果映射到了EF Core的实体上的话,有办法在Person实体被Update的时候自动设置为系统当前时间吗?答案是当然有!
EF Core 实体
首先我们将这两张表映射到EF Core的实体对象上:
Person实体:
public partial class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
public DateTime? CreateTime { get; set; }
public DateTime? UpdateTime { get; set; }
}
Book实体:
public partial class Book
{
public int Id { get; set; }
public string Name { get; set; }
public string BookDescription { get; set; }
public string Isbn { get; set; }
public DateTime? CreateTime { get; set; }
}
EF Core的DB First生成的DbContext类EFDemoContext
public partial class EFDemoContext : DbContext
{
public EFDemoContext()
{
} public EFDemoContext(DbContextOptions<EFDemoContext> options)
: base(options)
{
} public virtual DbSet<Book> Book { get; set; }
public virtual DbSet<Person> Person { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=localhost;User Id=sa;Password=1qaz!QAZ;Database=EFDemo");
}
} protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>(entity =>
{
entity.Property(e => e.Id).HasColumnName("ID"); entity.Property(e => e.BookDescription).HasMaxLength(); entity.Property(e => e.CreateTime)
.HasColumnType("datetime")
.HasDefaultValueSql("(getdate())"); entity.Property(e => e.Isbn)
.HasColumnName("ISBN")
.HasMaxLength(); entity.Property(e => e.Name).HasMaxLength();
}); modelBuilder.Entity<Person>(entity =>
{
entity.Property(e => e.Id).HasColumnName("ID"); entity.Property(e => e.CreateTime)
.HasColumnType("datetime")
.HasDefaultValueSql("(getdate())"); entity.Property(e => e.Name).HasMaxLength(); entity.Property(e => e.UpdateTime).HasColumnType("datetime");
});
}
}
DbContext.ChangeTracker.StateChanged事件
之后最关键的一点到了,我们需要用到DbContext.ChangeTracker.StateChanged这个事件,这个事件会在DbContext中被Track的实体对象的EntityState状态发生变化时被触发,有多少个实体的EntityState状态变化了,它就会被触发多少次。
为此,我们需要再定义一个自定义的DbContext类EFDbContext,来继承DB First自动生成的EFDemoContext类:
//EFDbContext继承自EFDemoContext,EFDemoContext又继承自DbContext
public class EFDbContext: EFDemoContext
{
public EFDbContext()
{
//设置数据库Command永不超时
this.Database.SetCommandTimeout(); //DbContext.ChangeTracker.StateChanged事件,会在DbContext中被Track的实体其EntityState状态值发生变化时被触发
this.ChangeTracker.StateChanged += (sender, entityStateChangedEventArgs) =>
{
//如果实体状态变为了EntityState.Modified,那么就尝试设置其UpdateTime属性为当前系统时间DateTime.Now,如果实体没有UpdateTime属性,会抛出InvalidOperationException异常,所以下面要用try catch来捕获异常避免系统报错
if (entityStateChangedEventArgs.NewState == EntityState.Modified)
{
try
{
//如果是Person表的实体那么下面的Entry.Property("UpdateTime")就不会抛出异常
entityStateChangedEventArgs.Entry.Property("UpdateTime").CurrentValue = DateTime.Now;
}
catch(InvalidOperationException)
{
//如果上面try中抛出InvalidOperationException,就是实体没有属性UpdateTime,应该是表Book的实体
}
} //如果要自动更新多列,比如还要自动更新实体的UpdateUser属性值到数据库,可以像下面这样再加一个try catch来更新UpdateUser属性
//if (entityStateChangedEventArgs.NewState == EntityState.Modified)
//{
// try
// {
// entityStateChangedEventArgs.Entry.Property("UpdateUser").CurrentValue = currentUser;
// }
// catch (InvalidOperationException)
// {
// }
//}
};
} }
然后我们在Program.cs的Main方法中(我在本例建立的是一个.Net Core控制台程序)先初始化Person表和Book表的数据,然后再修改Person表和Book表的数据,看看被修改的Person表数据其列UpdateTime的值是否设置为了系统当前时间:
class Program
{
//初始化Person表和Book表的数据
static void InitializeDataToDB()
{
var personJim = new Person() { Name="Jim", Age= };
var personTom= new Person() { Name = "Tom", Age = };
var personSam = new Person() { Name = "Sam", Age = };
var personJerry = new Person() { Name = "Jerry", Age = };
var personHenry = new Person() { Name = "Henry ", Age = }; var bookScience = new Book() { Name = "Science", BookDescription= "Science", Isbn="" };
var bookMath = new Book() { Name = "Math", BookDescription = "Math", Isbn = "" };
var bookPhysics = new Book() { Name = "Physics", BookDescription = "Physics", Isbn = "" };
var bookComputer = new Book() { Name = "Computer", BookDescription = "Computer", Isbn = "" };
var bookEnglish = new Book() { Name = "English", BookDescription = "English", Isbn = "" }; using (var efDbContext = new EFDbContext())
{
efDbContext.Person.Add(personJim);
efDbContext.Person.Add(personTom);
efDbContext.Person.Add(personSam);
efDbContext.Person.Add(personJerry);
efDbContext.Person.Add(personHenry); efDbContext.Book.Add(bookScience);
efDbContext.Book.Add(bookMath);
efDbContext.Book.Add(bookPhysics);
efDbContext.Book.Add(bookComputer);
efDbContext.Book.Add(bookEnglish); efDbContext.SaveChanges();
}
} static void Main(string[] args)
{
Console.WriteLine("Testing start!"); //初始化Person表和Book表的数据
InitializeDataToDB(); //修改Person表和Book表的数据
using (var efDbContext = new EFDbContext())
{
//更改Person.Name为Tom的实体的Age属性值,这会导致personTom这个Person实体的EntityState变为Modified
Expression<Func<Person, bool>> expressionTom = p => p.Name == "Tom";
var personTom = efDbContext.Person.First(expressionTom);
personTom.Age = ; //更改Book.Name为Computer的实体的Isbn属性值,这会导致bookComputer这个Book实体的EntityState变为Modified
Expression<Func<Book, bool>> expressionComputer = b => b.Name == "Computer";
var bookComputer = efDbContext.Book.First(expressionComputer);
bookComputer.Isbn = ""; //由于上面DbContext中有两个实体的EntityState改变了,下面的SaveChanges方法会触发两次DbContext.ChangeTracker.StateChanged事件,在实体数据保存到数据库之前,自动更新personTom这个Person实体的UpdateTime属性值为系统当前时间
efDbContext.SaveChanges();
} Console.WriteLine("Testing end!");
Console.ReadLine();
}
}
当执行完InitializeDataToDB方法后,数据库两张表的值:
Person表:

Book表:

当Program.cs的Main方法运行完毕后,数据库两张表的值:
Person表:

Book表:

我们可以看到Person表中列Name为Tom的行,其UpdateTime也被自动更新为了系统当前时间。这样数据库中所有带UpdateTime列的表,其UpdateTime列的值都会在EF Core中自动被更新,省去了很多冗余的代码。
EF Core中怎么实现自动更新实体的属性值到数据库的更多相关文章
- Mybatis中resultMap的作用-解决实体类属性名和数据库字段不一致
解决实体类属性名和数据库字段不一致
- EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况
使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- [小技巧]EF Core中如何获取上下文中操作过的实体
原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...
- EF Core中如何通过实体集合属性删除从表的数据
假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系 Person表数据: Book表数据: 可以看到数据库Book表中所有的数据都属于Person表中"F ...
- EF Core中避免贫血模型的三种行之有效的方法(翻译)
Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...
- EF Core中如何正确地设置两张表之间的关联关系
数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...
- 文章翻译:ABP如何在EF core中添加数据过滤器
原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...
- 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)
原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...
随机推荐
- Centos 7 系统安装(简单步骤)
前面步骤忽略.进入安装步骤. 运行安装 到选择语言的时候最好选英文版,这里做模板,用的中文版 接着下一步到安装选项 在日期和时间里,选择上海时区 紧接着进行软件安装选择,如图安装就好 接着进行分区,也 ...
- SPOJ:COT2 Count on a tree II
题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h ...
- 计算(calc.cpp)
计算(calc.cpp) [问题描述] 小明在你的帮助下,破密了Ferrari设的密码门,正要往前走,突然又出现了一个密码门,门上有一个算式,其中只有“(”,“)”,“0-9”,“+”,“-”,“*” ...
- mui.ajax()和asp.net sql服务器数据交互【1】
简单的ajax和asp.net的交互,例如遍历数据,前端显示复杂内容没有添加代码,可自行研究!非常适合懂那么一点点的我们! 实现步骤: 1.APP前端HTML: <div class=" ...
- html中块级元素和行内元素
块级元素和行内元素的三个区别 1.行内元素与块级元素直观上的区别: 行内元素会在一条直线上排列,都是同一行,水平方向排列 块级元素独占一行,垂直方向排列.块级元素从新行开始结束接着一个断行 2.块级元 ...
- css flexbox 弹性布局
flexbox 即css flexible box layout. ie9及以下不支持flexbox. flex详细规范(https://www.w3.org/TR/css-flexbox/) 为什么 ...
- 轻松看懂机器学习十大常用算法 (Machine Learning Top 10 Commonly Used Algorithms)
原文出处: 不会停的蜗牛 通过本篇文章可以对ML的常用算法有个常识性的认识,没有代码,没有复杂的理论推导,就是图解一下,知道这些算法是什么,它们是怎么应用的,例子主要是分类问题. 每个算法都看了 ...
- 小Y的轮回之路——攒机装机、B150装win7
两个月前,陪伴我5年多的小Y(ideapad-y460N卡)突然大伤元气,硬盘跪了,显示屏也黑了一小块.本着经济实惠凑合用的态度换了个320G的硬盘,没想过几天显示屏情况加重,出现无数个红绿相间的线条 ...
- C++ virtual inheritance ZZ
虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类. 举例来说:假如类A和类B各自从类X派生(非虚继承且假设类X ...
- IE浏览器兼容问题(下)——IE6的常见问题
IE6常见兼容性问题 1.盒模型问题 (1)DTD问题 DTD:文档定义类型,规定了要遵循的书写规范. 如果不写DTD,高级浏览器还是可以正常加载,IE6会以怪异模式进行加载. 盒模型:正常应该是外扩 ...