如何设置创建日期和修改日期以在DB第一种方法中进行设置

LP1*_*P13 15 entity-framework ef-database-first

在我们的应用程序的每个SQL表中,我们都有"CreatedDate"和"ModifiedDate"列.我们正在使用DB第一种方法.当我保存数据时,我希望自动填充这两列.一种方法是getdate()在SQL表列本身上使用默认值.所以这将部分地解决问题.这意味着当实体是新的时,它将设置CreatedDate和ModifiedDate.但是,当我编辑/更新实体时,我只想ModifiedDate更新.
使用Code第一种方法有很多文章在做这件事.但我们正在使用DB第一种方法.
我有什么选择?

kjp*_*ter 11

如果您想覆盖OnSave,则必须覆盖所有保存方法.在EF核心2.1中,您可以使用ChangeTracked和事件获得更好的解决方案.例如:

您可以创建接口或基类,如下例所示:

public interface IUpdateable 
{ 
    DateTime ModificationDate{ get; set; }

}

public class SampleEntry : IUpdateable
{
    public int Id { get; set; }
    public DateTime ModificationDate { get; set; }

} 
Run Code Online (Sandbox Code Playgroud)

然后在上下文创建中向Change tracker添加事件:

context.ChangeTracker.StateChanged += context.ChangeTracker_StateChanged;
Run Code Online (Sandbox Code Playgroud)

方法:

private void ChangeTracker_StateChanged(object sender, Microsoft.EntityFrameworkCore.ChangeTracking.EntityStateChangedEventArgs e)
    {
        if(e.Entry.Entity is IUpdateable && e.Entry.State == EntityState.Modified)
        {
            var entry = ((IUpdateable)e.Entry.Entity);
            entry.ModyficationDate = DateTime.Now;

        }
    }
Run Code Online (Sandbox Code Playgroud)

它更容易,您不必覆盖所有方法;


Ara*_*ash 10

对于那些使用异步系统(SaveChangesAsync)和.net核心的人来说,最好将下面的代码添加到DbContext类中.

    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
    {
        var AddedEntities = ChangeTracker.Entries().Where(E => E.State == EntityState.Added).ToList();

        AddedEntities.ForEach(E =>
        {
            E.Property("CreationTime").CurrentValue = DateTime.Now;
        });

        var EditedEntities = ChangeTracker.Entries().Where(E => E.State == EntityState.Modified).ToList();

        EditedEntities.ForEach(E =>
        {
            E.Property("ModifiedDate").CurrentValue = DateTime.Now;
        });

        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }
Run Code Online (Sandbox Code Playgroud)

你也可以定义一个类或接口,如下所示.

public class SaveConfig
{
    public DateTime CreationTime { get; set; }
    public DateTime? ModifiedDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

只是从SaveConfig实体继承实体.

  • 很好,谢谢!另外,如果您_do_继承自`SaveConfig`,则可以使用`ChangeTracker.Entries&lt;SaveConfig&gt;()...`,然后将`E.Property("CreationTime").CurrentValue`替换为`E.Entity.CreationTime `,避免魔术字符串:) (2认同)

Kah*_*azi 8

您可以在DbContext中覆盖SaveChanges方法并获取已添加和修改实体的列表,如果添加了Set CreatedDate,则修改后设置为ModifiedDate.

