L’entity framework EF6 con DatabaseGeneratedOption.Identity Guid Id force inserisce il mio valore Id

Sto cercando di utilizzare EF per esportare / importare il database esistente di un DbContext. In questo contesto, esistono diverse entity framework con proprietà Guid Id con DatabaseGeneratedOption.Identity definito da ModelBuilder. Quando reimportazione le quadro, voglio utilizzare il valore Id dall’object serializzato, ma genera sempre un nuovo valore Id quando salvi le modifiche. Esiste un modo per forzare EF a utilizzare il mio valore Id in questo caso? Conosco DatabaseGeneratedOption.Non mi consentirà di farlo, ma poi sarò sempre responsabile della generazione dell’ID. Conosco i problemi di segmentazione dell’indice che si verificano senza utilizzare Guid sequenziali, quindi non voglio farlo.

Sono sfortunato o qualcuno ha trovato un trucco?

Aggiornamento: abbiamo deciso di cambiare semplicemente tutti gli Id di guida da DatabaseGeneratedOption.Identity a DatabaseGenerationOption.None e fornire l’Id da soli. Anche se questo porta alla frammentazione dell’indice, non ci aspettiamo che questo sia un problema con la dimensione più piccola delle nostre tabelle.

È ansible ottenere ciò che si desidera definendo due contesti che derivano da un contesto di base. Un contesto definisce le sue chiavi con DatabaseGeneratedOption.Identity , l’altro con DatabaseGeneratedOption.None . Il primo sarà il contesto dell’applicazione normale.

Ciò è ansible in virtù del fatto che le chiavi principali di Guid non sono colonne di id quadro reali. Sono solo colonne con un vincolo predefinito, quindi possono essere inserite senza un valore o con un valore senza dover impostare identity_insert .

Per dimostrare che funziona, ho usato una class molto semplice:

 public class Planet { public Guid ID { get; set; } public string Name { get; set; } } 

Il contesto di base:

 public abstract class BaseContext : DbContext { private readonly DatabaseGeneratedOption _databaseGeneratedOption; protected BaseContext(string conString, DatabaseGeneratedOption databaseGeneratedOption) : base(conString) { this._databaseGeneratedOption = databaseGeneratedOption; } public DbSet Planets { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity().HasKey(p => p.ID); modelBuilder.Entity().Property(p => p.ID) .HasDatabaseGeneratedOption(this._databaseGeneratedOption); base.OnModelCreating(modelBuilder); } } 

Le sottoclassi di contesto:

 public class GenerateKeyContext : BaseContext { public GenerateKeyContext(string conString) : base(conString, DatabaseGeneratedOption.Identity) { } } public class InsertKeyContext : BaseContext { public InsertKeyContext(string conString) : base(conString, DatabaseGeneratedOption.None) { } } 

Per prima cosa eseguo del codice per creare e seminare il database di origine:

 var db1 = @"Server=(localDB)\MSSQLLocalDB;Integrated Security=true;Database=GuidGen"; var db2 = @"Server=(localDB)\MSSQLLocalDB;Integrated Security=true;Database=GuidInsert"; // Set initializers: // 1. just for testing. Database.SetInitializer(new DropCreateDatabaseAlways()); // 2. prevent model check. Database.SetInitializer(null); using (var context = new GenerateKeyContext(db1)) { var earth = new Planet { Name = "Earth", }; var mars = new Planet { Name = "Mars", }; context.Planets.Add(earth); context.Planets.Add(mars); context.SaveChanges(); } 

E un database di destinazione:

 using (var context = new GenerateKeyContext(db2)) { context.Database.Initialize(true); } 

Finalmente questo è il codice che fa il vero lavoro:

 var planets = new List(); using (var context = new GenerateKeyContext(db1)) { planets = context.Planets.AsNoTracking().ToList(); } using (var context = new InsertKeyContext(db2)) { context.Planets.AddRange(planets); context.SaveChanges(); } 

Ora in entrambi i database vedrai due record con valori chiave identici.

Potresti chiederti: perché non posso usare una class di contesto e costruirla con o senza l’opzione Identity ? Questo perché EF costruisce il modello EDM solo una volta per un tipo di contesto e lo memorizza nell’AppDomain. Quindi l’opzione che usi per prima determinerebbe quale modello EF utilizzerà per la tua class di contesto.