Reputation: 27220
In our solution, we use dependency injection to instantiate instances of a DbContext
class which many other services depend on:
var container = new UnityContainer();
container.RegisterType<OurAppDbContext>(new PerThreadLifetimeManager());
// ... A bunch of services that take a new OurAppDbContext as an argument
This successfully results in instantiating a dozen or so services with their own OurAppDbContext
instances.
The Problem
The OurAppDbContext
constructor (auto-generated by EntityFramework, therefore not editable) is not quite sufficient for our purposes. If we had it our way, every instance would be constructed as such:
OurAppDbContext newDbContext = new OurAppDbContext();
newDbContext.Database.Log += log => Debug.WriteLine(log);
return newDbContext;
Right now, the way we can hook this event is in every constructor of every service that gets one of these instances. e.g.:
public class AdminDataRepository : IAdminDataRepository
{
private readonly OurAppDbContext _dbContext;
public AdminDataRepository(OurAppDbContext dbContext)
{
_dbContext = dbContext;
_dbContext.Database.Log += log => Debug.WriteLine(log);
...
But we would rather not have this code copy-pasted into 10+ different service constructors as it becomes a maintenance headache, we might miss it in a few places, or they might get out of sync, etc.
Is there a way to instruct the dependency injector to do this to each OurAppDbContext
it constructs - before it passes it along to whatever service it's constructing that depends on it?
Upvotes: 0
Views: 472
Reputation: 27220
A potential solution occurred to me, turns out it works!
We can create our own DBContext
class derived from the EF auto-generated one:
public class LoggingOurAppDbContext : OurAppDbContext
{
public LoggingOurAppDbContext() : base()
{
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}
}
And instruct Unity (dependency injection) to create instances of these rather than instances of the EF-generated one.
It should "just work" - without even editing the constructor arguments of all the services that depend on a PortfolioAccumulationDbContext instance - because inheritance makes the two types interchangeable.
I'm familiar with the syntax for providing a concrete type to implement an interface wherever it's required as a dependency:
container.RegisterType<ISomeInterface, SomeConcreteType>();
But I wasn't sure it would work in my case since the base OurAppDbContext
auto-generated by entity framework does not implement any kind of interface, all services that require one are expecting an instance of the concrete type OurAppDbContext
. It turns out the same syntax can be used to provide a more-derived type to satisfy an already-concrete type argument:
container.RegisterType<OurAppDbContext, LoggingOurAppDbContext>(...);
Problem solved.
Upvotes: 1
Reputation: 172646
You can make the registration using a factory delegate:
container.RegisterFactory<OurAppDbContext>(c =>
{
OurAppDbContext newDbContext = new OurAppDbContext();
newDbContext.Database.Log += log => Debug.WriteLine(log);
return newDbContext;
},
new PerThreadLifetimeManager());
Upvotes: 2