Reputation: 4712
I've used DI across multiple ASP.NET (Core) applications where DbContext is injected into a controller constructor, and upon the first request to the controller, the constructor is ran and the dependency injected.
I'm now working with a web API project that is hooked into an Azure Message Bus, processing and persisting data from the bus and providing an API endpoint for the data. The class processing and persisting the messages from Azure Message Bus is QueueProcessor
.
I need to persist the data from project start, which means I need an instance of DbContext from when the project is ran as opposed to when I'm querying data out via an API endpoint. Due to this, the constructor of QueueProcessor
is never implicitly called, and if I want to manually do so I need a pre-existing instance of MyDbContext
to pass to it.
I've been researching this for a couple of days, trying out some manual patterns but I'm running into concurrency issues and the whole project feels like a hack at the moment.
This is what I'm currently doing:
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("default")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
I'm then having to create a new instance of my QueueProcessor
and use var context = new MyDbContext()
inside the constructor. This is causing concurrency issues and completely negates the DI
.
If I want to inject MyDbContext
with DI like I have done hundreds of times, creating an instance of the class to run a process inside the QueueProcessor
means I have to pass an instance of MyDbContext
to the constructor myself. VS flags this up at design time.
I've ever tried botching this as much as possible by doing:
services.AddTransient(x => new QueueProcessor(x.GetService<MyDbContext>(), Configuration.GetConnectionString("MessageBus")));
I'm looking for a cleaner way of achieving:
QueueProcessor
to handle Azure Message Bus messages from project start.MyDbContext
that I can use to persist the messages when they get processed.Controller
to set up the DI correctly.Is there a way to achieve this or have I completely missed the mark here?
Upvotes: 3
Views: 1798
Reputation: 4712
A working solution, not sure if it's the best pattern:
Startup.cs
chance ConfigureServices
to return IServiceProvider
rather than void
.var serviceProvider = services.BuildServiceProvider();
var context = serviceProvider.GetRequiredService<MyDbContext>();
QueueProcessor
and pass context
to the constructor and invoke method to begin processing messages.ConfigureServices
, return your new serviceProvider
.Full code block:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("...")));
var serviceProvider = services.BuildServiceProvider();
var localMyDbContext = serviceProvider.GetRequiredService<MyDbContext>();
Processor = new QueueProcessor(localDbContext, Configuration.GetConnectionString("Other"));
Processor.RunAsync().GetAwaiter().GetResult();
return serviceProvider;
}
Upvotes: 1