网络编程
位置:首页>> 网络编程>> ASP.NET>> Entity Framework Core生成列并跟踪列记录

Entity Framework Core生成列并跟踪列记录

作者:Sweet-Tang  发布时间:2023-07-01 07:06:46 

标签:Entity,Framework,Core,生成,跟踪,列

注意:我使用的是 Entity Framework Core 2.0 (2.0.0-preview2-final)。正式版发布时,功能可能存在变动。

当您设计数据库时,有时需要添加列以跟踪记录何时更改,以及谁进行了更改。例如,您添加以下列:

  • CreatedAt

  • CreatedBy

  • LastUpdatedAt

  • LastUpdatedBy

您可以轻松地使用默认值和触发器来处理CreatedAtLastUpdatedAt列。老实说,创建触发器是件无聊的事情,你也不想自己做。此外,很难设置用户名,因为它是应用程序的信息;实际上,在Web程序的上下文中,它们只是连接到数据库的一个用户,因此,您无法使用数据库中CURRENT_USER函数设置跟踪列的值。

当然,您不想访问这个属性。相反,您希望 Entity Framework 为您自动执行,所以,解决的办法是调用SaveChangesSaveChangesAsync方法之前自动设置这些属性的值。在模型中有多种方法处理这些属性:您可以在模型中添加读写(R/W)属性;您也可以使用只读(R/O)属性;还有一种方案,您可以使用阴影(Shadow)属性。在最后一种方案中,是将列不映射到模型中的属性,只有 Entity Framework 才知道这些属性。是的,EF Core 非常灵活:)

我们用 Entity Framework 实现这3种方案!

读写属性

在这种情况下,实体将具有读/写属性。这意味着,您可以在代码中更改属性的值。

public interface ITrackable
{
   DateTime CreatedAt { get; set; }
   string CreatedBy { get; set; }
   DateTime LastUpdatedAt { get; set; }
   string LastUpdatedBy { get; set; }
}

public class Post : ITrackable
{
   public int Id { get; set; }
   public string Title { get; set; }
   public string Content { get; set; }

public DateTime CreatedAt { get; set; }
   public string CreatedBy { get; set; }
   public DateTime LastUpdatedAt { get; set; }
   public string LastUpdatedBy { get; set; }
}

现在,我们将更改SaveChanges方法的默认行为来设置跟踪属性的值。要小心,有两个方法要重写:SaveChanges和异步方法SaveChangesAsync。在代码中遍历上下文中跟踪的实体,并根据状态设置跟踪属性的值。代码非常简单:

public class BloggingContext : DbContext
{
   public DbSet<Post> Posts { get; set; }

public override int SaveChanges(bool acceptAllChangesOnSuccess)
   {
       OnBeforeSaving();
       return base.SaveChanges(acceptAllChangesOnSuccess);
   }

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
   {
       OnBeforeSaving();
       return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
   }

private void OnBeforeSaving()
   {
       var entries = ChangeTracker.Entries();
       foreach (var entry in entries)
       {
           if (entry.Entity is ITrackable trackable)
           {
               var now = DateTime.UtcNow;
               var user = GetCurrentUser();
               switch (entry.State)
               {
                   case EntityState.Modified:
                       trackable.LastUpdatedAt = now;
                       trackable.LastUpdatedBy = user;
                       break;

case EntityState.Added:
                       trackable.CreatedAt = now;
                       trackable.CreatedAt = user;
                       trackable.LastUpdatedAt = now;
                       trackable.LastUpdatedBy = user;
                       break;
               }
           }
       }
   }

private string GetCurrentUser()
   {
       return "UserName"; // TODO implement your own logic
   }
}

这个方案有个不容忽视的问题,属性是可写的,您不希望在代码中设置属性的值。让我们看看如何使用只读属性。

只读属性

Entity Framework Core 允许将列映射到字段。这样,您可以使用只读属性,我们来修改 Post类:

