ef mysql自动更新_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 classPerson

{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 classBook

{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 classEFDemoContext : DbContext

{publicEFDemoContext()

{

}public EFDemoContext(DbContextOptionsoptions)

:base(options)

{

}public virtual DbSet Book { get; set; }public virtual DbSet Person { get; set; }protected override voidOnConfiguring(DbContextOptionsBuilder optionsBuilder)

{if (!optionsBuilder.IsConfigured)

{

optionsBuilder.UseSqlServer("Server=localhost;User Id=sa;Password=1qaz!QAZ;Database=EFDemo");

}

}protected override voidOnModelCreating(ModelBuilder modelBuilder)

{

modelBuilder.Entity(entity =>{

entity.Property(e=> e.Id).HasColumnName("ID");

entity.Property(e=> e.BookDescription).HasMaxLength(100);

entity.Property(e=>e.CreateTime)

.HasColumnType("datetime")

.HasDefaultValueSql("(getdate())");

entity.Property(e=>e.Isbn)

.HasColumnName("ISBN")

.HasMaxLength(50);

entity.Property(e=> e.Name).HasMaxLength(50);

});

modelBuilder.Entity(entity =>{

entity.Property(e=> e.Id).HasColumnName("ID");

entity.Property(e=>e.CreateTime)

.HasColumnType("datetime")

.HasDefaultValueSql("(getdate())");

entity.Property(e=> e.Name).HasMaxLength(50);

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 classEFDbContext: EFDemoContext

{publicEFDbContext()

{//设置数据库Command永不超时

this.Database.SetCommandTimeout(0);//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的值是否设置为了系统当前时间:

classProgram

{//初始化Person表和Book表的数据

static voidInitializeDataToDB()

{var personJim = new Person() { Name="Jim", Age=20};var personTom= new Person() { Name = "Tom", Age = 30};var personSam = new Person() { Name = "Sam", Age = 25};var personJerry = new Person() { Name = "Jerry", Age = 35};var personHenry = new Person() { Name = "Henry", Age = 26};var bookScience = new Book() { Name = "Science", BookDescription= "Science", Isbn="0001"};var bookMath = new Book() { Name = "Math", BookDescription = "Math", Isbn = "0002"};var bookPhysics = new Book() { Name = "Physics", BookDescription = "Physics", Isbn = "0003"};var bookComputer = new Book() { Name = "Computer", BookDescription = "Computer", Isbn = "0004"};var bookEnglish = new Book() { Name = "English", BookDescription = "English", Isbn = "0005"};using (var efDbContext = newEFDbContext())

{

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 = newEFDbContext())

{//更改Person.Name为Tom的实体的Age属性值,这会导致personTom这个Person实体的EntityState变为Modified

Expression> expressionTom = p => p.Name == "Tom";var personTom =efDbContext.Person.First(expressionTom);

personTom.Age= 50;//更改Book.Name为Computer的实体的Isbn属性值,这会导致bookComputer这个Book实体的EntityState变为Modified

Expression> expressionComputer = b => b.Name == "Computer";var bookComputer =efDbContext.Book.First(expressionComputer);

bookComputer.Isbn= "1000";//由于上面DbContext中有两个实体的EntityState改变了,下面的SaveChanges方法会触发两次DbContext.ChangeTracker.StateChanged事件,在实体数据保存到数据库之前,自动更新personTom这个Person实体的UpdateTime属性值为系统当前时间

efDbContext.SaveChanges();

}

Console.WriteLine("Testing end!");

Console.ReadLine();

}

}

当执行完InitializeDataToDB方法后,数据库两张表的值:

Person表:

ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库_第1张图片

Book表:

ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库_第2张图片

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

Person表:

ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库_第3张图片

Book表:

ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库_第4张图片

我们可以看到Person表中列Name为Tom的行,其UpdateTime也被自动更新为了系统当前时间。这样数据库中所有带UpdateTime列的表,其UpdateTime列的值都会在EF Core中自动被更新,省去了很多冗余的代码。

你可能感兴趣的:(ef,mysql自动更新)