Entity Framework (EF6) insert or update if exists for properties

c# entity-framework entity-framework-6

Question

Assuming the model be like:

class Foo {
    virtual Bar Bar {get; set ;}
}

class Bar {
    int Id { get; set; }
    string Property {get; set;}
}

class MyContext {
     virtual DbSet<Foo> Foos {get; set;}

    void OnModelCreating(DbModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Bar>()
            .HasKey(c => c.Property);
    }
}

And some code like:

void DoStuff() {
   var foos = GetFoosFromExternalSource();

   using(var ctx = new MyContext() {
       foreach(var foo in foos) {
            ctx.Foos.Add(foo);
       }

       ctx.SaveChanges();
   }
}

IEnumerable<Foo> GetFoosFromExternalSource() {
     yield return new Foo {
          Bar = new Bar { Id = 1, Property = "Hello" }          
     };
     yield return new Foo {
          Bar = new Bar { Id = 2, Property = "World" }          
     };
     yield return new Foo {
          Bar = new Bar { Id = 1, Property = "Hello" }          
     }    
}

This throws an exception:

Violation of PRIMARY KEY constraint 'PK_dbo.Bar'. Cannot insert duplicate key in object 'dbo.Bar'. The duplicate key value is (Hello).

How can I make it clear to EF that if a Bar object has the same Key (or Id, or Both) that it is considered the same instance?

I know that if I could do something like

 IEnumerable<Foo> GetFoosFromExternalSource() {
     var bar1 = new Bar { Id = 1, Property = "Hello" };
     var bar2 = new Bar { Id = 2, Property = "World" };

     yield return new Foo {
          Bar = bar1          
     };
     yield return new Foo {
          Bar = bar2         
     };
     yield return new Foo {
          Bar = bar1
     }    
}

It would work fine. However, since this is data coming from an external source this is not directly possible. My real scenario has multiple levels and a lot of properties. So I would like to solve this in the model.

1
0
11/15/2018 4:52:09 PM

Popular Answer

Instead of adding the entities, you should use an "upsert" library (or create your own). For example FlexLabs.Upsert is such a library for Entity Framework Core.

In your case the code would then look like the following (based on docu):

async Task DoStuff()
{
   var foos = GetFoosFromExternalSource();
   using(var ctx = new MyContext()
   {
       await ctx.Foos
           .UpsertRange(foos)
           .On(f => f.Property)
           .RunAsync();
       ctx.SaveChanges(); // not sure if savechanges call is necessary based on docu...
   }
}

Notes: I have not used FlexLabs.Upsert before, a friend recommend it to me some time ago, have not yet had the time to experiment further with it.

1
11/15/2018 3:28:20 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow