A while back, I blogged about using open generics with .NET Core Dependency Injection. It really does work great for an out of the box experience. However, one drawback is that you can’t use a factory pattern like you can when injecting in a type-specific way.
The factory pattern is one design pattern that is often useful in the context of DI. What is the factory pattern, though?
Googling for factory pattern provides us with a pretty succinct definition.
The group includes the Simple Factory, Factory Method and Abstract Factory patterns: … The Factory method lets a class defer instantiation to subclasses. Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Like my previous post, I’m dealing with an IRepository<T>. My repository is using EF Core, but I wanted to be able to change the context that is used based on what namespace T was in. The condition isn’t important, though, and it could be any condition relevant to the creation of objects.
In order to achieve a factory pattern with .NET Core DI, we have to implement a sort-of facade pattern on top of the open generics. As with the previous post, I’m still injecting my “DomainRepository” through the open-generics paradigm:
services.AddScoped(typeof(IRepository<>), typeof(DomainRepository<>));
Next the DomainRepository<T> is effectively going to have a private member that implements IRepository<T> and expose all of that member’s interface methods through its facade. We’ll inject an IServiceProvider into the DomainRepository so that it can obtain a reference to whatever is relevant in constructing its IRepository<T>. The full code illustrating switching contexts for the same DomainRepository based on what namespace the T model lives in and creating the IRepository is below.
// This could be a factory if we really wanted to swap between contexts .. // Why is this necessary? Well, .NET Core open generics don't support factories (sigh) public class DomainRepository<T> : IRepository<T> where T : class { // This is our private repository private IRepository<T> _repo; public DomainRepository(IServiceProvider serviceProvider) { // We're going to use namesapces to determine how to construct our IRepository<T> var type = typeof(T); var ns = type.Namespace; if (ns.ToString().Contains(typeof(Commissions.ClientModels.DataModels.ObjectType1).Namespace.ToString())) { var dbContext = (DbContext1)serviceProvider.GetService(typeof(DbContext1)); _repo = new BaseRepository<T>(dbContext); } else if (ns.ToString().Contains(typeof(Commissions.ClientModels.DataModels.ObjectType2).Namespace.ToString())) { var dbContext = (DbContext2)serviceProvider.GetService(typeof(DbContext2)); _repo = new BaseRepository<T>(dbContext); } else { var dbContext = (DbContext3)serviceProvider.GetService(typeof(DbContext3)); _repo = new BaseRepository<T>(dbContext); } } // IRepository<T> methods here. This is where it is a bit redudant with the facade public int Count => _repo.Count; public string DbSchema => _repo.DbSchema; public DbContext DbContext => _repo.DbContext; public void Add(T entity) { _repo.Add(entity); } public IQueryable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includes) { return _repo.Get(filter, orderBy, includes); } // ... etc }
Of course, this is a tad contrived, and the one unfortunate side-effect is that it does create a bit of redundancy through interface (re)implementation. But, it’s light enough and we do achieve our goal of having open generics with a factory. It is also a write-once-use-wherever scenario, so our factory can be reused as needed in various projects. Being able to provide open-generic factories through .NET Core DI opens a lot of possibilities in terms of code reuse and startup simplification.