Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Injector / IoC - Windows Service and Request Cycles of a Queue Processor

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.

like image 205
BuddyJoe Avatar asked Jan 14 '15 16:01

BuddyJoe


1 Answers

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).

like image 109
Steven Avatar answered Oct 15 '22 13:10

Steven