0

Whenever an entity which inherits from HistoricalData is changed, I want to do an insert instead of an update.

This is the abstract base class with Key {VersionID, ParentID}:

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

This is my approach in the DbContext:

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

private void InsertInsteadOfUpdate(DbEntityEntry<HistoricalData> entry)
{
    // ValidFrom is not set manually -> Set it automatically to now
    if (entry.OriginalValues["ValidFrom"].Equals(entry.CurrentValues["ValidFrom"]))
    {
        entry.Entity.ValidFrom = DateTime.Now;
    }
    // Insert instead of Update
    entry.CurrentValues["VersionID"] = null;
    entry.State = EntityState.Added;
}

I change the VersionID, because it is an Identity Column and I want the database to create a new value for the insert.

However, I get the following Exception:

System.InvalidOperationException: The property 'VersionID' is part of the object's key information and cannot be modified.

If I don't try to modify VersionID, the Insert fails with a DuplicateKey Exception.

Is there any way to change a DbEntityEntry from Modified to Added with a new ID?

magnattic
  • 12,638
  • 13
  • 62
  • 115
  • This question is related to [my earlier question](http://stackoverflow.com/questions/22938978/insert-instead-of-update-for-history-data), but I am using a different approach which resulted in a different problem. – magnattic Apr 09 '14 at 12:51
  • Have you tried setting it to Detached first then change the id? – JC Ford Apr 09 '14 at 12:54
  • @JC. No, good idea. I will give that a try. – magnattic Apr 09 '14 at 12:54
  • Then I get the problem: `Member 'CurrentValues' cannot be called for the entity of type 'CustomerData' because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet.` Is there some other way to reset the VersionID? int is not nullable :/ – magnattic Apr 09 '14 at 12:58
  • When an entity is detached, it's just an object. So maybe `entity.VersionID = default(int)` – JC Ford Apr 09 '14 at 13:00
  • @JC. I am aware of that. But I have no idea how to "reset" the VersionID field so that it will be created by the database on insert. If I leave it as it is, I get the DuplicateKey problem again. – magnattic Apr 09 '14 at 13:01
  • If VersionID is not nullable then its default value is default(int) or 0. If EF sees an id column with zero then I think it will assume it's new. – JC Ford Apr 09 '14 at 13:03
  • Sorry, you are totally correct. My problem was in fact that I the column was not really an identity column. (Turns out you can't alter a column to identity via CF Migrations. It will silently fail.) EF now will ignore the `VersionID` completely, so all I have to do is set the `State` to `Added`. If you post your solution as an answer, I will gladly accept it. – magnattic Apr 09 '14 at 14:46
  • It also should be mentioned that adding Entities this way does not invalidate the Cache. So if I use `HistoricalData` as a collection/navigational property in another entity, I have to manually reload it. In my case: `entry.Collection(x => x.Versions).Load();` where `Versions` is a Collection of `HistoricalData` – magnattic Apr 09 '14 at 14:48
  • On detecting that the object has been modified, could you clone the object and Attach it (for an insert?). – Mike Apr 09 '14 at 15:08

0 Answers0