我们在开发系统的时候,经常会遇到这种需求数据库表中的行被更新时需要自动更新某些列。

数据库


比如下面的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中怎么实现自动更新实体的属性值到数据库的更多相关文章

  1. Mybatis中resultMap的作用-解决实体类属性名和数据库字段不一致

    解决实体类属性名和数据库字段不一致

  2. EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况

    使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...

  3. 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

    项目开发中的一些注意事项以及技巧总结   1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...

  4. [小技巧]EF Core中如何获取上下文中操作过的实体

    原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...

  5. EF Core中如何通过实体集合属性删除从表的数据

    假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系 Person表数据: Book表数据: 可以看到数据库Book表中所有的数据都属于Person表中"F ...

  6. EF Core中避免贫血模型的三种行之有效的方法(翻译)

    Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...

  7. EF Core中如何正确地设置两张表之间的关联关系

    数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...

  8. 文章翻译:ABP如何在EF core中添加数据过滤器

    原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...

  9. 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...

随机推荐

  1. Python之异常设计(一)

    一 定义 异常分为两类:一类是自动触发异常如除零错误:另一类是通过raise触发. 二 为什么要使用异常 当程序运行时,如果检测到程序错误,Python就会引发异常,我们可以在程序中使用try语句捕获 ...

  2. JS加法相关

    1:首先JS是一种弱语言,但是同类型可以自己相加减 例如“a”+”b” 可以自动组成ab : 1+ 2 自动变成3 var data = 2; var currentPage = data; //2 ...

  3. The method setItems(String) in the type ForTokensTag is not applicable for the arguments (Object)

    1. 问题 看到这个错误以为是貌似jsp页面有误,c:forTokens标签用错了?? An error occurred at line: in the jsp file: /WEB-INF/pag ...

  4. inline-block元素,在同一行上下显示

    两个元素使用了inline-block,并列显示时,会上下显示,给人感觉不在同一行 原因:其中一个元素使用了overflow:hidden,导致了基线变更 解决:1.另一个元素也添加overflow: ...

  5. KMP算法的一个简单实现

    今天学习KMP算法,参考网上内容,实现算法,摘录网页内容并记录自己的实现如下: 原文出处: http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93M ...

  6. Git学习-Git配置(一)

    零.前言 Git是一个工具,就没必要把时间浪费在那些"高级"但几乎永远不会用到的命令上.一旦你真的非用不可了,到时候再自行Google或者请教专家也未迟. 如果你是一个开发人员,想 ...

  7. canvas.addEventListener()

    对 canvas 元素的事件监听用addEventListener()实现, 但是有一点缺陷是:canvas 不支持键盘事件,为了解决这个问题,可以采用以下两种方案: 方案一: key event - ...

  8. idea maven 依赖问题

    今天发现,原来idea引用maven依赖的时候,优先依赖的是本地.但是有个问题,如果我删除了一个packet,如果刚刚好本地另外一个项目里面也有这个对于的packet,idea的智能提示 是 将其导入 ...

  9. 根据viewport的size自动调整fontsize大小

    现在的网站必须要考虑mobile上访问的友好性,bootstrap作为mobile first的前端框架得到很多应用,它通过默认就使用.col-xs-xx定义的width,同时加上@media(min ...

  10. css best practice for big team and project

    推荐查看以下文章: https://segmentfault.com/a/1190000000704006 关于BEM,SMACSS,OOCSS的通俗易懂的介绍 http://philipwalton ...