Skip to content

Instantly share code, notes, and snippets.

@arkadiuszwojcik
Created December 11, 2019 20:38
Show Gist options
  • Save arkadiuszwojcik/6506f227c16d6613e7e0270493d6f7af to your computer and use it in GitHub Desktop.
Save arkadiuszwojcik/6506f227c16d6613e7e0270493d6f7af to your computer and use it in GitHub Desktop.
Scoped dependency injection in Proto Actor
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);
}
}
[AttributeUsage(AttributeTargets.Class)]
public class ActorScopeAttribute : Attribute
{
public ActorScopeAttribute(Type installerType)
{
InstallerType = installerType;
}
public Type InstallerType { get; }
}
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;
}
}
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();
}
}
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);
}
}
@MiloszKrajewski
Copy link

Thanks (and sorry for late answer)

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