-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Abstraction of ServiceProvider, Improving Akka.DependencyInjection #4814
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
Merged
Aaronontheweb
merged 7 commits into
akkadotnet:dev
from
SamEmber:4813-abstraction-of-serviceprovider-in-a
May 26, 2021
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a5bfb93
Abstraction of ServiceProvider
SamEmber 7815c96
introduced non-breaking Akka.DependencyInjection API changes
Aaronontheweb 30dcffb
fixed unit tests / Props bug
Aaronontheweb 5cde0e2
fixed up DelegateInjectionSpecs
Aaronontheweb ea8a396
Merge branch 'dev' into 4813-abstraction-of-serviceprovider-in-a
Aaronontheweb 32152fc
Added type checking for `Props(Type type, params object[] args)`
Aaronontheweb 902d528
fixed non-generic `Props()` method
Aaronontheweb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
src/contrib/dependencyinjection/Akka.DependencyInjection/DependencyResolver.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="ServiceProvider.cs" company="Akka.NET Project"> | ||
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net> | ||
// </copyright> | ||
//----------------------------------------------------------------------- | ||
|
||
using System; | ||
using Akka.Actor; | ||
using Akka.Configuration; | ||
using Akka.Event; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Akka.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Provides users with immediate access to the <see cref="IDependencyResolver"/> bound to | ||
/// this <see cref="ActorSystem"/>, if any. | ||
/// </summary> | ||
public sealed class DependencyResolver : IExtension | ||
{ | ||
public DependencyResolver(IDependencyResolver resolver) | ||
{ | ||
Resolver = resolver; | ||
} | ||
|
||
/// <summary> | ||
/// The globally scoped <see cref="IDependencyResolver"/>. | ||
/// </summary> | ||
/// <remarks> | ||
/// Per https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines - please use | ||
/// the appropriate <see cref="IServiceScope"/> for your actors and the dependencies they consume. DI is typically | ||
/// not used for long-lived, stateful objects such as actors. | ||
/// | ||
/// Therefore, injecting transient dependencies via constructors is a bad idea in most cases. You'd be far better off | ||
/// creating a local "request scope" each time your actor processes a message that depends on a transient dependency, | ||
/// such as a database connection, and disposing that scope once the operation is complete. | ||
/// | ||
/// Actors are not MVC Controllers. Actors can live forever, have the ability to restart, and are often stateful. | ||
/// Be mindful of this as you use this feature or bad things will happen. Akka.NET does not magically manage scopes | ||
/// for you. | ||
/// </remarks> | ||
public IDependencyResolver Resolver { get; } | ||
|
||
public static DependencyResolver For(ActorSystem actorSystem) | ||
{ | ||
return actorSystem.WithExtension<DependencyResolver, DependencyResolverExtension>(); | ||
} | ||
|
||
/// <summary> | ||
/// Uses a delegate to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection | ||
/// and others are not. | ||
/// </summary> | ||
/// <remarks> | ||
/// YOU ARE RESPONSIBLE FOR MANAGING THE LIFECYCLE OF YOUR OWN DEPENDENCIES. AKKA.NET WILL NOT ATTEMPT TO DO IT FOR YOU. | ||
/// </remarks> | ||
/// <typeparam name="T">The type of actor to instantiate.</typeparam> | ||
/// <param name="args">Optional. Any constructor arguments that will be passed into the actor's constructor directly without being resolved by DI first.</param> | ||
/// <returns>A new <see cref="Akka.Actor.Props"/> instance which uses DI internally.</returns> | ||
public Props Props<T>(params object[] args) where T : ActorBase | ||
{ | ||
return Resolver.Props<T>(args); | ||
} | ||
|
||
/// <summary> | ||
/// Used to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection | ||
/// and others are not. | ||
/// </summary> | ||
/// <remarks> | ||
/// YOU ARE RESPONSIBLE FOR MANAGING THE LIFECYCLE OF YOUR OWN DEPENDENCIES. AKKA.NET WILL NOT ATTEMPT TO DO IT FOR YOU. | ||
/// </remarks> | ||
/// <typeparam name="T">The type of actor to instantiate.</typeparam> | ||
/// <returns>A new <see cref="Akka.Actor.Props"/> instance which uses DI internally.</returns> | ||
public Props Props<T>() where T : ActorBase | ||
{ | ||
return Resolver.Props<T>(); | ||
} | ||
|
||
/// <summary> | ||
/// Used to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection | ||
/// and others are not. | ||
/// </summary> | ||
/// <remarks> | ||
/// YOU ARE RESPONSIBLE FOR MANAGING THE LIFECYCLE OF YOUR OWN DEPENDENCIES. AKKA.NET WILL NOT ATTEMPT TO DO IT FOR YOU. | ||
/// </remarks> | ||
/// <param name="type">The type of actor to instantiate.</param> | ||
/// <returns>A new <see cref="Akka.Actor.Props"/> instance which uses DI internally.</returns> | ||
public Props Props(Type type) | ||
{ | ||
return Resolver.Props(type); | ||
} | ||
|
||
/// <summary> | ||
/// Used to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection | ||
/// and others are not. | ||
/// </summary> | ||
/// <remarks> | ||
/// YOU ARE RESPONSIBLE FOR MANAGING THE LIFECYCLE OF YOUR OWN DEPENDENCIES. AKKA.NET WILL NOT ATTEMPT TO DO IT FOR YOU. | ||
/// </remarks> | ||
/// <param name="type">The type of actor to instantiate.</param> | ||
/// <param name="args">Optional. Any constructor arguments that will be passed into the actor's constructor directly without being resolved by DI first.</param> | ||
/// <returns>A new <see cref="Akka.Actor.Props"/> instance which uses DI internally.</returns> | ||
public Props Props(Type type, params object[] args) | ||
{ | ||
return Resolver.Props(type, args); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// INTERNAL API | ||
/// </summary> | ||
public sealed class DependencyResolverExtension : ExtensionIdProvider<DependencyResolver> | ||
{ | ||
public override DependencyResolver CreateExtension(ExtendedActorSystem system) | ||
{ | ||
var setup = system.Settings.Setup.Get<DependencyResolverSetup>(); | ||
if (setup.HasValue) return new DependencyResolver(setup.Value.DependencyResolver); | ||
|
||
var exception = new ConfigurationException("Unable to find [DependencyResolverSetup] included in ActorSystem settings." + | ||
" Please specify one before attempting to use dependency injection inside Akka.NET."); | ||
system.EventStream.Publish(new Error(exception, "Akka.DependencyInjection", typeof(DependencyResolverExtension), exception.Message)); | ||
throw exception; | ||
} | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/contrib/dependencyinjection/Akka.DependencyInjection/DependencyResolverSetup.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="ServiceProviderSetup.cs" company="Akka.NET Project"> | ||
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net> | ||
// </copyright> | ||
//----------------------------------------------------------------------- | ||
|
||
using System; | ||
using Akka.Actor; | ||
using Akka.Actor.Setup; | ||
|
||
namespace Akka.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Used to help bootstrap an <see cref="ActorSystem"/> with dependency injection (DI) | ||
/// support via a <see cref="IServiceProvider"/> reference. | ||
/// | ||
/// The <see cref="IServiceProvider"/> will be used to access previously registered services | ||
/// in the creation of actors and other pieces of infrastructure inside Akka.NET. | ||
/// | ||
/// The constructor is internal. Please use <see cref="Create"/> to create a new instance. | ||
/// </summary> | ||
[Obsolete("Used DependencyResolverSetup instead.")] | ||
public class ServiceProviderSetup : Setup | ||
{ | ||
internal ServiceProviderSetup(IServiceProvider serviceProvider) | ||
{ | ||
ServiceProvider = serviceProvider; | ||
} | ||
|
||
public IServiceProvider ServiceProvider { get; } | ||
|
||
public static ServiceProviderSetup Create(IServiceProvider provider) | ||
{ | ||
if (provider == null) | ||
throw new ArgumentNullException(nameof(provider)); | ||
|
||
return new ServiceProviderSetup(provider); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Used to help bootstrap an <see cref="ActorSystem"/> with dependency injection (DI) | ||
/// support via a <see cref="IDependencyResolver"/> reference. | ||
/// | ||
/// The <see cref="IDependencyResolver"/> will be used to access previously registered services | ||
/// in the creation of actors and other pieces of infrastructure inside Akka.NET. | ||
/// | ||
/// The constructor is internal. Please use <see cref="Create"/> to create a new instance. | ||
/// </summary> | ||
public class DependencyResolverSetup : Setup | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be far better if we kept the |
||
{ | ||
public IDependencyResolver DependencyResolver { get; } | ||
|
||
internal DependencyResolverSetup(IDependencyResolver dependencyResolver) | ||
{ | ||
DependencyResolver = dependencyResolver; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new instance of DependencyResolverSetup, passing in <see cref="IServiceProvider"/> | ||
/// here creates an <see cref="IDependencyResolver"/> that resolves dependencies from the specified <see cref="IServiceProvider"/> | ||
/// </summary> | ||
public static DependencyResolverSetup Create(IServiceProvider provider) | ||
{ | ||
if (provider == null) | ||
throw new ArgumentNullException(nameof(provider)); | ||
|
||
return new DependencyResolverSetup(new ServiceProviderDependencyResolver(provider)); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new instance of DependencyResolverSetup, an implementation of <see cref="IDependencyResolver"/> | ||
/// can be passed in here to resolve services from test or alternative DI frameworks. | ||
/// </summary> | ||
public static DependencyResolverSetup Create(IDependencyResolver provider) | ||
{ | ||
if (provider == null) | ||
throw new ArgumentNullException(nameof(provider)); | ||
|
||
return new DependencyResolverSetup(provider); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably shouldn't rename the extension here, for API compatibility reasons