21

Sto tentando di utilizzare le migrazioni EF 4.3 con più DbContexts code-first. La mia applicazione è divisa in diversi plugin, che possibilmente hanno il loro DbContext per quanto riguarda il loro dominio. L'applicazione dovrebbe utilizzare un singolo database sql.EF 4.3 Migrazioni automatiche con più DbContexts in un database

Quando si tenta di eseguire la migrazione automatica dei contesti in un database vuoto, questo ha esito positivo solo per il primo contesto. Ogni altro contesto richiede che la proprietà AutomaticMigrationDataLossAllowed sia impostata su true, ma poi tenta di eliminare le tabelle della precedente.

Quindi la mia domanda è:

  • Come posso dire la migrazione configurazione solo a guardare dopo le tabelle definite nel loro contesto corrispondente e lascia in pace tutti gli altri?
  • Qual è il flusso di lavoro corretto per gestire più DbContexts con la migrazione automatica in un singolo database?

Grazie!

+0

Questa è una domanda molto interessante. Mi chiedo se il supporto contestuale multiplo fosse parte dei casi di utilizzo della migrazione. –

+2

Dubito fortemente che più contesti possano funzionare con le migrazioni automatiche, è progettato per aggiornare il db in modo che assomigli al contesto, indipendentemente da cosa. Potresti avere più fortuna nello sviluppo dei plugin usando le migrazioni manuali, contro database separati per generare le migrazioni, quindi applicarli tutti allo stesso db. – Betty

+0

Nel frattempo ho sbirciato negli assiemi EF 4.3 e dubito anche che il framework di migrazione possa far fronte a diversi contesti. Ma non c'è una ragione tecnica a cui possa pensare. Con un modello EDM sul posto si potrebbe diff contro il database trovare le tabelle delle tabelle esistenti creare o modificare e lasciare lo scenario di cancellazione da migrazioni manuali all'utente. –

risposta

6

Code First Migrations presuppone che sia presente una sola configurazione di migrazione per database (e un contesto per configurazione).

Mi vengono in mente due possibili soluzioni:

  1. Creare un contesto complessivo che include tutte le entità di ogni contesto di riferimento e questo "super" contesto dalla classe di configurazione migrazioni. In questo modo tutte le tabelle verranno create nel database dell'utente, ma i dati saranno solo in quelli per cui sono stati installati i plugin.

  2. Utilizzare database separati per ogni contesto. Se sono presenti entità condivise tra i contesti, aggiungere una migrazione personalizzata e sostituire la chiamata CreateTable(...) con una chiamata Sql("CREATE VIEW ...") per ottenere i dati dal database "di origine" dell'entità.

Proverei # 1 perché conserva tutto in un unico database. È possibile creare un progetto separato nella soluzione per contenere le migrazioni e questo contesto "super". Basta aggiungere il progetto, fare riferimento a tutti i progetti dei plug-in, creare un contesto che includa tutte le entità, quindi chiamare Enable-Migrations su questo nuovo progetto. Le cose dovrebbero funzionare come previsto dopo.

+5

Accetto questa risposta, indipendentemente dal fatto che non sia davvero la risposta alla mia domanda. Penso che progettare le migrazioni in un solo contesto per database sia un errore. Da un lato ogni database legacy con tabelle non utilizzate nel contesto crea problemi. D'altra parte (anche con viste precompilate) il dominio dell'app con un contesto con circa 100 entità diverse impiega anni per iniziare. Quindi dividerlo in contesti più piccoli è l'unica soluzione finora. –

+3

La mia risposta originale è diventata un po 'datata. EF 6.0 ha rimosso la limitazione di un codice Primo contesto per database. Le cose dovrebbero "funzionare" solo nelle versioni 6.0 e successive. – bricelam

3

Ho un sito di lavoro con più contesti utilizzando le migrazioni. Tuttavia, è necessario utilizzare un database separato per contesto ed è tutto guidato da una classe * Configuration nello spazio dei nomi Migrations del progetto, quindi, ad esempio, CompanyDbContext punta a Company.sdf utilizzando CompanyConfiguration. update-database -configurationtypename CompanyConfiguration. Un altro LogDbContext punta a Log.sdf usando LogConfiguration, ecc.

Dato che funziona, si è tentato di creare 2 contesti che puntano allo stesso database e si dice al modelbuilder di ignorare l'elenco di tabelle dell'altro contesto?

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Ignore<OtherContextsClass>(); 
    // more of these 
} 

Dal momento che le migrazioni lavorano con il ModelBuilder, questo potrebbe fare il lavoro.

