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 ...
随机推荐
- 简单工厂模式的C++、Java实现
1.简单工厂模式UML UML如下: 图1. 简单工厂模式UML 2.C++实现 类视图如下: 图2. C++实现简单工厂模式类视图 其中,SimpleFactory实现为: Product * Si ...
- Scrapy爬虫requests
requests 模块 模块的由来: 浏览器可以浏览网站, 是由于浏览器发送了requests , 各种请求.打开一个网站可能有几十到几百个请求. 从而服务器端会反馈各种因应不同请求生成的数据. 我们 ...
- Redis 实现分布式锁
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- Java集合排序
[ 1.对普通的包装类基本数据类型的list数组排序(Integer,Long,Double) ] Collections.sort(List list) [例] List<Long> m ...
- Vue2.0中的系统指令
v-on注册事件 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...
- [英中双语] Pragmatic Software Development Tips 务实的软件开发提示
Pragmatic Software Development Tips务实的软件开发提示 Care About Your Craft Why spend your life developing so ...
- css tips: 清除float影响,containing的div跟随floated sub等
/** * For modern browsers * 1. The space content is one way to avoid an Opera bug when the * content ...
- ns2.35-classifier.cc
line143:recv() /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyri ...
- Congestion Avoidance in TCP
Congestion Avoidance in TCP Consequence of lack of congestion control When a popular resource is sha ...
- 如何使用Flashfxp上传下载文件
一.首先您本地电脑需要安装flashfxp软件,您可以通过百度搜索下载. 二.我们打开flashfxp,然后在右上角点击“会话”,再点击“快速连接”,如下图. 三.弹出“如下图”窗口.请输入FTP连接 ...