Using Unity IoC to register and resolve SignalR hu

2019-02-13 07:59发布

问题:

I think I'm missing something very simple and maybe just need a new set of eyes. I have an ASP.NET MVC application. In that app, I am using Unity for my IoC to handle dependency injection. Each of my repositories need to have a database factory injected into it and each database factory needs to have a principal injected into it. So far, I've been utilizing the PerRequestLifetimeManager to register these.

//Repositories

container.RegisterType<ChatMessageRepository>(new PerRequestLifetimeManager());
           container.RegisterType<SignalRConnectionRepository>(new PerRequestLifetimeManager());

//Context
container.RegisterType<IPrincipal, Principal>(new PerRequestLifetimeManager());
container.RegisterType<IDatabaseFactory, DatabaseFactory>(new PerRequestLifetimeManager());
container.RegisterType<UnitOfWork>(new PerRequestLifetimeManager());

Logically, I've tried to register my Hub in the same fashion.

container.RegisterType<ChatHub>(new PerRequestLifetimeManager()); 

However, whenever I run my app and navigate away from my chat page, I get a "Resolution of the dependency failed" exception and the InnerException tells me "Operation is not valid due to the current state of the object." I've also tried using the default (Transient), PerResolve, and ContainerControlled lifetime Unity managers when registering these guys and cannot seem to get resolve my issue.

Could someone just provide me some demo code with how you used Unity in an ASP.NET MVC application to handle dependency injection into your signalr hubs?

Here's where Unity will inject parameters into my SignalR Hub

public class ChatHub : Hub
{
    private readonly ChatMessageRepository _chatMessageRepository;
    private readonly SignalRConnectionRepository _signalRConnectionRepository;
    private readonly UnitOfWork _unitOfWork;

    public ChatHub(ChatMessageRepository chatMessageRepository,
        SignalRConnectionRepository signalRConnectionRepository,
        UnitOfWork unitOfWork)
    {
        _chatMessageRepository = chatMessageRepository;
        _signalRConnectionRepository = signalRConnectionRepository;
        _unitOfWork = unitOfWork;
    } ... }

Thanks!

回答1:

Do it in 3 steps

First. Create UnityHubActivator class

public class UnityHubActivator : IHubActivator
{
    private readonly IUnityContainer _container;

    public UnityHubActivator(IUnityContainer container)
    {
        _container = container;
    }

    public IHub Create(HubDescriptor descriptor)
    {
        return (IHub)_container.Resolve(descriptor.HubType);
    }
}

Second. Create Unity container and register your dependency resolver before run Startup class

unityContainer = new UnityContainer();
var unityHubActivator = new UnityHubActivator(_unityContainer);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => unityHubActivator);
//register some types in container
WebApp.Start<Startup>(startOptions);

Third. Use it in your Hub

public class MyHub : Hub
{
    public MyHub(Logger logger)
    {
        logger.Info("hub constructor");
    }
}

Note. I do not change anything in my Startup class



回答2:

There's a trick to do that. You will need to do something like this:

container.RegisterType< ChatHub >(new InjectionFactory(CreateChatHub));

......

and then create a private method CreateChatHub

private static object CreateChatHub(IUnityContainer container)
{
    return new ChatHub();
}


回答3:

1 Create "UnitySignalRDependencyResolver.cs"

public class UnitySignalRDependencyResolver : DefaultDependencyResolver
{
    protected IUnityContainer Container;
    private bool IsDisposed = false;

    public UnitySignalRDependencyResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        Container = container.CreateChildContainer();
    }

    /// <summary>
    /// Gets the Autofac implementation of the dependency resolver.
    /// </summary>
    public static UnitySignalRDependencyResolver Current
    {
        get { return GlobalHost.DependencyResolver as UnitySignalRDependencyResolver; }
    }


    public override object GetService(Type serviceType)
    {
        if (Container.IsRegistered(serviceType))
        {
            return Container.Resolve(serviceType);
        }

        return base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        if (Container.IsRegistered(serviceType))
        {
            return Container.ResolveAll(serviceType);
        }

        return base.GetServices(serviceType);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (IsDisposed)
        {
            return;
        }

        if (disposing)
        {
            Container.Dispose();
        }

        IsDisposed = true;
    }
}

2.Add your resolver to Owin pipeline

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
         // Get container
        IUnityContainer container = UnityConfig.Container;

        // Create resolver
        var resolver = new UnitySignalRDependencyResolver(container);

        // Create SignalR Configuration
        var config = new HubConfiguration
        {
            Resolver = resolver
        };
        // Start SignalR
        app.Map("/signalr", map =>
        {
            map.RunSignalR(config);
        });
    }
}

3.Inject your dependency in your controller's constructor

public class ValuesController : ApiController
{
    private readonly IMyDependency _myDependency;

    public ValuesController(IMyDependency myDependency)
    {
        _myDependency= myDependency;
    }
}