Dependency Injection is an important part of writing loosely coupled, easily tested code. I’ve written a bit about it before, but not in the context of Microsoft Orleans.
Microsoft Orleans, like most (all?) applications, can make use of dependency injection. How do we do it in Orleans? Luckily, it is accomplished in a very similar manner to what you should already be used to when working with .net core!
If you aren’t familiar with .net core DI, a quick sample:
public interface IStuffDoer
Within (generally) your Startup.cs or thereabouts:
// This method gets called by the runtime. Use this method to add services to the container.
And that’s pretty much all there is to it (as it relates to a MVC/WebApi site anyway). When instances of
IStuffDoer are needed in class constructors, an instance is injected into the class — in this case the same instance, since we registered it as a singleton. You can read more about dependency injection here:
We can demonstrate this dependency injection concept in Orleans by building a new
IOrleansFunction of course! Note that this functionality was created for my Orleans series in:
First, let’s start with our non grain related code — the stuff that we’ll be using and registering with the IOC container.
An email sending interface:
public interface IEmailSender
and an implementation:
public class FakeEmailSender : IEmailSender
We can register this
FakeEmailSender in our
ISiloHostBuilder. I use a little helper class to keep all my DI registration in its own area, separate from the ISiloHostBuilder.
public static class DependencyInjectionHelper
Call the helper class from the
ISloHostBuilder method now looks like:
private static async Task<ISiloHost> StartSilo()
I’m just going to put into place a grain that sends out an email, using out new dependency injected service. Yes, we could just write the email sending within the grain itself, but I wanted to show off dependency injection Additionally, this way we can swap in a “real” implementation w/o the (small amount of) boilerplate involved with standing up a grain.
New Grain Interface:
public interface IEmailSenderGrain : IGrainWithGuidKey, IGrainInterfaceMarker
public class EmailSenderGrain : Grain, IEmailSenderGrain, IGrainMarker
In the above Grain, we’re taking in an instance of an
IEmailSender for use within the actual implementation of the
IEmailSenderGrain contract. With the setup we did in the
FakeEmailSender is passed into the class automatically.
Now we need to wire up the new grain call into our console app menu — luckily this is simple due to the refactor pointed out in the above blog post.
Add a new class that implements
public class DependencyInjectionEmailService : IOrleansFunction
Let’s see what this looks like running.
dotnet runthe SiloHost
dotnet runthe client
- select whatever option it ends up being for the new grain
Note in the above that the “email” is being shown in the Orleans console, as the
FakeEmailSender told it to just “log”, and from the context of where the function is running, it hits the Orleans log, rather than the menu-ed console app.
That’s all there is to it!
Code at this point is in this release on the GitHub repository:
- Getting Started with Microsoft Orleans
- Microsoft Orleans — Reusing Grains and Grain State
- Using polymorphism to update Orleans Project to be ready for new Orleans Examples!
- Microsoft Orleans — Reporting Dashboard
- Microsoft Orleans — Code Generation Issue
- Code as of end of blog post — v0.40
- Microsoft Docs — Dependency Injection