1

I'm trying to setup a dependency which I want to be injected in the resolution scope of a base interface (of MediatR handlers):

container.Register<DbContext, Model1>(reuse: Reuse.InResolutionScopeOf(typeof(IAsyncRequestHandler<,>)));

However, this interface is being setup with a few decorators, which have a dependency on a IActionHandler which, in turn, depends on the DbContext:

public class Decorator<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse>
{
    public Decorator(IActionHandler handler, IAsyncRequestHandler<TRequest, TResponse> inner);
}

I'm getting an exception trying to resolve the implementation of that IActionHandler, because it can't inject the DbContext, as it doesn't seem to be available in the scope.

I've tried to set the IActionHandler to be the target of InResolutionScopeOf, but then the DbContext can't be resolved in my IAsyncRequestHandler<,>.

I need for each DbContext instance to be available across any Decorators or IActionHandler originated from the resolution of a IAsyncRequestHandler<,>, and that instance should also be injected in the IAsyncRequestHandler<,> implementation.

Any ideas as to how I can achieve this type of injection?

Thanks

djed
  • 141
  • 10
  • What I'm trying to achieve is basically setting up a cross cutting concern using a decorator for EF DbContexts, which i want to be available across any dependencies of a MediatR request. After the action is executed i want to be able to save the changes in the decorator. However, i want this cross cutting logic to be abstracted away from the MediatR decorator, so it can be reused in other types of AOP, so that's where the IActionHandler steps in. Perhaps I'm taking an entirely wrong approach towards this, so I'll also take any advice as to how I can model this properly. – djed Oct 25 '16 at 15:46
  • That's kind of chicken and egg problem. I will try to play with this setup Tomorrow and update you. – dadhi Oct 26 '16 at 20:18
  • Thanks @dadhi, I understand it's kind of tricky. – djed Oct 27 '16 at 10:03

1 Answers1

1

Update:

The fix to enable this code is available since DryIoc 2.8.4

Old answer:

DryIoc as of latest version 2.8.3 does not support specifying the resolution scope reuse with open-generic type. Like so Reuse.InResolutionScopeOf(typeof(IAsyncRequestHandler<,>).

Specifying as concrete closed type works fine. Check the sample below (live):

using System;
using DryIoc;

public class Program
{
    public static void Main()
    {
        var c = new Container();


        c.Register<IActionHandler, SomeActionHandler>();

        c.Register<IAsyncRequestHandler<string, string>, SomeRequestHandler>();

        // works with closed-generic spec.
        c.Register<DbContext, Model1>(reuse: Reuse.InResolutionScopeOf(typeof(IAsyncRequestHandler<string, string>)));

        // Error: not working with open-generic type in reuse spec
        // c.Register<DbContext, Model1>(reuse: Reuse.InResolutionScopeOf(typeof(IAsyncRequestHandler<,>)));

        c.Register(typeof(IAsyncRequestHandler<,>), typeof(Decorator<,>), setup: Setup.Decorator);

        var result = c.Resolve<IAsyncRequestHandler<string, string>>();

        Console.WriteLine("decorator: " + result);
        Console.WriteLine("decorator.DbContext is the same as action handler's: " + 
                          (result.DbContext == ((Decorator<string, string>)result).ActionHandler.DbContext));
    }

    public interface IAsyncRequestHandler<TRequest, TResponse> 
    {
        DbContext DbContext { get; }
    }

    public interface IActionHandler 
    {
        DbContext DbContext { get; }
    }

    public class DbContext {}

    public class Model1 : DbContext {}

    public class Decorator<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse>
    {
        public DbContext DbContext { get { return _decorated.DbContext; } }

        IAsyncRequestHandler<TRequest, TResponse> _decorated;

        public readonly IActionHandler ActionHandler;

        public Decorator(IActionHandler handler, IAsyncRequestHandler<TRequest, TResponse> inner) 
        {
            ActionHandler = handler;
            _decorated = inner;
        }
    }

    public class SomeRequestHandler : IAsyncRequestHandler<string, string> 
    {
        public DbContext DbContext { get; private set; }

        public SomeRequestHandler(DbContext dbContext) 
        {
            DbContext = dbContext;
        }
    }

    public class SomeActionHandler : IActionHandler 
    {
        public DbContext DbContext { get; private set; }

        public SomeActionHandler(DbContext context) 
        {
            DbContext = context;
        }
    }
}

I have created an issue to add the support into the next version.

As alternative, you may use the key without type as following:

container.Register<DbContext, Model1>(reuse: Reuse.InResolutionScopeOf(serviceKey: blah));

But then you need to register you IAsyncRequestHandler with the key as well.

dadhi
  • 4,807
  • 19
  • 25
  • I've updated to 2.8.5. and the injection works correctly for the decorator, however it does not work for the decorator's dependencies. In the example of the question, the DbContext is injected in IActionHandler, not the decorator itself, and after the update, it's still throwing a DryIoc.ContainerException. Is this an expected behaviour? – djed Nov 02 '16 at 15:12
  • Can you modify my sample above with reproducible behavior and give me a link, or create an issue in bitbucket? – dadhi Nov 02 '16 at 15:48
  • I have updated the live code with dependency checking and updated to 2.8.5. Everything works as I would expect. Please check: https://dotnetfiddle.net/miTXLm – dadhi Nov 04 '16 at 07:17
  • I'll check this out as soon as I get the chance, I've been rather busy lately. Thank you very much for your help – djed Nov 04 '16 at 12:16
  • Sorry for the delay, @dadhi, I think the problem is that I'm overriding the IActionHandler arguments of the Decorator, the setup is not quite the same as yours. I've created the fiddle with my setup, and as you can see it throws an exception. https://dotnetfiddle.net/HViFi6 – djed Nov 14 '16 at 14:55
  • Here is the corrected version of your fiddle: https://dotnetfiddle.net/EMNGY5 I've just specified required service type instead of custom parameter value. The later designed for external (created outside of container) value injection, and does not play well with decorators and wrappers. And you don't need it in your case. – dadhi Nov 14 '16 at 21:33