Entity framework replaces delete+insert with an update. How to turn it off

100 Views Asked by At

I want to remove a row in database and insert it again with the same Id, It sounds ridiculous, but here is the scenario:

The domain classes are as follows:

public class SomeClass
{
    public int SomeClassId { get; set; }
    public string Name { get; set; }
    public virtual Behavior Behavior { get; set; }
}

public abstract class Behavior
{
    public int BehaviorId { get; set; }
}

public class BehaviorA : Behavior
{
    public string BehaviorASpecific { get; set; }
}

public class BehaviorB : Behavior
{
    public string BehaviorBSpecific { get; set; }
}

The entity context is

public class TestContext : DbContext
{
    public DbSet<SomeClass> SomeClasses { get; set; }
    public DbSet<Behavior> Behaviors { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        modelBuilder.Entity<SomeClass>()
            .HasOptional(s => s.Behavior)
            .WithRequired()
            .WillCascadeOnDelete(true);

    }
}

Now this code can be executed to demonstrate the point (described with comments in the code below)

    using(TestContext db = new TestContext())
    {
        var someClass = new SomeClass() { Name = "A" };
        someClass.Behavior = new BehaviorA() { BehaviorASpecific = "Behavior A" };
        db.SomeClasses.Add(someClass);

        // Here I have two classes with the state of added which make sense
        var modifiedEntities = db.ChangeTracker.Entries()
                                 .Where(entity => entity.State != System.Data.Entity.EntityState.Unchanged).ToList();
        // They save with no problem
        db.SaveChanges();

        // Now I want to change the behavior and it causes entity to try to remove the behavior and add it again
        someClass.Behavior = new BehaviorB() { BehaviorBSpecific = "Behavior B" };

        // Here it can be seen that we have a behavior A with the state of deleted and 
        // behavior B with the state of added
        modifiedEntities = db.ChangeTracker.Entries()
                 .Where(entity => entity.State != System.Data.Entity.EntityState.Unchanged).ToList();

        // But in reality when entity sends the query to the database it replaces the 
        // remove and insert with an update query (this can be seen in the SQL Profiler) 
        // which causes the discrimenator to remain the same where it should change.
        db.SaveChanges();
    } 

How to change this entity behavior so that delete and insert happens instead of the update?

1

There are 1 best solutions below

5
On

A possible solution is to make the changes in 2 different steps: before someClass.Behavior = new BehaviorB() { BehaviorBSpecific = "Behavior B" }; insert

someClass.Behaviour = null;
db.SaveChanges();

The behaviour is related to the database model. BehaviourA and B in EF are related to the same EntityRecordInfo and has the same EntitySet (Behaviors). You have the same behaviour also if you create 2 different DbSets on the context because the DB model remains the same.

EDIT
Another way to achieve a similar result of 1-1 relationship is using ComplexType. They works also with inheritance. Here an example

public class TestContext : DbContext
{
    public TestContext(DbConnection connection) : base(connection, true) { }

    public DbSet<Friend> Friends { get; set; }
    public DbSet<LessThanFriend> LessThanFriends { get; set; }
}

public class Friend
{
    public Friend()
    {Address = new FullAddress();}

    public int Id { get; set; }
    public string Name { get; set; }

    public FullAddress Address { get; set; }
}

public class LessThanFriend
{
    public LessThanFriend()
    {Address = new CityAddress();}

    public int Id { get; set; }
    public string Name { get; set; }

    public CityAddress Address { get; set; }
}


[ComplexType]
public class CityAddress
{
    public string Cap { get; set; }
    public string City { get; set; }
}

[ComplexType]
public class FullAddress : CityAddress
{
    public string Street { get; set; }
}