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

数据库


比如下面的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. Js获取request中的对象的属相值

    将这个值放在页面上.再取出来. 在body中定义隐藏属性的input按钮来接收request中的值: <input type="hidden" value="< ...

  2. ccf-201709-2 公共钥匙盒

    问题描述 有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家.每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中 ...

  3. java idea+ssm框架遇到的问题

    0.学习教程 http://www.cnblogs.com/jiekzou/p/9205117.html https://github.com/crossoverJie/SSM 1.gradle没刷新 ...

  4. 第三次scrum作业!

    1.小组成员 舒 溢 许嘉荣 唐 浩 黄欣欣 廖帅元 刘洋江 薛思汝 2.个人在小组第三次冲刺任务及其完成情况描述 根据小组讨论所分配任务,积极辅助组长以及各个成员,理清思路,编写代码,尽量在规定时间 ...

  5. scrum心得和团队作业

    一.学习scrum心得 敏捷的介绍 最近上课我们了解到了敏捷,很多人开始谈论敏捷开发.研究敏捷开发,那么究竟什么才是敏捷开发呢? 简单的说,敏捷开发是一种以人为核心.迭代.循序渐进的开发方法.在敏捷开 ...

  6. 佛系结对编程---四则运算(Core 第四组)

    ----by core 第四组 ( 邹卫其 范力 ) 一. 项目介绍 1. 能自动生成小学四则运算题目并给出答案,生成题目时可以选择下列参数:   1)生成题目数量   2)每道题目中运算数数量   ...

  7. sqlserver2008 insert语句性能

    在sqlserver2008中“新建查询”,执行批量添加语句的执行时间: declare @i int ) begin INSERT INTO [xxx].[dbo].[北京万奇亚讯科技_QueryL ...

  8. 【Leetcode】【Medium】Minimum Path Sum

    Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which ...

  9. xml布局显示需要预判断,可是还没有show出来,怎么办?

    最近在实际工作中遇到了一种情况,写一个音量条,音量条显示出来之前要判断系统的音量大小,然后给音量条设置显示的位置.解决办法有两种, 第一种:    m_pHostThread>MsgAsyncC ...

  10. css如何制作八边形

    随着技术的发展,css也越发强大,css可以制作很多有趣的图形,让我们一起来看一下如何使用css制作一个八边形吧.   方法/步骤     1新建一个html文件.如图:   在html文件上创建一个 ...