I find dependency injection to be quite a useful pattern. It also happens to be a pattern utilized in many of the code bases I've found myself using the most often. Asp.Net Boilerplate and nopCommerce to name a couple, which use Castle Windsor and AutoFac respectively.
It wasn't until I started digging into .Net Core 2.1's native dependency injection that I realized, with a relatively small amount of work, you can write powerful applications that do a great deal of the heavy lifting for you!
The Problem Space
My goal is to get a better handle in .Net Core and Angular. One way I enjoy familiarizing myself with new technology is to try and create a workable Content Management System. I created a User, and Blog entity. My IRepository interface and Repository object. A UserService and a BlogService. I've barely gotten off the ground and now have 4 separate objects to register in the IoC controller!
My startup.cs class (which of course, this stuff can and should be moved to a configuration object) is already starting to become a mess, and I've barely created any functionality!
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ngCmsDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ngCmsConnectionString"), c => c.MigrationsAssembly("ngCmsBase.Web")));
services.AddScoped(typeof(IRepository<Blog, long>), typeof(Repository<Blog, long>));
services.AddScoped(typeof(IRepository<User, long>), typeof(Repository<User, long>));
services.AddTransient(BlogService);
services.AddTransient(UserService);
}
It's becoming apparent this is going to get out of hand and I am going to miss something - soon. But, there's an easy fix!
The scoping of the repository was really easy. I just removed the two lines for:
services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));
Registering the services, however, took a little more work and the magic of reflection.
First, I created an empty interface, and made my Services implement it. I then use reflection to track down all the objects that implement that interface, and simply register them on the fly, like so:
My interface is named: IngServiceBase
var type = typeof(IngServiceBase);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
foreach(var t in types)
{
services.AddTransient(t);
}
Now, no matter how many Entites I make, they will all immediately be able to make use of the Repository. And no matter how many services I write, so long as they implement the IngServiceBase interface, they are ready to be injected!