41

如果我有以下实体:

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

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

我怎样才能自动

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

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

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

不是这个:

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

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

我正在使用 Entity Framework 4.0 - 有没有一种方法可以让 EF 自动为我做到这一点?(可能是 EDMX 中的特殊设置?)

从 SQL Server 端,我可以使用DefaultValue,但这仅适用于INSERT(而不是 UPDATE)。

同样,我可以使用 POCO 上的构造函数设置默认值,但这仅在实例化对象时才有效。

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

因为我使用的是实体框架,所以我可以连接到SavingChanges事件并在此处更新日期字段,但问题是我需要“了解”POCO(目前,我的存储库是用泛型实现的)。我需要做一些面向对象的技巧(比如让我的 POCO 实现一个接口,并在上面调用一个方法)。我对此并不反对,但如果我必须这样做,我宁愿手动设置字段。

我基本上是在寻找 SQL Server 2008 或 Entity Framework 4.0 解决方案。(或智能 .NET 方式)

有任何想法吗?

编辑

感谢@marc_s 的回答,但我选择了一个更适合我的场景的解决方案。

4

7 回答 7

53

我知道我参加聚会有点晚了,但我刚刚为我正在从事的一个项目解决了这个问题,并认为我会分享我的解决方案。

首先,为了使解决方案更具可重用性,我创建了一个具有时间戳属性的基类:

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();
    }
}
于 2011-06-08T17:01:32.423 回答
9

由于我的控制器(我使用 ASP.NET MVC)和我的存储库之间有一个服务层调解,我决定在这里自动设置字段。

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

所以我创建了一个接口,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.
}

稍后我可能会在 Add out to a helper/extension 方法中破坏该代码。

但我认为这对于我的场景来说是一个不错的解决方案,因为我不想在 Save 上使用 virtuals(因为我正在使用 Unit of Work、Repository 和 Pure POCO),也不想使用触发器。

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

于 2010-10-12T01:03:36.080 回答
6

我还想提出一个较晚的解决方案。这个仅适用于 .NET Framework 4,但使此类任务变得微不足道。

var vs = oceContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var v in vs)
{
    dynamic dv = v.Entity;
    dv.DateLastEdit = DateTime.Now;
}
于 2011-07-25T09:50:50.763 回答
6

这是先前回复的编辑版本。上一个对我来说不适用于更新。

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();
    }
于 2015-04-01T23:02:18.943 回答
1

你有两个选择:

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

    poco.LastModified = DateTime.Now;
    

    .Save()在所有其他人都必须调用的虚拟方法中

  • 在数据库中使用触发器

我认为没有其他合理安全且简单的方法可以实现这一目标。

于 2010-10-07T08:34:56.017 回答
1

必须添加基础实体从我的数据库第一个部分类扩展它(这并不理想)

    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;
        }
    }
于 2017-05-26T13:29:26.947 回答
0

我们可以使用部分类并重写 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();
        }
    }
}
于 2014-09-18T08:39:57.477 回答