【问题标题】:Insert instead of update for history data?插入而不是更新历史数据?
【发布时间】:2014-05-21 06:19:56
【问题描述】:

我正在使用 EF 6.1 Code First 并希望通过使用如下模型(简化)来跟踪历史数据:

public class Customer : IHistoryData<CustomerData>
{
    public Guid ID { get; set; }
    public virtual ISet<CustomerData> Versions { get; set; }
}

public class CustomerData : HistoricalData
{
    public string Name { get; set; }
}


public interface IHistoryData<T> where T : HistoricalData
{
    ISet<T> Versions { get; set; }
}

public class HistoricalData
{
    public int VersionID { get; set; }
    public Guid ParentID { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime? ValidUntil { get; set; }
}

它们以下列方式连接到 DbContext 中:

modelBuilder.Ignore<HistoricalData>();
modelBuilder.Entity<Customer>().ToTable("Customers").HasMany(c => c.Versions).WithRequired().HasForeignKey(cd => cd.ParentID);
modelBuilder.Entity<CustomerData>().Map(m => m.ToTable("CustomerData")).HasKey(cd => new { cd.VersionID, cd.ParentID });

现在要跟踪不同的客户版本,我需要对 CustomerData 的更改执行插入而不是更新。为此,我加入了SaveChangesAsync()DbContext

public override Task<int> SaveChangesAsync()
{
    foreach (var entry in ChangeTracker.Entries<HistoricalData>().Where(e => e.State == EntityState.Modified))
    {
        InsertInsteadOfUpdate(entry);
    }
    return base.SaveChangesAsync();
}

现在我不确定如何实现InsertInsteadOfUpdate(),这是我目前的做法:

private void InsertInsteadOfUpdate<T>(DbEntityEntry<T> oldEntry) where T : HistoricalData
{
    // Get a copy of the modified data 
    var newEntry = ???
    // Modified entry has ended
    var originalValues = oldEntry.OriginalValues;
    originalValues["ValidUntil"] = DateTime.Now;
    oldEntry.CurrentValues.SetValues(originalValues);
    // New entry has started
    newEntry.ValidFrom = DateTime.Now;
    Set<T>().Add(newEntry);
}

但是,我不知道如何获得DbEntityEntry&lt;CustomerData&gt; 的新副本/克隆。而且因为我都需要修改“旧”条目(设置ValidUntil)并插入一个新条目,所以我需要两个不同的条目。

我会怎么做呢?还是我完全走错了路?

【问题讨论】:

    标签: c# entity-framework ef-code-first change-tracking entity-framework-6.1


    【解决方案1】:

    试试这个:

    DbSet<T> dbSet = Context.Set<T>();
    T newEntry = dbSet.Create();
    dbSet.Attach(newEntry);
    newEntry.CurrentValues.SetValues(oldEntry);
    

    【讨论】:

    • 我考虑过这个,但我记得它不起作用,因为HistoricalData 是一个未映射到表的抽象基类,因此调用Set&lt;HistoricalData&gt; 不起作用。 (T 最终成为 HistoricalData,因为我查询 ChangeTracker 的方式)
    • 如果将DbSet&lt;HistoricalData&gt; 属性添加到上下文会发生什么?
    • 我不知道。但我无法想象 EF 如何能够Create() 来自一组抽象基类的实体,所以我认为这种方法行不通。我将不得不以某种方式获得一组子类。 (即以某种方式让T 成为子类而不是HistoryData
    • 我认为你应该试试我发布的代码。您正在将一个具体的类传递给您的泛型方法,因此将使用正确的 DbSet - 我有一个类似的方法可以工作。顺便说一句 - HistoricalData 在您的问题中没有标记为抽象。
    • 你说得对,问题不是抽象的,对不起。同时我把它改成了abstract,因为实例化基类没有意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2011-04-09
    • 2021-03-17
    相关资源
    最近更新 更多