3

我正在尝试使用实体框架来完成一种“通用”机制来更新我的 SQL Server 数据库中的时间数据。

我所做的是创建一个名为“标记”的接口ITemporalData,它定义了两个需要存在的属性 -DateTime ValidFromDateTime? ValidTo.

public interface ITemporalData
{
    DateTime ValidFrom { get; set; }
    DateTime? ValidTo { get; set; }
}

我希望在我的DbContext.SaveChanges()覆盖中实现一种“通用”方法:

  • 克隆任何ITemporalData对象,这会给我一个新对象来存储(EntityState.Added),并将其ValidFrom值设置为当前日期和时间
  • 将原始的、修改后的条目重置为其数据库值(调用.Reset()实体),然后ValidTo将该“旧”记录的设置为当前日期和时间

虽然我可以像这样轻松地过滤掉覆盖ITemporalData中的修改对象:SaveChanges()

public partial class MyDbContext
{
    // override the "SaveChanges" method
    public override int SaveChanges()
    {
        DateTime currentDateTime = DateTime.Now;

        // get the modified entities that implement the ITemporalData interface
        IEnumerable<DbEntityEntry<ITemporalData>> temporalEntities = ChangeTracker.Entries<ITemporalData>().Where(e => e.State == EntityState.Modified);

        foreach (var temporalEntity in temporalEntities)
        {
            // how would I do that, really? I only have an interface - can't clone an interface...... 
            var cloned = temporalEntity.Entity.Clone();

            // and once it's cloned, I would need to add the new record to the correct DbSet<T> to store it

            // set the "old" records "ValidTo" property to the current date&time
            temporalEntity.Entity.ValidTo = currentDateTime;
        }

        return base.SaveChanges();
    }
}

我正在为“克隆修改后的记录”方法而苦苦挣扎——我只有一个ITemporalData接口,真的——但是克隆(使用 AutoMapper 或其他方法)总是取决于实际的、底层的具体数据类型.....

4

2 回答 2

2

要克隆实体,您可能只需通过反射 ( Activator.CreateInstance) 创建新实例并通过反射将所有原始(非导航)属性复制到它。最好不要为此使用自动映射器工具,因为它们也会访问导航属性,这可能会导致延迟加载(或至少确保延迟加载被禁用)。

如果您不喜欢反射(请注意,自动映射器无论如何都会使用它) - 您还可以从每个实体继承您的接口ICloneable并实现Clone方法ITemporalData(如果您的实体是自动生成的 - 为此使用部分类)。然后每个实体自行决定如何克隆,无需任何反思。如果您的克隆逻辑很复杂(例如涉及从导航属性克隆相关对象),这种方式也有好处。

要将实体添加到更正 DbSet,请使用无类型的Set方法DbContext

this.Set(temporalEntity.GetType()).Add(temporalEntity);
于 2016-05-31T20:06:10.470 回答
1

您可以将此Clone方法添加到您的上下文中:

T Clone<T>(DbEntityEntry<T> entry)
    where T : class
{
    var proxyCreationEnabled = this.Configuration.ProxyCreationEnabled;
    try
    {
        this.Configuration.ProxyCreationEnabled = false;
        var clone = (T)entry.CurrentValues.ToObject();
        Set(clone.GetType()).Add(clone);
        return clone;
    }
    finally
    {
        this.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

并按如下方式使用它:

var cloned = Clone(temporalEntity);

clone.GetType将返回克隆对象的实际类型,而T将是编译时类型,ITemporalData.

这使用 EF 自己的基础架构来创建克隆,这无疑比反射更快。

尽管克隆的状态立即设置为Added,但它不会执行延迟加载。但是确保克隆永远不会成为代理可能更安全,因此,如果您决定对克隆执行其他操作,则永远不会触发延迟加载。(感谢 Evk 的敏锐评论)。

于 2016-05-31T21:29:36.747 回答