public override int SaveChanges()
{
    var AddedEntities = ChangeTracker.Entries<Entity>().Where(E => E.State == EntityState.Added).ToList();

    AddedEntities.ForEach(E => 
    {
        E.CreatedDate = DateTime.Now;
    });

    var ModifiedEntities = ChangeTracker.Entries<Entity>().Where(E => E.State == EntityState.Modified).ToList();

    ModifiedEntities.ForEach(E => 
    {
        E.ModifiedDate = DateTime.Now;
    });

    return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

您还可以编写具有两个DateTime属性的接口,并使您的实体继承它们.

interface IEntityDate
{
    DateTime AddedDate { set; get;}
    DateTime ModifiedDate { set; get;}
}

class Entity : IEntityDate
{
    public DateTime AddedDate { set; get;}
    public DateTime ModifiedDate { set; get;}
}
Run Code Online (Sandbox Code Playgroud)


Ogg*_*las 8

我喜欢这样使用的更通用的方法:

interface IEntityDate
{
    DateTime CreatedDate { get; set; }

    DateTime UpdatedDate { get; set; }
}

public abstract class EntityBase<T1>: IEntityDate
{
    public T1 Id { get; set; }

    public virtual DateTime CreatedDate { get; set; }
    public virtual string CreatedBy { get; set; }
    public virtual DateTime UpdatedDate { get; set; }
    public virtual string UpdatedBy { get; set; }
}

public override int SaveChanges()
{
    var now = DateTime.Now;

    foreach (var changedEntity in ChangeTracker.Entries())
    {
        if (changedEntity.Entity is IEntityDate entity)
        {
            switch (changedEntity.State)
            {
                case EntityState.Added:
                    entity.CreatedDate = now;
                    entity.UpdatedDate = now;
                    break;

                case EntityState.Modified:
                    Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                    entity.UpdatedDate = now;
                    break;
            }
        }
    }

    return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

更新:

为了处理CreatedByUpdatedBy我使用DbContext的包装器,如下所示:

public interface IEntity
{
    DateTime CreatedDate { get; set; }

    string CreatedBy { get; set; }

    DateTime UpdatedDate { get; set; }

    string UpdatedBy { get; set; }
}

public interface ICurrentUser
{
    string GetUsername();
}

public class ApplicationDbContextUserWrapper
{
    public ApplicationDbContext Context;

    public ApplicationDbContextUserWrapper(ApplicationDbContext context, ICurrentUser currentUser)
    {
        context.CurrentUser = currentUser;
        this.Context = context;
    }
}

public class ApplicationDbContext : DbContext
{

    public ICurrentUser CurrentUser;

    public override int SaveChanges()
    {
        var now = DateTime.Now;

        foreach (var changedEntity in ChangeTracker.Entries())
        {
            if (changedEntity.Entity is IEntity entity)
            {
                switch (changedEntity.State)
                {
                    case EntityState.Added:
                        entity.CreatedDate = now;
                        entity.UpdatedDate = now;
                        entity.CreatedBy = CurrentUser.GetUsername();
                        entity.UpdatedBy = CurrentUser.GetUsername();
                        break;
                    case EntityState.Modified:
                        Entry(entity).Property(x => x.CreatedBy).IsModified = false;
                        Entry(entity).Property(x => x.CreatedDate).IsModified = false;
                        entity.UpdatedDate = now;
                        entity.UpdatedBy = CurrentUser.GetUsername();
                        break;
                }
            }
        }

        return base.SaveChanges();
    }

    ...
Run Code Online (Sandbox Code Playgroud)


Ara*_*ani 6

从 EF-Core 5x 开始,您可以使用拦截器和外部方法,DbContext而不是重写该SaveChanges方法。

它的一般格式如下:

public class AuditableEntitiesInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData,
        InterceptionResult<int> result)
    {
        if (eventData == null)
        {
            throw new ArgumentNullException(nameof(eventData));
        }

        BeforeSaveTriggers(eventData.Context);
        return result;
    }

     public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData,
        InterceptionResult<int> result,
        CancellationToken cancellationToken = default)
    {
        if (eventData == null)
        {
            throw new ArgumentNullException(nameof(eventData));
        }

        BeforeSaveTriggers(eventData.Context);
        return ValueTask.FromResult(result);
    }

    private void BeforeSaveTriggers(DbContext? context)
    {
        var entries = context?.ChangeTracker
                .Entries()
                .Where(e => e.Entity is BaseEntity && (
                     e.State == EntityState.Added
                     || e.State == EntityState.Modified));

        foreach (var entityEntry in entries)
        {
            ((BaseEntity)entityEntry.Entity).UpdatedDate = DateTimeOffset.UtcNow;

        if (entityEntry.State == EntityState.Added)
        {
            ((BaseEntity)entityEntry.Entity).CreatedDate = DateTimeOffset.UtcNow;
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用ChangeTrackerinBeforeSaveTriggers方法来跟踪实体更改。

由于该拦截器使用依赖注入,因此需要将其注册为服务。

services.AddSingleton<AuditableEntitiesInterceptor>();
Run Code Online (Sandbox Code Playgroud)

然后可以在程序启动时从依赖注入中取出并引入到上下文中。

services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
                optionsBuilder
                    .UseSqlServer(connectionString)
                    .AddInterceptors(serviceProvider.GetRequiredService<AuditableEntitiesInterceptor>()));
Run Code Online (Sandbox Code Playgroud)

值得注意的是,该示例BaseEntity使用DateTimeOffset而不是DateTime为了更好的本地化,您可以根据需要调整您的实现。

 public class BaseEntity{ 
    public DateTimeOffset CreatedDate { get; set; }
    public DateTimeOffset UpdatedDate { get; set; }    
}
Run Code Online (Sandbox Code Playgroud)