Ho bisogno di iniettare una dipendenza nella mia classe DbContext per il mio progetto. Sto cercando di farlo in un modo che segue "Clean Architecture".
Devo essere in grado di usare il costruttore vuoto del DbContext e fare comunque riferimento alla stringa di connessione da un file di configurazione.
Uso questo repository generico e lo inserisco nei servizi. È definito nel Livello applicazione.
public class GenericRepository<C, T> : IGenericRepository<C, T> where T : class where C : DbContext, new()
{
private DbContext _entities = new C();
}
Nella mia classe Contesto EF Core (definita nel livello di persistenza) ho questo:
public partial class MyContext : DbContext
{
private IConnectionStringProvider _connectionStringProvider;
public virtual DbSet<MyEntity> { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(_connectionStringProvider.GetConnectionString());
}
}
}
Ho anche l'implementazione per IConnectionStringProvider definita nel livello Persistence. Voglio che questa implementazione sia facilmente sostituibile se necessario. Al momento legge da App.config e utilizza il pacchetto nuget ConfigurationManager. Ma questo potrebbe cambiare in futuro, quindi deve essere facilmente sostituibile.
IConnectionStringProvider è definito nel livello Applicazione:
public interface IConnectionStringProvider
{
string GetConnectionString();
}
Nel mio livello di presentazione ho un progetto ASP.NET Core MVC. In StartUp.cs sto usando il modo standard di iniettare dbContext nei miei controller:
services.AddDbContext<ContractsWorkloadContext>();
Posso farlo funzionare correttamente se eseguo l'override di OnModelCreatingMethod nel modo seguente.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
_connectionStringProvider = new ConnectionStringProvider();
optionsBuilder.UseSqlServer(ConnectionStringProvider.GetConnectionString());
}
}
Questo metodo mi consente di utilizzare i valori della stringa di connessione dalla configurazione con un'istanza del costruttore di contesto vuota, necessaria per funzionare con il mio repository generico, e di poter comunque utilizzare il costruttore con un parametro DbContextOptionsBuilder.
Il mio unico problema rimasto da risolvere è come raggiungere questo obiettivo senza che il contesto dipenda dall'attuazione.
Ho provato l'iniezione di proprietà di Autofac registrando l'interfaccia e l'implementazione di IConnectionStringProvider e registrando anche la classe Context con proprietà "Autowired". Ma la proprietà è sempre nulla.
E sì, capisco che quanto sopra è un campo e non una proprietà. Ho provato a modificare la classe di contesto per avere una proprietà anziché un campo. Ho anche provato a creare un metodo per impostare il campo e utilizzare anche l'iniezione di metodo con autofac. Ma la proprietà / campo è sempre nulla in ogni caso.
builder.RegisterType<ConnectionStringProvider>().As<IConnectionStringProvider>()();
builder.RegisterType<MyContext>().PropertiesAutowired();
E
builder.RegisterType<ContractsWorkloadContext>().OnActivated(e =>
{
var provider = e.Context.Resolve<IConnectionStringProvider>();
e.Instance.SetConnectionstringProvider(provider);
});
In sintesi, devo:
Qualsiasi aiuto qui sarebbe apprezzato.
Grazie.
Questo è un modo migliore ... per chiunque inciampa in questo ...
Le implementazioni del repository più specifiche sono solo classi che estendono l'implementazione del repository di base. Ad esempio:
EFCustomerRepository : EFRepository<MyContext, Customer>
Ora posso iniettare IMyEFCoreDatabase
qualsiasi punto dell'app tramite l'iniezione delle dipendenze e fare riferimento ai repository memorizzati sull'implementazione.
Questo è un modo migliore di sopra perché i miei repository non chiamano più il nuovo C (). Invece ora iniettano il contesto tramite l'iniezione del costruttore.
Quello che ho finito per fare è stato creare un riferimento statico alla stringa di connessione e quindi una proprietà di sola lettura nel contesto che restituisce quel valore.
In questo modo sono in grado di astrarre IConnectionStringProvider nel livello dell'applicazione e implementarlo nel livello Infrastruttura. Ciò consentirà di sostituire l'implementazione con il minimo sforzo, se necessario.
Inoltre, aggiungendo la proprietà readonly che restituisce la stringa di connessioni statica, sono in grado di utilizzare un repository generico che utilizza new () e ottenere comunque la stringa di connessione da un file di configurazione.
L'unico aspetto negativo di questo è che la stringa di connessione è archiviata in memoria per l'intera durata dell'applicazione.
Il valore statico è impostato dall'implementazione del provider nel file StartUp.cs nel livello di presentazione.
Funziona sicuramente ... Ma se c'è un modo migliore, allora sono tutto orecchie ... :)