Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented DI property injection in addition to used dependency provider #3933

Conversation

Ralf1108
Copy link
Contributor

@Ralf1108 Ralf1108 commented Sep 23, 2019

The current implementation of Akka.DI only allows injection of services via a dependency resolver (e.g. AutoFac).
But when instantiating actors it would be nice to also allow creating props not only via IoC but also set properties for an actor (e.g. an entity guid).

Here is a proposal to inject properties which are provided on actor props creation time.
These properties will be set after the dependency provider has resolved the required services.

The API proposal can be checked out in the unit test "DependencyResolver_should_inject_property_values_into_DiPerRequestActor"

Requirements:

  • The property has to be an instance property
  • The property has to have a public setter
  • The value of the property has to be serializable like all other props arguments
  • If the property was set via dependency provider it would be overwritten. (We could also implement a check and set only null/default properties)
  • Unknown properties will be skipped (or we throw an exception to catch programming errors early)

The current implementation should be independent of the used dependency resolver because here we just try to mimic additional ctor arguments for an actor.
Our current workaround was to send a "Initalize" message directly after creating an actor via DI and take care to resend this message in case of actor restart. But this is error prone and unnecessary.

What do you think?

@Horusiath
Copy link
Contributor

@Ralf1108 DI is being a subject of full redesign (including the fact it's going to be supported nativelly) - see #3862

@Ralf1108
Copy link
Contributor Author

Ah I see.
I couldnt found the use case I wanted to cover.

Instantiate an actor which requires services and also properties
e.g.

public class CustomerActor : ReceiveActor
{
    public CustomerActor (IDatabaseConnector db, Guid customerGuid)
    {
    }
}

The first ctor paramter will be resolved by autofac.
The second will be provided when the actor is instantiated and and not via DI.

var actorRef = system.ActorOf(() => CustomerActor ( ???, Guid.Parse("xxxxx"));

This is what I want to be covered.
I didnt find such case in your PR.
Will this be supported?

In my PR it would look like:

var props = Sys.DI().Props<CustomerActor >(x =>
{
	x.Set(a => a.CustomerGuid, Guid.Parse("xxxxx"));
});
var diActor1 = Sys.ActorOf(props);

@Horusiath
Copy link
Contributor

Mixed DI with manual argument insertion is not supported by default, which is pretty much the case with every other .NET framework in existence. If it's really needed (which honestly I doubt), you can always create 2 constructors: 1 for production system usage (which takes eg. ActorSystem's service resolver) and second used for testing.

@Ralf1108
Copy link
Contributor Author

The use case I wanted to support is for production environment. My example was only taken from a unittest.

I try to explain the use case in other words:

If your actors doesnt need external dependencies you can use the props to initialize the actor with property values like an entity guid or so.
Then you just create your actor via Props like:

var actorRef = system.ActorOf(() => CustomerActor (5);

But if your actor also requires external dependencies you cant express this in the current DI implementation and I also didnt found anything in the new approach.

class CustomerActor
    {
        /// <summary>
        /// this ctor can only be used without DI
        /// </summary>
        /// <param name="id"></param>
        public CustomerActor(int customerId)
        {
        }

        /// <summary>
        /// How can this ctor be expressed with using DI?
        /// </summary>
        public CustomerActor(int customerId, IDatabase database)
        {
        }
    }

In the second second ctor only the last parameter "database" should be injected by DI.

The underlying intention is:
Akka will take care of via the Props that the actor will be constructed with the desired arguments even in case of an actor restart or if the actor has to be instantiated on a remote akka system.

The current workaround is to create the actor with DI and send an "Initialization" message to the actor which would provide the additional properties (in the example above "customerID").
But then the implementor in the actor has to ensure that this "Initialization" is executed by the actor directly after creating, in case the actor will be restarted.
e.g.

protected override void PreRestart(Exception reason, object message)
        {
            Self.QueueInitialize(Stash, new Initialize(CustomerId));
            base.PreRestart(reason, message);
        }

public static void QueueInitialize(this IActorRef actorRef, IStash stash, object message)
        {
            stash.ReceiveNext(actorRef, message);
            stash.UnstashAll();
        }

As we use this pattern often in our code and in my opinion this is no unusual case I thought it would be nice if this would be better supported when using DI.
In my PR I showed how this could work in an DI provider agnostic way. The DI provider will only resolve the external dependencies and akka will insert the desired property values.

@Horusiath
Copy link
Contributor

Horusiath commented Sep 25, 2019

Maybe let me simply challenge the core concept: if you need to do fight so to make this work with dependency injection, what stops you from getting rid of dependency injection and just pass the necessary parameter explicitly?

Remember that actors are statefull, can live for long time. They are not equivalent of controllers and patterns used there are not always applicable in actor systems.

@Ralf1108
Copy link
Contributor Author

Of course I can pass all parameters explicit into the ctor but then I loose the nice benefit of DI but the idea was to support both.

So

  • if I dont use DI I have to resolve my dependencies myself and provide explicit all parameters.
  • If I use DI I loose the built in ability that akka handled all ctor parameters correctly via Props.

If it is not possible what I proposed we have to stick with the "Initialize" method approach as this would be the smaller workaround in my opinion.

@Aaronontheweb
Copy link
Member

Completed as part of Akka.NET v1.4.15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants