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

[WIP] Native dependency injection #3057

Closed

Conversation

Horusiath
Copy link
Contributor

As proposed in #3050, this is a reference work-in-progress example of how we could replace existing actor creation pipeline and expose it to provide native support for dependency injection. I've used Microsoft.Extensions.DependencyInjection here, the same lib which is used in ASP.NET Core - this also means, that we can reuse DI mechanics between those two.

In general, there are multitude of places, where DI could be used - potentially almost every place where Activator.CreateInstance is used right now, except hot paths. There should be a way to create an actor in two ways, both fast using explicit construction and with dependency injection. Here's how:

  • Since Props.Create(() => new Actor(args)) is explicitly describing how actor should be created, there's no need for DI here. This is a fast lane.
  • However stuff like Props.Create<Actor>() doesn't provide any details about construction mechanics, therefore it could be injected.

I was thinking about the place where custom user services could be registered, since we don't have equivalent of Startup class that could be used to configure services from code. However my idea was to add extra method to an void IExtension.ConfigureServices(IServiceCollection) - this way when someone would get an extension like Cluster, Persistence etc. it would register all necessary dependencies inside. IExtensions themselves could be resolved using DI - this would simplify API of this interface (no longer need for ExtensionIdProvider<> or IExtension.Get/Apply etc. methods. It also encourages users to define their own business rules as akka extensions, which is actually a good pattern. I'm not sure if this library API will allow us to do so, but I'm pretty optimistic here.

Microsoft.Extensions.DependencyInjection introduces 3 lifetime scopes:

  • Singleton - in our case ActorSystem.
  • Transient - a particular incarnation of an actor, created each time an actor constructor is called.
  • Scoped - an IActorContext scope. Shared between incarnations as described bellow.

The extra stuff I added to an ActorCell itself is IScope interface. Motivation is here: while Props are working as a descriptors of how actors of particular type should be constructed, there is no primitive that would tell how to create an actor in the context of a particular actor context. This is where scope comes in.

While actors created with Props.Create(() => new Actor()) are defined explicitly, the scope for them is essentially the same thing as Props: we want to keep them fast. However actors created dynamically, potentially with help of dependency injection, have custom scopes assigned to an ActorCell itself. This means that ServiceLifetime.Scoped object lifetime is equal to that of actor cell, not an actor incarnation itself.

@Aaronontheweb
Copy link
Member

This will need to go into a feature branch, not dev

@Horusiath Horusiath force-pushed the native-dependency-injection branch from b4093ec to 1a6d606 Compare September 3, 2017 08:43
@Horusiath Horusiath force-pushed the native-dependency-injection branch from 878c287 to f6eea05 Compare September 7, 2017 15:01
@Horusiath Horusiath changed the base branch from dev to v1.5 September 7, 2017 15:02
@Horusiath Horusiath changed the base branch from v1.5 to dev September 7, 2017 15:03
@Horusiath
Copy link
Contributor Author

Missing parts:

  • Using DI for things like serializers and event adapters.
  • Separate tests for components injected with the new API.
  • Akka.NET extensions probably won't be DI-created at the moment. I've got some example in the side branch, but it breaks apart existing extensions API. I need to think about the way how we could use them there without destroying everything.
  • API to register custom user dependencies. Dependency injection is now possible, however service registration it's not exposed to user.

This last thing is the most troublesome - from what I've understood Microsoft.Extensions.DependencyInjection has separate interfaces for registering services (IServiceCollection) and creating them (IServiceProvider) - latter must be created by providing the first one. This means that all dependencies must be registered while ActorSystem is being created. We don't give our users too many ways to do so. I can think about 2 solutions here:

  1. Give ExtensionIdProvider<> a virtual method like ConfigureServices(IServiceCollection) - akka allows to load any custom extension defined in HOCON via akka.extensions = [] at the start of actor system. Unfortunately, this is limited option as it won't work if extension was invoked after actor system had been created. It could cause unexpected behavior on the user side.
  2. Correlate this change with an ability to expose configuration via CLR object rather than HOCON string (as proposed in Should we adopt Microsoft.Extensions libraries? #3050) - this would allow us to pass an actual type safe configuration upon ActorSystem creation - this configuration object could expose method for registering dependencies by itself.

@stijnherreman
Copy link

@Horusiath how come this PR was abandoned? (I arrived here via #1675)

@Horusiath
Copy link
Contributor Author

@stijnherreman it's not abandoned - it just moved to #3862

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

Successfully merging this pull request may close these issues.

4 participants