I have a need to inject a dependency into my DbContext Class for my project. I am trying to do this in a way that follows "Clean Architecture".
I need to be able to use the DbContext's empty constructor and still reference the connection string from a config file.
I make use of this Generic Repository and inject this into services. It is defined in the Application Layer.
public class GenericRepository<C, T> : IGenericRepository<C, T> where T : class where C : DbContext, new()
{
private DbContext _entities = new C();
}
In my EF Core Context class (defined in the persistence layer) I have this:
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());
}
}
}
I also have the implementation for IConnectionStringProvider defined in the Persistence layer. I want this implementation to be easily swappable if need be. At the moment it reads from App.config and uses the ConfigurationManager nuget package. But this could change in the future so it needs to be easily swappable.
The IConnectionStringProvider is defined in the Application layer:
public interface IConnectionStringProvider
{
string GetConnectionString();
}
In my Presentation layer I have an ASP.NET Core MVC project. In StartUp.cs I am using the standard way of injecting the dbContext to my contollers:
services.AddDbContext<ContractsWorkloadContext>();
I can get this to work just fine if I override the OnModelCreatingMethod in the following way.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
_connectionStringProvider = new ConnectionStringProvider();
optionsBuilder.UseSqlServer(ConnectionStringProvider.GetConnectionString());
}
}
This method allows me to use connection string values from configuration with an empty context constructor instance, which is needed for it to work with my generic repository, and also still be able to use the constructor with a DbContextOptionsBuilder parameter.
My only problem left to solve is how to achieve this without the Context being dependent upon the implementation.
I have tried Autofac property injection by registering the IConnectionStringProvider interface and implementation and also registering the Context class with properties "Autowired". But the property is always null.
And yes I understand that the above is a field and not a property. I have tried to modify the context class to have a property instead of a field. I have tried to even create a method to set the field and use method injection with autofac as well. But the property/field is always null in every case.
builder.RegisterType<ConnectionStringProvider>().As<IConnectionStringProvider>()();
builder.RegisterType<MyContext>().PropertiesAutowired();
And
builder.RegisterType<ContractsWorkloadContext>().OnActivated(e =>
{
var provider = e.Context.Resolve<IConnectionStringProvider>();
e.Instance.SetConnectionstringProvider(provider);
});
In summary I need to:
Any help here would be appreciated.
Thanks.
This is a better way... for any who stumble upon this..
The more specific Repository implementations are just classes that extend the base repository implementation. Such as:
EFCustomerRepository : EFRepository<MyContext, Customer>
Now I can inject IMyEFCoreDatabase
any where throughout the app via dependency injection and reference the repositories stored on the implementation.
This is a better way than above because my repositories no longer call new C(). Instead they now inject the context via constructor injection.