Created
December 11, 2019 20:38
-
-
Save arkadiuszwojcik/6506f227c16d6613e7e0270493d6f7af to your computer and use it in GitHub Desktop.
Scoped dependency injection in Proto Actor
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
public class ActorContainerNext : WindsorContainer | |
{ | |
private readonly ConcurrentDictionary<Type, PID> singletons = new ConcurrentDictionary<Type, PID>(); | |
public ActorContainerNext(IWindsorContainer parent = null) | |
{ | |
Parent = parent; | |
Register(ContainerComponents().ToArray()); | |
} | |
public PID CreateActor<TActor>(ISpawnContext context, string name) where TActor : IActor | |
{ | |
var handlers = Kernel.GetAssignableHandlers(typeof(TActor)); | |
// if no handler, allow for windsor resolve exception | |
if (handlers.Length == 0) CreateActorFactory(typeof(TActor)); | |
if (TryFindSingleton<TActor>(out PID value)) | |
{ | |
return value; | |
} | |
PID actor; | |
var props = Props | |
.FromProducer(() => CreateActorFactory(typeof(TActor))) | |
.WithDispatcher(CustomDispatchers.ThreadPoolDispatcher); | |
// Is scope actor? | |
var scopeAttribute = (ActorScopeAttribute)Attribute.GetCustomAttribute(typeof(TActor), typeof(ActorScopeAttribute)); | |
if (scopeAttribute != null) | |
{ | |
props = props | |
.WithContextDecorator(ctx => new ActorScopeContext(ctx, this, scopeAttribute.InstallerType)) | |
.WithReceiveMiddleware(ActorScopeMiddleware.Middleware()); | |
} | |
if (string.IsNullOrEmpty(name)) | |
actor = context.SpawnPrefix(props, typeof(TActor).Name); | |
else | |
actor = context.SpawnNamed(props, name); | |
var lifeType = handlers[0].ComponentModel.LifestyleType; | |
if (lifeType == LifestyleType.Undefined || lifeType == LifestyleType.Singleton) | |
singletons.TryAdd(typeof(TActor), actor); | |
return actor; | |
} | |
public override void Dispose() | |
{ | |
singletons.Clear(); | |
base.Dispose(); | |
} | |
protected bool TryFindSingleton<TActorClass>(out PID pid) | |
{ | |
if (singletons.TryGetValue(typeof(TActorClass), out pid)) | |
{ | |
return true; | |
} | |
if (Parent is ActorContainerNext parentActorContainer) | |
{ | |
return parentActorContainer.TryFindSingleton<TActorClass>(out pid); | |
} | |
return false; | |
} | |
private IEnumerable<IRegistration> ContainerComponents() | |
{ | |
yield return Component.For(typeof(IKnowActor<>)).ImplementedBy(typeof(KnowActor<>)).LifestyleTransient(); | |
yield return Component.For(typeof(ActorContainer)).Instance(this); | |
} | |
private IActor CreateActorFactory(Type actorType) | |
{ | |
return (IActor)Resolve(actorType); | |
} | |
} |
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
[AttributeUsage(AttributeTargets.Class)] | |
public class ActorScopeAttribute : Attribute | |
{ | |
public ActorScopeAttribute(Type installerType) | |
{ | |
InstallerType = installerType; | |
} | |
public Type InstallerType { get; } | |
} |
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
class ActorScopeContext : ActorContextDecorator | |
{ | |
private readonly IWindsorContainer parentContainer; | |
private readonly Type windsorInstallerClass; | |
private ActorContainerNext container = null; | |
public ActorScopeContext(IContext context, IWindsorContainer parent, Type windsorInstallerClass) : base(context) | |
{ | |
this.parentContainer = parent; | |
this.windsorInstallerClass = windsorInstallerClass; | |
} | |
public PID Spawn<TActor>(string actorName = null) where TActor : IActor | |
{ | |
return container.CreateActor<TActor>(this, actorName); | |
} | |
internal void CreateContainer() | |
{ | |
container = new ActorContainerNext(parentContainer); | |
container.Install(windsorInstallerClass.CreateInstance<IWindsorInstaller>()); | |
} | |
internal void DisposeContainer() | |
{ | |
container?.Dispose(); | |
container = null; | |
} | |
} |
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
class ActorScopeMiddleware | |
{ | |
public static Func<Receiver, Receiver> Middleware() | |
{ | |
return next => async (context, envelope) => | |
{ | |
var actorContext = context as ActorScopeContext; | |
if (actorContext == null) | |
{ | |
await next(context, envelope); | |
return; | |
} | |
switch (envelope.Message) | |
{ | |
case Started _: | |
ActorStarted(actorContext); | |
await next(context, envelope); | |
break; | |
case Stopped _: | |
try | |
{ | |
await next(context, envelope); | |
} | |
finally | |
{ | |
ActorStopped(actorContext); | |
} | |
break; | |
default: | |
await next(context, envelope); | |
break; | |
} | |
}; | |
} | |
public static void ActorStarted(ActorScopeContext actorScopeContext) | |
{ | |
actorScopeContext.CreateContainer(); | |
} | |
public static void ActorStopped(ActorScopeContext actorScopeContext) | |
{ | |
actorScopeContext.DisposeContainer(); | |
} | |
} |
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
public class SystemRootContext : RootContextDecorator | |
{ | |
private readonly ActorContainerNext container; | |
public SystemRootContext(IRootContext context, ActorContainerNext rootActorContainer) : base(context) | |
{ | |
container = rootActorContainer; | |
} | |
public PID Spawn<TActor>(string actorName = null) where TActor : IActor | |
{ | |
return container.CreateActor<TActor>(this, actorName); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks (and sorry for late answer)