I'm writing a queue processor in C# as a Windows Service. The backend queue mechanism is MongoDB. The purpose of the queue is to run out-of-band requests that originated from our main website (Angular w Web API). For each queued item, a serialized Command instance + the serialized Context info, I would like to
foreach request cycle:
1) New up a DbContext (EF) if it is needed for the current Command Handler
2) Deserialize the AppContext and get that info injected into the current Command Handler
Not sure how to handle these patterns in Simple Injector. Especially since this is the cycle of Timer not a Web Request that there are helpers/classes already written for. Thoughts? I have seen other IoC containers use lambda expressions to handle this kind of stuff in the past. Just not sure how to approach my #1 and #2 scenarios.
Each timer pulse can be considered a new request. Or if you are processing multiple commands in one single pulse, each command can be considered a new request.
Some frameworks such as ASP.NET and WCF have the notion of a request (web request, WCF operation, etc.) and this allows Simple Injector to plug into the framework's request model. Because of this, Simple Injector contains integration packages for MVC, Web API and WCF. Those integration packages hook into the request model of the framework and this allows you to register per-request instances without having to do anything special.
Windows Services, however, don't provide us with such request-based model. This means that you will have to define the request boundaries manually. This holds for all DI containers; not only Simple Injector.
Simple Injector contains two different Lifestyles that allow you to create an explicit scope. These are the ThreadScopedLifestyle and AsyncScopedLifestyle. The ThreadScopedLifestyle
is thread-specific, while the AsyncScopedLifestyle
can be used when processing asynchronous operations; it allows the scope to flow over asynchronous method calls.
TIP: Prefer the use of AsyncScopedLifestyle
over ThreadScopedLifestyle
, since it will work for both asynchronous as single-threaded operations. ThreadScopedLifestyle
should typically only be used when you are running a .NET 4.0 application, since AsyncScopedLifestyle
is only available for .NET 4.5, .NET Core and .NET Standard 1.3.
This means that you will have to start a new 'scope' in the container, before resolving a new service from the container that you use to process that command. For instance:
public void ProcessCommand(object command) {
using (AsyncScopedLifestyle.BeginScope(this.container)) {
Type handlerType =
typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic handler = container.GetInstance(handlerType);
handler.Handle((dynamic)command);
}
}
By wrapping the operation in a lifetime scope, we allow services to be reused. We can do this by registering them using the AsyncScopedLifestyle
:
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IUnitOfWork, DbContextUnitOfWork>(Lifestyle.Scoped);
Services that are registered using the lifetime scope will live for the duration of that scope and will get disposed when the scope gets disposed (in the example at the end of the ProcessCommand
method).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With