Dependency Injection in Microsoft Orleans

The source code for this article is available at the Gigi Labs BitBucket repository.

Microsoft Orleans added support for dependency injection in version 1.1.0, which was released in December 2015. It is based on ASP .NET Core’s style of dependency injection.

To see how this works, let’s create an interface and a class that implements it. It is not important what they do, but we will use them as dependencies.

    public interface IWhatever
    {
    }

    public class Whatever : IWhatever
    {
    }

In order to set up our dependency injection bindings, we need a special startup class with a ConfigureServices() method with a predefined signature. In our case, it will look like this:

    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IWhatever, Whatever>();

            return services.BuildServiceProvider();
        }
    }

Here we are assuming that our dependency will be in singleton scope, but if you’ve used dependency injection before, you’ll know that there are other possible scopes. In fact, you’ll notice that there are different methods supporting a variety of scopes:

orleans-di-scopes

Once we have a startup class, we need to tell Orleans to use it. This can be done either in XML configuration (refer to the official documentation on Orleans DI to see how to do this) or in code as follows:

            var config = ClusterConfiguration.LocalhostPrimarySilo();
            config.UseStartupType<Startup>();

If you’re using an Orleans Dev/Test Host project type, you’ll find this cluster configuration in OrleansHostWrapper.cs. Note that dependency injection can only be done in the server (silo) and is not currently supported in the client (so you won’t find UseStartupType() on your ClientConfiguration).

Now that we have our dependency all set up, all we need is a Grain that uses it.

    public interface IHello : IGrainWithIntegerKey
    {
        Task HelloAsync();
    }

    public class HelloGrain : Grain, IHello
    {
        private IWhatever whatever;

        public HelloGrain(IWhatever whatever)
        {
            this.whatever = whatever;
        }

        public Task HelloAsync()
        {
            Console.WriteLine("Hello!");
            return TaskDone.Done;
        }
    }

In the main program, let’s create this and call it:

            var grain = GrainClient.GrainFactory.GetGrain<IHello>(0);
            grain.HelloAsync();

Now we can run the program and see that it works without problems:

orleans-di-running

We can verify that dependency injection is working by commenting out the binding configuration in the startup class. Without it, Orleans cannot activate the grain because it cannot resolve the dependency:

orleans-di-failed