La merda alternativa è quella di evitare l'uso di migrazioni automatiche, generare una migrazione ogni volta e poi manualmente vagliare e rimuovere le dichiarazioni indesiderate, poi eseguirli, anche se non c'è nulla ti impedisce di creare un semplice strumento che analizza i contesti e generati dichiarazioni e fa i fixup di migrazione per voi.

+1

Grazie per la risposta. Ma sfortunatamente non posso dividere il database. Ma mi hai dato un buon suggerimento per usare l'output degli automatismi come base per quelli manuali. –

+0

qualche idea se avrebbe funzionato con più schemi (ad esempio dbo)? – Betty

32

Ecco cosa puoi fare. molto semplice.

È possibile creare una classe di configrazione per ciascun contesto. , ad esempio

internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{ 
    public Configuration1(){ 
     AutomaticMigrationsEnabled = false; 
     MigrationsNamespace = "YourProject.Models.ContextNamespace1"; 
    } 
} 

internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{ 
    public Configuration2(){ 
     AutomaticMigrationsEnabled = false; 
     MigrationsNamespace = "YourProject.Models.ContextNamespace2"; 
    } 
} 

Ora aggiungi la migrazione. Non è necessario abilitare la migrazione poiché lo hai già fatto con i 2 di cui sopra.

Add-Migration -configuration Configuration1 Context1Init 

Questo creerà lo script di migrazione per context1. lo puoi ripetere ancora per altri contesti.

Add-Migration -configuration Configuration2 Context2Init 

per aggiornare il database

Update-Database -configuration Configuration1 
Update-Database -configuration Configuration2 

Questo può essere fatto in qualsiasi ordine. Tranne che devi assicurarti che ogni configrazione venga chiamata in sequenza.

+0

Perfetto, ha detto "Per favore specifica quello da usare" e non sapevo come farlo. Grazie! – joshcomley

+1

Ricevo "il modello che supporta il contesto è cambiato" quando provo a usare Context1 –

+2

mio dio questo dovrebbe essere contrassegnato come la risposta !!! grazie mille! Nel caso in cui ho avuto modo di lavorare con questa soluzione su MVC 5 e EF 6 – oskar132

0

Ho funzionato con le migrazioni manuali, ma non è possibile eseguire il downgrade in quanto non può discrimitare tra le configurazioni nella tabella __MigrationHistory. Se provo a eseguire il downgrade, tratta le migrazioni dalle altre configurazioni come automatiche e poiché non permetto la perdita dei dati fallisce. Lo useremo solo per l'aggiornamento, quindi funziona per i nostri scopi.

Anche se sembra un po 'troppo semplice, sono sicuro che non sarebbe difficile supportarlo a patto che non ci fosse una sovrapposizione tra DbContexts.

1

Ok, ho lottato con questo per un giorno all'altro, e qui è la soluzione per chi cerca la risposta ...

Io parto dal presupposto che la maggior parte persone che leggono questo post sono qui perché hanno un grande DbContext classe con molte proprietà di DbSet <> e richiede molto tempo per essere caricato. Probabilmente hai pensato a te stesso, ciao, che avrebbe senso, dovrei suddividere il contesto, dal momento che non userò tutti i dbset contemporaneamente, e caricherò solo un contesto "Parziale" basato sulla situazione in cui ho bisogno esso. Quindi li dividi, solo per scoprire che le migrazioni di Code First non supportano il tuo modo di pensare rivoluzionario.

Quindi il primo passaggio deve essere stato la suddivisione dei contesti, quindi è stata aggiunta la classe MigrationConfiguration per ciascuno dei nuovi contesti, sono state aggiunte le stringhe di connessione denominate esattamente come le nuove classi Context.

Quindi è provato in esecuzione i contesti di recente divisi uno per uno, facendo Add-migrazione context1 poi facendo Update-Database -Verbose ...

Tutto sembrava funzionare bene, ma poi ci si accorge che ogni successiva La migrazione ha eliminato tutte le tabelle dalla migrazione precedente e ha lasciato le tabelle solo dall'ultima migrazione.

Questo perché, il modello di migrazione corrente prevede Single DbContext per Database e deve essere una corrispondenza speculare.

Ciò che ho provato anche, e qualcuno ha suggerito di farlo, è creare un singolo SuperContext, che contiene tutti i set di Db. Crea una singola classe Migration Configuration ed eseguila. Lascia le tue classi Context parziali in posizione, e prova ad Istanziare e usarle. L'EF si lamenta che il modello di supporto è cambiato. Di nuovo, questo è perché l'EF confronta il tuo dbcontext parziale con la firma di contesto Tutto-Set che era rimasta dalla tua migrazione Super Contesto.

