Dependency injection in long-running Windows service - Composition root the right idea?
Asked Answered
S

2

10

I am writing a Windows Service that (if all goes according to plan) will run for months at a time. Throughout the lifetime of the service, it will maintain several WCF services (named pipes for communicating with other local apps), it will run an embedded data store (RavenDB probably, which is more or less irrelevant to this question), and it will use some third-party software to maintain a SIP service (VoIP functionality). Some of the challenges I am facing means having to react to events and create some new business objects to handle what these events represent.

Now, I've read Mark Seemann's book (at least the relevant parts for this discussion), and I understand why a service locator is bad, and why handling this in the composition root (service starting point in my case) is good - in the general case.

However, what I don't understand is how this can apply to every situation. I see how it would be perfect in the cases where you CAN compose your whole root at the start of the application, or in something like MVC where the IoC engine is used per request by the framework. However, for a long-running service, I imagine it would be inefficient in the best case, and impossible in some cases, to create all objects up front. I can't imagine being able to write a non-trivial service that gets all the objects it could ever need up front, and never need to create new ones as life goes on.

Now, this isn't enough to lure me to the dark side and take on the expense of hidden dependencies like a service locator would have me do. But what is the right thing to do here? If I have a CallHandlerService that needs to be created in response to each incoming call (because it uses expensive, unmanaged resources for instance), how do I go about doing that?

Composition root + just a wee bit of service locator?

That last part wasn't serious, but I would still love to know how to solve this properly.

Stripe answered 18/5, 2013 at 15:3 Comment(4)
The composition root is where you "compose" the graph, defining relationships and interdependencies. It is not where you create the objects.Saros
That's fine, no problem with that - but how do I create the objects, without introducing a de facto service locator?Stripe
You'll reference the container to instantiate the root object. So your service will rely on the container, all the other dependencies are injected (usual stuff). Object lifetime is managed by the container.Saros
An objects ability to act as a singleton should not decay over time - hours, day or months). The container should be the most capable class in this scenario.Saros
P
16

It is possible to break the question into two:

  • How to manage scope
    • Avoid creation of all possible dependencies up-front
    • Create only required dependencies
  • How to manage lifetime
    • Keep references to already created dependencies
    • Reuse dependencies if it is possible
    • Cleanup them as soon as possible

How to manage scope

You could define either interfaces or realizations of several specialized Abstract Factories, each of them will limited to manage only its own special sort of dependencies. For example: one for database related dependencies, another SIP related. Then you could inject factories themselves, not dependencies, and retrieve dependencies from them.

Isn't it sound like Service Locator? Yes it is, but it is not Service Locator. You could read more on this at Mark Seemann blog: Abstract Factory or Service Locator?.

And if Abstract Factories are ok, take a look at Ninject.Extensions.Factory to create them automatically based upon your IKernel configuration

How to manage lifetime

If you are going to run your service 24-7, this is more complex and important part. Here I could give you only several practical advises:

  • For such kind of services, failures at external dependency side (i.e. database, services) are routine, not an exception
  • Do not keep any references to dependencies yourself in code aiming for performance or reuse
  • Do not use complex lifetimes (for example named scope for Ninject )
  • Do release dependencies as soon as possible. Configure Ninject to manage them for you.
  • Think about timeouts for dependencies, to recreate new instances
  • May be for long-running tasks, create separate instance of Kernel, and make sure that you have disposed them afterwards
Phoneme answered 19/5, 2013 at 5:18 Comment(2)
Excellent answer, but I still find myself struggling to not "ServiceLocate" those factories. How do I inject them? Do I keep them at static singletons? Thanks!Standin
Difference between Service Locator and Abstract Factory is that second one could create only specific dependencies, in contrast locator one could create almost anything. You could found more in Mark's article, but most important is following: An Abstract Factory is a generic type with a non-generic Create method; a Service Locator is a non-generic type with a generic Create method.Phoneme
E
0

In my humble opinion, Dependency Injection (used for providing services in general) is somewhat of a dark art and really requires some insight on how and when to use it properly.

From my experience, trying to debug and application in which the injected dependencies had bugs or exceptions was a tedious process at best, especially if the particular object was one of several hidden behind the same Interface. This is why unit testing of all objects using that Interface is important, because you never know which one would fail with the same inputs.

Your dependency injection would be best used on your service start-up to create handlers for each type of call, then when each call comes in, dispatch appropriately. However, I think trying to create an object to handle each call is something which adds more overhead and complexity.

Go with your gut instinct and keep it lean & mean.

Extrauterine answered 18/5, 2013 at 15:18 Comment(1)
-1 You didnt answer the question hence I submit that this, while being a valid opinion should be a comment. I also dont agree your assessment of the OP's 'gut instinct' but I guess said OP should be the authority on that!Hannahannah

© 2022 - 2024 — McMap. All rights reserved.