This is a series of blog posts:
- Part 1: Stateful service
- Part 2: Stateless service
- Part 3: Unit testing
The template of a Service Fabric application that is shipped with Visual Studio instantiates all services explicitly. Like and any other basic template, such as of ASP.NET infrastructure pipeline, it instantiates all filters and handlers right away as well.
But sooner or later you’ll likely have to pass to your service some dependencies, which have their own dependencies, and so on. This way you end up having a dependency tree. Natural answer to its growing complexity would be using a Dependency Injection (DI) container. I cannot recommend more Simple Injector for its performance and elegance of API.
Another reason to use a container would be the design principle which suggests that all constructor calls in your code (the new
keyword in C-like languages) are the aspects of explicit lifetime (aka lifestyle) management scattered across your codebase. While actually it has to be performed by a container in one, centralized place called composition root.
ServiceRuntime.RegisterServiceAsync(nameof(MyStatefulService) + "Type", context => CreateService(context))
.GetAwaiter()
.GetResult();
private static StatefulServiceBase CreateService(StatefulServiceContext context)
{
var container = ContainerConfig.CreateContainer(context);
return container.GetInstance();
}
Now you only need to register the service (optionally) and the service context (mandatory):
internal static class ContainerConfig
{
public static Container CreateContainer(StatefulServiceContext context)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.RegisterInstance<StatefulServiceContext>(context);
container.RegisterInstance<ServiceContext>(context);
container.RegisterSingleton<MyStatefulService>();
return container;
}
}
The caveat here is to map service context to two types, StatefulServiceContext
and ServiceContext
. Why? Because you’ll likely have both steteful and stateless services in your application, and their dependencies, such as configuration management, will likely depend on the base type ServiceContext
. Meanwhile services require the concrete type in their respective constructors. The good thing is that the same instance would be resolved in both cases:
public sealed class MyStatefulService
{
public MyStatefulService(StatefulServiceContext context, ...)
{
}
}
public sealed class ServiceFabricConfigurationManager : IConfigurationManager
{
public ServiceFabricConfigurationManager(ServiceContext context, ...)
{
}
}
That’s it! Now your stateful service itself and all its dependencies are recursively instantiated and controlled by the container.