Supponendo che il modello sia come:
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);
}
}
E un codice come:
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" }
}
}
Ciò genera un'eccezione:
Violazione del vincolo PRIMARY KEY "PK_dbo.Bar". Impossibile inserire la chiave duplicata nell'oggetto "dbo.Bar". Il valore della chiave duplicata è (Ciao).
Come posso rendere chiaro a EF se un oggetto Bar ha la stessa chiave (o Id o entrambi) che è considerata la stessa istanza?
So che se potessi fare qualcosa del genere
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
}
}
Funzionerebbe bene. Tuttavia, poiché si tratta di dati provenienti da una fonte esterna, ciò non è direttamente possibile. Il mio scenario reale ha più livelli e molte proprietà. Quindi mi piacerebbe risolvere questo nel modello.
Invece di aggiungere le entità, dovresti usare una libreria "upsert" (o crearne una tua). Ad esempio FlexLabs.Upsert è una libreria di Entity Framework Core.
Nel tuo caso il codice sarebbe quindi simile al seguente (basato su 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...
}
}
Note: Non ho usato FlexLabs.Upsert prima, un amico me lo consigliava qualche tempo fa, non ho ancora avuto il tempo di sperimentarlo ulteriormente.