Service Fabric and Dependency Injection, part 2: Stateless Service

This is a series of blog posts:

Last time we instantiated a stateful service from a DI container, it was elegant and relatively easy. What I can’t say about instantiating a stateless service. The problem is that it needs a reference to the same container in 3 different places: Program.cs, MyStatelessService.cs, Startup.cs.

First the near identical code between stateless and statefull services:

ServiceRuntime.RegisterServiceAsync(nameof(MyStatelessService) + "Type", context => CreateService(context))
              .GetAwaiter()
              .GetResult();

private static StatelessServiceBase CreateService(StatelessServiceContext context)
{
    var container = ContainerConfig.CreateContainer(context);
    return container.GetInstance<MyStatelessService>();
}

Now where the differences start. Let’s start from refactoring the Startup class. From the template it comes as a so-called conventional startup, in other words follows the Convention over Configuration principle. Now it inherits StartupBase and accepts Container in its constructor:

internal sealed class Startup : StartupBase
{
   private readonly Container _container;

   public Startup(Container container)
   {
       _container = container;
   }
}

Then inject IStartup into service’s ctor alongside its other dependencies (if any):

internal sealed class MyStatelessService : StatelessService
{
    private readonly IStartup _startup;

    public MyStatelessService (StatelessServiceContext context, IStartup startup, ...)
       : base(context)
    {
        _startup = startup;
    }

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        yield return new ServiceInstanceListener(serviceContext =>
            new HttpSysCommunicationListener(serviceContext, nameof(MyStatelessService) + "Endpoint", (url, listener) =>
            {
                var builder = new WebHostBuilder();
                return builder.UseHttpSys()
                              .ConfigureServices(services => services.AddSingleton(serviceContext)
                                                                     .AddSingleton(_startup))
                              .UseContentRoot(Directory.GetCurrentDirectory())
                              .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                              .UseUrls(url)
                              .Build();
            }));
    }
}

The CreateServiceInstanceListeners() method differs from the template just slightly. Instead of UseStartup() now we use AddSingleton(_startup). What means the Startup class should be registered in the container by its interface:

container.RegisterInstance<ServiceContext>(context);
container.RegisterInstance<StatelessServiceContext>(context);
container.RegisterSingleton<MyStatelessService>();

container.RegisterSingleton<IStartup, Startup>();

The only thing left is to configure the ASP.NET Core infrastructure pipeline to use the container, for more details see the documentation:

public override void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<IControllerActivator>(new SimpleInjectorControllerActivator(container));
   services.AddSingleton<IViewComponentActivator>(new SimpleInjectorViewComponentActivator(container));

   services.EnableSimpleInjectorCrossWiring(container);
   services.UseSimpleInjectorAspNetRequestScoping(container);
}

public override void Configure(IApplicationBuilder app)
{
    container.AutoCrossWireAspNetComponents(app);
}

That’s it! Now your statefull service itself and all its dependencies are recursively instantiated and controlled by the container.

This entry was posted in Programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.