Alain
Alain

Reputation: 27220

Dependency injection to include post-construction actions?

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

Answers (2)

Alain
Alain

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

Steven
Steven

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

Related Questions