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实体继承实体.
您可以在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)
我喜欢这样使用的更通用的方法:
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)
更新:
为了处理CreatedBy
,UpdatedBy
我使用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)
从 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)
您可以使用ChangeTracker
inBeforeSaveTriggers
方法来跟踪实体更改。
由于该拦截器使用依赖注入,因此需要将其注册为服务。
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)
归档时间: |
|
查看次数: |
10509 次 |
最近记录: |