Dependency Injection in Akka .NET with state constructor parameters
Asked Answered
C

1

5

Having something like a string parameter in a constructor makes dependency injection very messy. Think:

public class CurrencyActor
{
    public CurrencyActor(string currency, IRepository repository)
    {
...

There have been other questions (such as this one) to address this particular problem with dependency injection. Often this is solved by rethinking the design and refactoring.

However, what if it actually makes sense to have multiple versions of an object that are each responsible for different data (e.g. a CurrencyActor for each currency)? This is pretty normal when using an actor model such as Akka .NET, but makes sense even outside that domain.

What is the best way to create these multiple instances using dependency injection while passing in the initial state they need?

Cunaxa answered 10/7, 2016 at 18:21 Comment(0)
A
11

Having a dependency in a constructor is not messy, it's very very common. There is nothing wrong with this.

You could create a default props static method on the CurrencyActor that takes in your dependencies:

public static Props CreateProps(string currency, Irepository repo)
    {
        return Props.Create(() => new CurrrncyActor(currency, repo));
    }

Then create as many as you like:

var usCurrency = system.ActorOf(CurrencyActor.CreateProps("US", someRepo), "US");
var swedishCurrency = system.ActorOf(CurrencyActor.CreateProps("SEK", someRepo), "SEK");

[Update]

Regarding the use of DI containers with Akka, this was listed as no. 2 out of the top 7 mistakes people make when using akka.net

https://petabridge.com/blog/top-7-akkadotnet-stumbling-blocks/

Thus it’s considered to be a good practice for actors to manage their own dependencies, rather than delegate that work to a DI framework.

So basically don't do it. And if you must, according to that article, Autofac is the best choice

[Update 2]

If you want to dynamically create new instances of the same Actor but change some initial state, then you could have a Supervisor that is responsible for creating them:

public class MatchesSupervisor : ReceiveActor
{
    List<IActorRef> _matches = new List<IActorRef>();
    public void MatchesSupervisor()
    {
        Receive<SomeCommandToStartANewMatch>(msg =>
        {
            // store the currently active matches somewhere, maybe on a FullTime message they would get removed?
            _matches.Add(Context.ActorOf(MatchActor.Create(msg.SomeMatchId)));
        }
    }
}

In the above example, there is no DI container being used, and if each MatchActor needed something else, like an IRepository, then this would be passed into the MatchesSupervisor when it is created, and subsequently passed to each MatchActor when they are created.

It also kinda depends where the state is coming from, and what the mechanism is for starting a new Match - i've just presumed some other Actor is sending a message.

(I'm typing on an ipad so the above code might not actually compile but hopefully you get the idea, i also left out an implementation of MatchActor, but it would just be an Actor that gets some values passed into its constructor)

Hope this helps!

Aircondition answered 11/7, 2016 at 7:10 Comment(8)
@Cunaxa What DI framework? Why do you need a DI framework? You can do dependency injection without a framework, and it is often a lot simplerAircondition
The question asks about DI in Akka .NET. That involves the use of a framework.Cunaxa
@Cunaxa Dependency Injection does not involve the use of a framework. ?? I'm very confused by your statement. You can 'inject dependencies' when using Akka the same way you would any other code, you do it in the Composition Root. If you look at the code in my example, does it not fulfil the requirements as set out in the question?Aircondition
I think i understand what you are asking now. You want the Actor System to resolve your dependencies for you through the use of a DI container, rather than manually wiring thing up in the Composition Root, is this correct? If so, it is probably container specific. Which one are you using? (I still think mostly its simpler not to use a container though..)Aircondition
No, I do want to set things up in the Composition Root. But there are certain types of actors which take state (such as an id or a name, for instance) that can't really be done this way. I'd like to find a pattern that makes sense for this kind of thing. Here's a concrete example: you want to have an actor for each match in the World Cup. They are different instances of the same actor, but with different state.Cunaxa
In my question, the "state" is the string passed in to the constructor.Cunaxa
Where does the state come from? Can you not have a supervisor actor that is responsible for creating Matches (using your example)?Aircondition
That is possible, yes. So you would just create them directly?Cunaxa

© 2022 - 2024 — McMap. All rights reserved.