-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Abstraction of ServiceProvider, Improving Akka.DependencyInjection (#…
…4814) * Abstraction of ServiceProvider * introduced non-breaking Akka.DependencyInjection API changes * fixed unit tests / Props bug * fixed up DelegateInjectionSpecs * Added type checking for `Props(Type type, params object[] args)` * fixed non-generic `Props()` method Co-authored-by: Aaron Stannard <aaron@petabridge.com>
- Loading branch information
1 parent
99afc0e
commit c663bc2
Showing
11 changed files
with
428 additions
and
67 deletions.
There are no files selected for viewing
This file contains 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 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 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 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 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 | ||
{ | ||
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.