【问题标题】:How to automatically set CreatedOn fields with Entity Framework on saved entity and its child properties如何在保存的实体及其子属性上使用实体框架自动设置 CreatedOn 字段
【发布时间】:2020-02-02 05:43:10
【问题描述】:

每当我调用databaseContext.SaveChanges() 时,在实体框架保存数据之前,我需要所有对象及其子类在CreatedOn 字段中填充DateTime.Now()。

详细信息:我有一个所有实体都继承自的 BaseEntity 类:

 public class BaseEntity
 {
        [Key]
        public virtual Guid Id{ get; set; }
        public DateTime CreatedOn { get; set; }
 }

只要保存了从上述BaseEntity 继承的实体,CreatedOn 属性就会被分配DateTime.Now。

这是一个可以做到这一点的基础存储库:

public abstract class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseEntity
{
     ....

     public virtual bool SaveChanges(TEntity entity)
     {            
         if (entity.CreatedOn == DateTime.MinValue)
             entity.CreatedOn = DateTime.Now;
         else
             entity.ModifiedOn = DateTime.Now;

         return _databaseContext.SaveChanges() > 0;           
     }
}

这很好用,但问题是只有对象本身的CreatedOn 属性得到更新。那里的任何子类可能不会更新。

如何更改SaveChanges() 方法中的逻辑以更新所有子类并同时设置它们的CreatedOn 日期?

我将提供一个示例来更清楚地说明这一点:想象下面的 User 对象实例添加到 dbContext 并分配了新的 Profile,以及分配了新的 Role 类实例,然后SaveChanges() 被调用:

public class User: BaseEntity
{
        [Key]
        public virtual Guid Id { get; set; }

        public UserRole Role { get; set; }
        public ProfileDetails Profile { get; set; }
        public DateTime CreatedOn { get; set; }
        public Guid CreatedBy { get; set; }
}

确保将CreatedOn 日期分配给子user.Role 以及user.Profile 对象(这两个类也继承自BaseEntity)的最佳方法是什么?我想过使用反射来检查CreatedOn 字段的子对象属性,但所有循环都感觉不对。有没有更好的办法?

【问题讨论】:

    标签: c# entity-framework inheritance entity-framework-6


    【解决方案1】:

    基本上,您应该在自己的 DB 上下文类中重写 SaveChanges 方法并使用 EF 更改跟踪器来获取所有新创建的对象,然后相应地设置它们的 CreatedOn 字段。

    类似这样的事情:

    public class DbContextBase : DbContext 
    {
        public override int SaveChanges() 
        {
            DateTime currentDateTime = DateTime.Now;
    
            // get all the entities in the change tracker - this could be optimized
            // to fetch only the entities with "State == added" if that's the only 
            // case you want to handle
            IEnumerable<DbEntityEntry<BaseEntity>> entities = ChangeTracker.Entries<BaseEntity>();
    
            // handle newly added entities
            foreach (DbEntityEntry<BaseEntity> entity in entities.Where(e => (e.State == EntityState.Added)) 
            {
                // set the CreatedOn field to the current date&time
                entity.Entity.CreatedOn = currentDateTime;
            }
    
            // to the actual saving of the data
            return base.SaveChanges();
        }
    }
    

    当然你可以改进:

    • 同样处理带有e.State == EntityState.Modified 的实体并在这种情况下设置ModifiedOn 字段
    • 添加一些自定义异常处理来处理常见问题案例
    • 还有更多 - 天空和您的想象力是极限!

    【讨论】:

    • 这看起来是一个不错的解决方案,除了 ChangeTracker 是 EF Core 的一部分?它不是实体框架 6 的一部分。
    • @InspiredBy:这是一个 EF 6 解决方案,我已经在生产环境中使用了好几个月 - 这不是 EF Core 独有 - 绝对是不是。见EF Change Tracker (EF 6 Tutorials) 和MS Docs on DbContext.ChangeTracker property (EF 6.2)
    • 谢谢。一旦我修改了语法,该解决方案就起作用了:基于DBChangeTracker Class docs 声明“DbContext 的 ChangeTracker 方法提供对上下文功能的访问......”。我将ChangeTracker.Entries&lt;BaseEntity&gt;() 交换为databaseContext.ChangeTracker.Entries&lt;BaseEntity&gt;(),然后它就像一个魅力。我很感激。谢谢你。也许可以更新您的答案,我会接受它作为解决方案。
    • @InspiredBy: 因为这个方法是在DbContext 后代类上,你应该能够使用我提供的代码原样 - 我有,好几个月了。不太清楚为什么需要添加databaseContext.ChangeTracker .....
    • 接受,因为这在技术上是正确的解决方案。希望这些 cmets 对后代有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多