I've done this so many times but I never end up liking how my code turns out. I'm asking if anyone else has the same problems and can offer solutions or conventions that they've used to make cleaner code.
When I build a C# service, I get TopShelf, Log4Net, and Ninject all installed and set up. Inside of my service project, I end up with a class like this. The part I'm most interested in is SuperLargeStaticClassThing.RegisterDependencies(_kernel);
sealed class ServiceMain
{
[NotNull]
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IKernel _kernel;
/// <summary> Starts the service.</summary>
public void Start()
{
Log.Info("Service Start");
// Step 1: Register Ninject Dependencies
_kernel = new StandardKernel();
SuperLargeStaticClassThing.RegisterDependencies(_kernel);
// Step 2: Start Service Endpoints
_kernel.Get<IEndpoint1>().Start();
// ...
}
/// <summary> Stops the service.</summary>
public void Stop()
{
Log.Info("Service Stop");
// Stop Service Endpoints
_kernel.Get<IEndpoint1>().Stop();
// ...
}
}
And my SuperLargeStaticClassThing.RegisterDependencies(_kernel);
method ends up being huge, ugly, monolithic, you name it. It's hundreds of lines of just binding interfaces to other types. Sometimes you can get away with convention based registration to make it shorter, but I feel like this isn't always a solution either. Not only is the method huge, it seems unwieldy. Sometimes certain dependencies need to be swapped out in chunks, so I'd enjoy seeing the different categories of dependencies registered together. I feel like there has to be a better way, but I'm stuck in finding one that I like.
Does anyone have any suggestions on a pattern to solve the problem of a huge and unwieldy registration method?
_kernel.Get<>()
(like in my post). How I build my components in a custom way now is by registering them with Ninject, and using methods likeWithConstructorArgument()
,OnActivation()
andOnDeactivation()
. Ninject also handles object lifetimes, which is a huge plus for long running services. The big problem is just that this extra "stuff" is in one hard-to-manage place.Creator.Make<Iinterface>()
is called, it retrieves the first type from the dictionary, walks the constructor tree looking for all the necessary types, instantiates them in order, and automagically wires everything up.