Questo è un grosso difetto a mio parere.

Nel mio caso, ho deciso che PERFORMANCE è più importante delle migrazioni. Quindi, quello che ho finito per fare, è dopo che ho corso nel contesto Super e ho avuto tutti i tavoli in atto, sono entrato nel database e ho eliminato manualmente la tabella _MigrationHistory.

Ora, posso creare un'istanza e utilizzare i miei contesti parziali senza che EF si lamenti. Non trova la tabella MigrationHistory e semplicemente si sposta, permettendomi di avere una vista "Parziale" del database.

Il compromesso ovviamente è che qualsiasi modifica al modello dovrà essere propagata manualmente al database, quindi fai attenzione.

Ha funzionato per me però.

0

Sicuramente la soluzione dovrebbe essere una modifica da parte del team EntityFramework per modificare l'API per supportare la modifica diretta della tabella _MigrationHistory su un nome tabella di vostra scelta come _MigrationHistory_Context1 in modo che possa gestire la modifica di entità DbContext indipendenti. In questo modo vengono tutti trattati separatamente e spettano allo sviluppatore assicurare che i nomi delle entità non si scontrino.

Sembra che ci siano molte persone che condividono la mia opinione sul fatto che un duplicato di DbContext con riferimenti al superset delle entità sia un modo falso non adatto alle imprese per affrontare le cose. DbContexts duplicati falliscono miseramente per soluzioni modulari (Prism o simili).

1

Come accennato in precedenza da Brice, la soluzione più pratica è disporre di 1 super DbContext per applicazione/database.

Dovendo utilizzare solo 1 DbContext per un'intera applicazione sembra essere uno svantaggio tecnico e metodologico cruciale, in quanto influenza la modularità tra le altre cose. Inoltre, se si utilizzano i servizi dati WCF, è possibile utilizzare solo 1 DataService per applicazione poiché un DataService può eseguire il mapping solo su 1 DbContext. Quindi questo altera considerevolmente l'architettura.

Sul lato positivo, un vantaggio minore è che tutto il codice di migrazione relativo al database è centralizzato.

1

Mi sono appena imbattuto in questo problema e ho capito che la ragione per cui li avevo divisi in contesti diversi era puramente avere raggruppamenti di modelli correlati in blocchi gestibili e non per nessun altro motivo tecnico. Invece ho dichiarato il mio contesto come una classe parziale e ora file di codice diversi con modelli diversi in essi possono aggiungere DbSets al DbContext.

In questo modo la magia di automazione continua a funzionare.

0

Voglio che la gente sappia che la risposta con questo sotto è ciò che ha funzionato per me, ma con un avvertimento: non utilizzare la riga MigrationsNamespace.

internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{ 
     public Configuration1(){ 
     AutomaticMigrationsEnabled = false; 
     MigrationsNamespace = "YourProject.Models.ContextNamespace1"; 
    } 
} 

internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{ 
    public Configuration2(){ 
     AutomaticMigrationsEnabled = false; 
     MigrationsNamespace = "YourProject.Models.ContextNamespace2"; 
    } 
} 

Tuttavia, ho già avuto i 2 basi di dati stabiliti con i propri contesti definiti così mi sono trovato sempre un errore che dice "YourProject.Models namespace ha già definito ContextNamespace1".Questo perché "MigrationsNamespace =" YourProject.Models.ContextNamespace2 ";" stava causando il dbcontext da definire nello spazio dei nomi YourProjects.Models due volte dopo che ho provato l'Init (una volta nel file Context1Init di migrazione e una volta dove l'avevo definito prima).

Così, ho scoperto che quello che ho dovuto fare a quel punto era iniziare il mio database e le migrazioni da zero (per fortuna non ho avuto i dati di cui avevo bisogno per mantenere) attraverso seguendo le indicazioni qui: http://pawel.sawicz.eu/entity-framework-reseting-migrations/

Poi Ho cambiato il codice per NON includere la riga MigrationsNamespace.

internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{ 
     public Configuration1(){ 
     AutomaticMigrationsEnabled = false; 
    } 
} 

internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{ 
    public Configuration2(){ 
     AutomaticMigrationsEnabled = false; 
    } 
} 

Poi ho eseguito il comando Add-migrazione configurazione beta Configurazione1 Context1Init ancora e ancora la linea Configurazione1 Update-Database configurazione beta (per il mio 2 ° contesto troppo), e, infine, tutto sembra funzionare grande ora.