asynkron / protoactor-dotnet

Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
http://proto.actor
Apache License 2.0
1.73k stars 288 forks source link

Context Decorators #409

Closed rogeralsing closed 6 years ago

rogeralsing commented 6 years ago

In an effort to intercept calls to methods of the different contexts. We need a decent way to decorate the context instances.

What I mean here is:

IContext.RequestAsyncMyDecoratorContext.RequestAsyncActorContext.RequestAsync

And something similar for RootContext.

First of all, RootContext is currently just created with new, for this to work properly, we need some factory for root contexts.

e.g. RootContext.Create() // returns an IRootContext with potential decorators wrapped

in the case of actors, it's easier. we can simply have a WithContextDecorator on Props Then when the ActorContext invokes ReceiveAsync on the actor, we just pass the wrapper context.

Thoughts ?

rogeralsing commented 6 years ago

Relates to #406

jrouaix commented 6 years ago

I tried to used something like that but ... Proto is not ready ... and it's not 4.5.2 compatible.

  class OpenTracingDecorator<T> : DispatchProxy
    {
        private OpenTracingDecorator() { }

        T _context;

        public static T Create<T>(T context, SpanSetup sendSpanSetup, ITracer tracer)
        {
            var proxy = Create<T, OpenTracingDecorator<T>>();
            var t = proxy as OpenTracingDecorator<T>;
            t._context = context;
            return proxy;
        }

        protected override object Invoke(MethodInfo targetMethod, object[] args)
        {
            if (targetMethod.Name == nameof(ISenderContext.RequestAsync))
                throw new InvalidCastException("Just to know I passed here");

            return targetMethod.Invoke(_context, args);
        }
    }

As our need is not that general, we could implement some "ContextBuilder" that will be able to add middleware/aspect for each methods :

props.WithContextDecorator(ctxBuilder => {
  ctxBuilder.WithRequestAsync(async (next, target, message) => {
    // do something before
    var result =   await next(target, message);
    // do something after
  };
});
rogeralsing commented 6 years ago

I'm thinking if we just implement a base class for decorators, that manually forwards each call. no magic, just code.

Then the implementor of a decorator could just override the method they want to intercept

rogeralsing commented 6 years ago

I've pushed this: https://github.com/AsynkronIT/protoactor-dotnet/blob/dev/src/Proto.Actor/ActorContextDecorator.cs

Inherit and override the request methods. that should at least technically work. Then we just need to make it all happen in the ActorContext so that the decorator is the one passed to the middlewares and the actor

rogeralsing commented 6 years ago

I'm closing this one as it is done, we could open new issues if there are bugs or API design ideas