1

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?

mesmoll
  • 167
  • 2
  • 7

1 Answers1

0

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; }
}
bubi
  • 6,414
  • 3
  • 28
  • 45
  • Hi Bubi, thanks for your answer. But I just made this example to show my point, in reality it is not that simple, I have a unit of work which is tracking the changes and I can not save them before the user actually wants to do so. – mesmoll Aug 13 '15 at 09:28
  • About this model there is another issue. You can't have 1 Behavior attached to 2 SomeClass. You could evaluate to use Behavior as a ComplexType. – bubi Aug 13 '15 at 09:40
  • I remember once I tried ComplexType but then I could not use inheritance (my ComplexType is an abstract class actually), Do you know a way to use ComplexType together with inheritance? – mesmoll Aug 13 '15 at 12:14
  • You can just use them... I update the answer with an example – bubi Aug 13 '15 at 13:09
  • I have to thank you for your effort first. But here is the issue, In two different classes you use two different data type (FullAddress in Friend and CityAddress in LessThanFriend). I have a person with a property of type address (which is abstract, it can be cityAddress or fullAddress). This person is my LessThanFriend and I have saved its cityAddress but after a while she became my Friend and I want to update her address to a fullAddress. This brings me back to my initial problem this time with her!! :) – mesmoll Aug 13 '15 at 15:38