public class Post
{
   private DateTime _createdAt;
   private string _createdBy;
   private DateTime _lastUpdatedAt;
   private string _lastUpdatedBy;

public int Id { get; set; }
   public string Title { get; set; }
   public string Content { get; set; }

public DateTime CreatedAt => _createdAt;
   public string CreatedBy => _createdBy;
   public DateTime LastUpdatedAt => _lastUpdatedAt;
   public string LastUpdatedBy => _lastUpdatedBy;
}

上面的代码有点麻烦,但属性现在是只读的。您需要指引 Entity Framework 使用这些字段来设置列的值。

public class BloggingContext : DbContext
{
   public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
       base.OnModelCreating(modelBuilder);

modelBuilder.Entity<Post>()
           .Property(post => post.CreatedAt)
           .HasField("_createdAt");

modelBuilder.Entity<Post>()
           .Property(post => post.CreatedBy)
           .HasField("_createdBy");

modelBuilder.Entity<Post>()
           .Property(post => post.LastUpdatedAt)
           .HasField("_lastUpdatedAt");

modelBuilder.Entity<Post>()
           .Property(post => post.LastUpdatedBy)
           .HasField("_lastUpdatedBy");
   }
}

您不能像以前那样实现OnBeforeSaving方法,因为属性没有setter。不过,您可以使用 ChangeTracker来指引 Entity Framework 为属性设置值。这种方式无需访问属性或字段。

private void OnBeforeSaving()
   {
       var entries = ChangeTracker.Entries();
       foreach (var entry in entries)
       {
           if (entry.Entity is Post post)
           {
               var now = DateTime.UtcNow;
               var user = GetCurrentUser();
               switch (entry.State)
               {
                   case EntityState.Modified:
                       entry.CurrentValues["LastUpdatedAt"] = now;
                       entry.CurrentValues["LastUpdatedBy"] = user;
                       break;

case EntityState.Added:
                       entry.CurrentValues["CreatedAt"] = now;
                       entry.CurrentValues["CreatedBy"] = user;
                       entry.CurrentValues["LastUpdatedAt"] = now;
                       entry.CurrentValues["LastUpdatedBy"] = user;
                       break;
               }
           }
       }
   }
}

我认为应是使用的方式,因为这种方式很清晰。

不定义属性

在某些情况下,您可能需要设置列的值而不在模型中定义属性。例如,您可能会公开最后更新的日期,但不会显示谁做了更改。Entity Framework Core 可以通过使用阴影(Shadow )属性来处理这个。阴影属性在OnModelCreating方法中声明,但在类中不存在。您可以使用ChangeTracker读取和写入阴影属性的值。

public class Post
{
   public int Id { get; set; }
   public string Title { get; set; }
   public string Content { get; set; }
}
public class BloggingContext : DbContext
{
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
       base.OnModelCreating(modelBuilder);

// Declare properties
       modelBuilder.Entity<Post>().Property<DateTime>("CreatedAt");
       modelBuilder.Entity<Post>().Property<string>("CreatedBy");
       modelBuilder.Entity<Post>().Property<DateTime>("LastUpdatedAt");
       modelBuilder.Entity<Post>().Property<string>("LastUpdatedBy");
   }

private void OnBeforeSaving()
   {
       var entries = ChangeTracker.Entries();
       foreach (var entry in entries)
       {
           if (entry.Entity is Post post)
           {
               var now = DateTime.UtcNow;
               var user = GetCurrentUser();
               switch (entry.State)
               {
                   case EntityState.Modified:
                       entry.CurrentValues["LastUpdatedAt"] = now;
                       entry.CurrentValues["LastUpdatedBy"] = user;
                       break;

case EntityState.Added:
                       entry.CurrentValues["CreatedAt"] = now;
                       entry.CurrentValues["CreatedBy"] = user;
                       entry.CurrentValues["LastUpdatedAt"] = now;
                       entry.CurrentValues["LastUpdatedBy"] = user;
                       break;
               }
           }
       }
   }
}

来源:https://www.cnblogs.com/tdfblog/p/entity-framework-core-generate-tracking-columns.html

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com