Entity Framework / SQL2008 – 如何自动更新实体的LastModified字段?

如果我有以下实体:

public class PocoWithDates { public string PocoName { get; set; } public DateTime CreatedOn { get; set; } public DateTime LastModified { get; set; } } 

其中对应于具有相同名称/属性的SQL Server 2008表…

我如何自动

  1. 将logging的CreatedOn / LastModified字段设置为现在 (执行INSERT时)
  2. 将logging的LastModified字段设置为现在 (执行UPDATE时)

当我自动说,我的意思是我想能够做到这一点:

 poco.Name = "Changing the name"; repository.Save(); 

不是这个:

 poco.Name = "Changing the name"; poco.LastModified = DateTime.Now; repository.Save(); 

在幕后,“东西”应该自动更新date时间字段。 那是什么“东西”?

我正在使用entity framework4.0 – 有没有一种方法,英孚可以为我自动做到这一点? (在EDMX的特殊设置可能?)

从SQL Server端来说,我可以使用DefaultValue ,但是这只对INSERT (而不是UPDATE)有效。

同样,我可以使用POCO的构造函数设置一个默认值,但是这只会在实例化对象时才起作用。

当然,我可以使用触发器,但这并不理想。

因为我使用entity framework,我可以钩入SavingChanges事件并更新这里的date字段,但问题是我需要变得“意识到”的POCO(目前,我的存储库是generics实现)。 我需要做一些面向对象的诡计(比如让我的POCO实现一个接口,然后调用一个方法)。 我没有被逆转,但如果我必须这样做,我宁愿手动设置字段。

我基本上正在寻找一个SQL Server 2008或entity framework4.0解决scheme。 (或者一个聪明的.NET方式)

有任何想法吗?

编辑

感谢@marc_s的回答,但是我用了一个更适合我的场景的解决scheme。

我知道我晚了一点,但我刚刚解决了这个项目,我正在努力,并认为我会分享我的解决scheme。

首先,为了使解决scheme更加可重用,我创build了一个具有timestamp属性的基类:

 public class EntityBase { public DateTime? CreatedDate { get; set; } public DateTime? LastModifiedDate { get; set; } } 

