Sto creando API Web in ASP.NET Core 1.1.
Ho un numero di database diversi (per sistemi diversi) che hanno schemi di base comuni per elementi di configurazione come Configurazione, Utenti e gruppi (circa 25 tabelle in tutto). Sto cercando di evitare di duplicare la configurazione EF piuttosto estesa per la parte condivisa del modello ereditando da una classe base come mostrato nel diagramma.
Tuttavia, ciò non funziona a causa del requisito Entity Framework (EF) per trasmettere DbContextOptions<DerivedRepository>
come parametro al costruttore, in cui DerivedRepository
deve corrispondere al tipo di repository su cui viene chiamato il costruttore. Il parametro deve quindi essere passato al DbContext
base chiamando :base(param)
.
Pertanto, quando (ad esempio) InvestContext viene inizializzato con DbContextOptions<InvestContext>
, chiama base(DbContextOptions<InvestContext>)
e EF genera un errore perché la chiamata al costruttore ConfigurationContext
riceve un parametro di tipo DbContextOptions<InvestContext>
digitare DbContextOptions<ConfigurationContext>
. Poiché il campo delle opzioni su DbContext è definito come
private readonly DbContextOptions _options;
Non riesco a vedere un modo per aggirare questo.
Qual è il modo migliore per definire il modello condiviso una volta e usarlo più volte? Immagino di poter creare una funzione di supporto e chiamarla da ogni contesto derivato, ma non è così pulita o trasparente come l'eredità.
OK, ho funzionato in un modo che usa ancora la gerarchia dell'eredità, come questa (usando InvestContext
dall'alto come esempio):
Come indicato, la classe InvestContext riceve un parametro costruttore del tipo DbContextOptions<InvestContext>
, ma deve passare DbContextOptions<ConfigurationContext>
alla sua base.
Ho scritto un metodo che scava il connectiontring da una variabile DbContextOptions
e crea un'istanza DbContextOptions del tipo richiesto. InvestContext utilizza questo metodo per convertire il parametro delle opzioni nel tipo giusto prima di chiamare base ().
Il metodo di conversione è simile al seguente:
protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
{
var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);
if (sqlExt == null)
throw (new Exception("Failed to retrieve SQL connection string for base Context"));
return new DbContextOptionsBuilder<T>()
.UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
.Options;
}
e la chiamata al costruttore InvestContext cambia da questo:
public InvestContext(DbContextOptions<InvestContext> options):base(options)
a questa:
public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
Finora sia InvestContext che ConfigurationContext lavorano per query semplici, ma sembra un po 'un hack e forse non qualcosa che i progettisti di EF7 avevano in mente.
Sono ancora preoccupato per il fatto che EF si farà un nodo quando cerco query complesse, aggiornamenti, ecc. Sembra che questo non sia un problema, vedi sotto)
Modifica: ho riscontrato questo problema come problema con il team EF7 qui , e un membro del team ha suggerito una modifica al core EF Core come segue:
"Dovremmo aggiornare il controllo per consentire a TContext di essere un tipo derivato dal tipo di contesto corrente"
Questo risolverebbe il problema.
Dopo un'ulteriore interazione con quel membro del team (che è possibile vedere sul problema) e alcuni scavi attraverso il codice EF Core, l'approccio che ho delineato sopra sembra sicuro e l'approccio migliore finché non viene implementata la modifica suggerita.
Vorrei portare questo post dal numero GitHub del PO all'attenzione di tutti:
Sono stato in grado di risolverlo senza un hack fornendo un costruttore protetto che utilizza DbContextOptions senza alcun tipo. La protezione del secondo costruttore garantisce che non venga utilizzato da DI.
public class MainDbContext : DbContext {
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options) {
}
protected MainDbContext(DbContextOptions options)
: base(options) {
}
}
public class SubDbContext : MainDbContext {
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options) {
}
}