然后我覆盖了我的DbContext的SaveChanges方法:

 public class MyContext : DbContext { public override int SaveChanges() { ObjectContext context = ((IObjectContextAdapter)this).ObjectContext; //Find all Entities that are Added/Modified that inherit from my EntityBase IEnumerable<ObjectStateEntry> objectStateEntries = from e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified) where e.IsRelationship == false && e.Entity != null && typeof(EntityBase).IsAssignableFrom(e.Entity.GetType()) select e; var currentTime = DateTime.Now; foreach (var entry in objectStateEntries) { var entityBase = entry.Entity as EntityBase; if (entry.State == EntityState.Added) { entityBase.CreatedDate = currentTime; } entityBase.LastModifiedDate = currentTime; } return base.SaveChanges(); } } 

因为我有一个介于我的控制器(即时通讯使用ASP.NET MVC)和我的存储库之间的服务层,我决定在这里自动设置字段。

另外,我的POCO没有任何关系/抽象,它们是完全独立的。 我想保持这种方式,而不是标记任何虚拟属性,或创build基类。

所以我创build了一个接口,IAutoGenerateDateFields:

 public interface IAutoGenerateDateFields { DateTime LastModified { get;set; } DateTime CreatedOn { get;set; } } 

对于任何POCO的我希望自动生成这些领域,我实现这个接口。

在我的问题中使用示例:

 public class PocoWithDates : IAutoGenerateDateFields { public string PocoName { get; set; } public DateTime CreatedOn { get; set; } public DateTime LastModified { get; set; } } 

在我的服务层,我现在检查具体对象是否实现了接口:

 public void Add(SomePoco poco) { var autoDateFieldsPoco = poco as IAutoGenerateDateFields; // returns null if it's not. if (autoDateFieldsPoco != null) // if it implements interface { autoDateFieldsPoco.LastModified = DateTime.Now; autoDateFieldsPoco.CreatedOn = DateTime.Now; } // ..go on about other persistence work. } 

我稍后可能会在添加到助手/扩展方法中分解该代码。

但我认为这是一个体面的解决scheme为我的scheme,因为我不想使用虚拟的保存(因为我使用工作单元,存储库和纯POCO的),并不想使用触发器。

如果您有任何想法/build议,请告诉我。

我也想提出一个晚点的解决办法。 这个只适用于.NET Framework 4,但是使这种任务变得微不足道。

 var vs = oceContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified); foreach (var v in vs) { dynamic dv = v.Entity; dv.DateLastEdit = DateTime.Now; } 

这里是以前回复的编辑版本。 以前的一个没有为我的更新工作。

 public override int SaveChanges() { var objectStateEntries = ChangeTracker.Entries() .Where(e => e.Entity is TrackedEntityBase && (e.State == EntityState.Modified || e.State == EntityState.Added)).ToList(); var currentTime = DateTime.UtcNow; foreach (var entry in objectStateEntries) { var entityBase = entry.Entity as TrackedEntityBase; if (entityBase == null) continue; if (entry.State == EntityState.Added) { entityBase.CreatedDate = currentTime; } entityBase.LastModifiedDate = currentTime; } return base.SaveChanges(); } 

你有两个select:

  • 有一个基类为您的所有业务实体类

     poco.LastModified = DateTime.Now; 

    在一个虚拟的.Save()方法,所有其他人将不得不打电话

  • 在数据库中使用触发器

我不认为有任何其他合理安全和简单的方法来实现这一点。

我们可以使用分类并重写SaveChanges方法来实现这一点。

 using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; namespace TestDatamodel { public partial class DiagnosisPrescriptionManagementEntities { public override int SaveChanges() { ObjectContext context = ((IObjectContextAdapter)this).ObjectContext; foreach (ObjectStateEntry entry in (context.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Modified))) { if (!entry.IsRelationship) { CurrentValueRecord entryValues = entry.CurrentValues; if (entryValues.GetOrdinal("ModifiedBy") > 0) { HttpContext currentContext = HttpContext.Current; string userId = "nazrul"; DateTime now = DateTime.Now; if (currContext.User.Identity.IsAuthenticated) { if (currentContext .Session["userId"] != null) { userId = (string)currentContext .Session["userId"]; } else { userId = UserAuthentication.GetUserId(currentContext .User.Identity.UserCode); } } if (entry.State == EntityState.Modified) { entryValues.SetString(entryValues.GetOrdinal("ModifiedBy"), userId); entryValues.SetDateTime(entryValues.GetOrdinal("ModifiedDate"), now); } if (entry.State == EntityState.Added) { entryValues.SetString(entryValues.GetOrdinal("CreatedBy"), userId); entryValues.SetDateTime(entryValues.GetOrdinal("CreatedDate"), now); } } } } return base.SaveChanges(); } } } 

不得不添加基础实体从我的数据库第一部分类(这是不理想的)

  public override int SaveChanges() { AddTimestamps(); return base.SaveChanges(); } public override async Task<int> SaveChangesAsync() { AddTimestamps(); return await base.SaveChangesAsync(); } private void AddTimestamps() { //var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified)); //ObjectiveContext context = ((IObjectContextAdapter)this).ObjectContext; var entities = ChangeTracker.Entries().Where(e => e.Entity is BaseEntity && (e.State == EntityState.Modified || e.State == EntityState.Added)).ToList(); var currentUsername = !string.IsNullOrEmpty(System.Web.HttpContext.Current?.User?.Identity?.Name) ? HttpContext.Current.User.Identity.Name : "Anonymous"; foreach (var entity in entities) { if (entity.State == EntityState.Added) { ((BaseEntity)entity.Entity).CREATEDON = DateTime.UtcNow; ((BaseEntity)entity.Entity).CREATEDBY = currentUsername; } ((BaseEntity)entity.Entity).MODIFIEDON = DateTime.UtcNow; ((BaseEntity)entity.Entity).MODIFIEDBY = currentUsername; } }