使用Autofac将SignalR IHubContext注入服务层 [英] Inject SignalR IHubContext into service layer with Autofac

查看:141
本文介绍了使用Autofac将SignalR IHubContext注入服务层的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在运行Framework 4.72(而不是.NET Core)的应用程序中,我试图将SignalR IHubContext注入Web API 2.x服务.我的解决方案分为三个项目,即Web,服务,数据.SignalR集线器位于Web层中.我有在服务层中运行的后台代码,完成后,我需要它通过集线器发送消息.后台任务不是由控制器启动的.

我的Global.asax非常标准:

 受保护的void Application_Start(){GlobalConfiguration.Configure(WebApiConfig.Register);//将JSON序列化器设置为使用camelCasevar json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();DIConfig.Setup();FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);var logConfigFilePath = Server.MapPath(〜/log4net.config");log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(logConfigFilePath));} 

我的DIConfig包含:

 内部静态void Setup(){var config = System.Web.Http.GlobalConfiguration.Configuration;var builder = new ContainerBuilder();builder.Register(c =>新的ShopAPDbContext()).AsImplementedInterfaces().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.RegisterType< ShopAPRepository>().As< IShopAPRepository>().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.RegisterType< ShopAPService>().As< IShopAPService>().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.AddAutoMapper(typeof(InvoiceMappingProfile).Assembly);builder.RegisterApiControllers(Assembly.GetExecutingAssembly());var container = builder.Build();DependencyResolver.SetResolver(new AutofacDependencyResolver(container));config.DependencyResolver =新的AutofacWebApiDependencyResolver(container);Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);} 

还有我的Startup.cs:

 公共类启动{公共无效配置(IAppBuilder应用){var container = DependencyConfiguration.Configure(app);SignalRConfiguration.Configure(app,container);HangFireDashboardConfig.Configure(app);}}公共静态类DependencyConfiguration{公共静态IContainer Configure(IAppBuilder应用){var builder = new ContainerBuilder();builder.RegisterHubs(typeof(SignalRConfiguration).Assembly);var container = builder.Build();app.UseAutofacMiddleware(container);返回容器;}}公共静态类SignalRConfiguration{公共静态无效的Configure(IAppBuilder应用程序,IContainer容器){HubConfiguration config = new HubConfiguration();config.Resolver =新的AutofacDependencyResolver(container);app.Map("/messages",map =>{map.UseCors(CorsOptions.AllowAll);var hubConfiguration =新的HubConfiguration{EnableDetailedErrors = true,EnableJavaScriptProxies =否};map.RunSignalR(hubConfiguration);});}} 

我的服务层的构造函数如下:

  public ShopAPService(){_shopAPRepository = new ShopAPRepository();_mapper =新的Mapper((IConfigurationProvider)typeof(InvoiceMappingProfile).Assembly);_hubContext = null;//这里是什么?}公共ShopAPService(IShopAPRepository shopAPRepository,IMapper映射器,IHubContext hubContext){_shopAPRepository = shopAPRepository;_mapper =映射器;_hubContext = hubContext;} 

我知道我需要将IHubContext的实例传递到服务中,但是到目前为止,我还没有成功.据我所知,第一个构造函数是从控制器以外的任何其他函数调用服务时使用的是什么?


第一次修订

好的,我知道所有内容都应该放在一个容器中.基于反馈并查看这些链接,我创建了一个容器并将其传递.这是我修改后的启动程序:

 公共类启动{公共无效配置(IAppBuilder应用){//var config = System.Web.Http.GlobalConfiguration.Configuration;var container = GetDependencyContainer();RegisterWebApi(app,container);RegisterSignalR(app,container);GlobalHost.DependencyResolver =新的AutofacDependencyResolver(container);HubConfiguration config = new HubConfiguration();config.Resolver =新的AutofacDependencyResolver(container);//config.DependencyResolver = new AutofacWebApiDependencyResolver(container);Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);HangFireDashboardConfig.Configure(app);}私有IContainer GetDependencyContainer(){返回AutofacConfig.RegisterModules();}私有void RegisterWebApi(IAppBuilder应用程序,IContainer容器){var configuration = new HttpConfiguration{DependencyResolver =新的AutofacWebApiDependencyResolver(容器)};WebApiConfig.Register(配置);app.UseAutofacMiddleware(container);app.UseAutofacWebApi(con​​figuration);app.UseWebApi(con​​figuration);}私有无效RegisterSignalR(IAppBuilder应用程序,IContainer容器){var configuration = new HubConfiguration{解析器=新的AutofacDependencyResolver(容器)};app.MapSignalR(configuration);}} 

然后我的AutofacConfig会构建容器并进行大多数注册:

 内部类AutofacConfig{公共静态IContainer RegisterModules(){var builder = new ContainerBuilder();builder.Register(c =>新的ShopAPDbContext()).AsImplementedInterfaces().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.RegisterType< ShopAPRepository>().As< IShopAPRepository>().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.RegisterType< ShopAPService>().As< IShopAPService>().InstancePerBackgroundJob().InstancePerLifetimeScope();builder.RegisterAutoMapper(typeof(InvoiceMappingProfile).Assembly);builder.RegisterApiControllers(Assembly.GetExecutingAssembly());//将Autofac解析器注册到容器中,稍后再设置为HubConfigurationbuilder.RegisterType< AutofacDependencyResolver>().As< IDependencyResolver>().SingleInstance();//将ConnectionManager注册为IConnectionManager,以便您可以通过将IConnectionManager注入到服务中来获取集线器上下文builder.RegisterType< ConnectionManager>().As< IConnectionManager>().SingleInstance();builder.RegisterHubs(Assembly.GetExecutingAssembly());var container = builder.Build();返回容器;}} 

但是,我仍然无法在Service中获得对集线器的引用(该集线器与API在一个单独的项目中,但在相同的解决方案中.

我的Service方法是从HangFire调用的,并且没有对Hub的引用.如何在我的无参数构造函数中引用它?

 公共局部类ShopAPService:IShopAPService{私有静态只读ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);只读IShopAPRepository _shopAPRepository;只读IMapper _mapper;私有IHubContext _hubContext;公共ShopAPService(){_shopAPRepository = new ShopAPRepository();_mapper =新的Mapper((IConfigurationProvider)typeof(InvoiceMappingProfile).Assembly);_hubContext = connectionManager.GetHubContext< MessageHub>();}公共ShopAPService(IShopAPRepository shopAPRepository,IMapper映射器,IHubContext hubContext){_shopAPRepository = shopAPRepository;_mapper =映射器;_hubContext = hubContext;}} 

解决方案

您不能直接解析 IHubContext .但是您可以解决此接口的一般实现.

In an app running Framework 4.72, not .NET Core, I'm trying to inject a SignalR IHubContext into a Web API 2.x service. I have my solution broken into three projects, web, service, data. The SignalR hub is in the web layer. I have background code that runs in the service layer and when complete I need it to send a mesage via the hub. This background task is not initiated by the controller.

My Global.asax is pretty standard:

 protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);

    // Set JSON serializer to use camelCase
    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

    DIConfig.Setup();

    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

    var logConfigFilePath = Server.MapPath("~/log4net.config");
    log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(logConfigFilePath));
}

My DIConfig contains:

internal static void Setup()
{
    var config = System.Web.Http.GlobalConfiguration.Configuration;
    var builder = new ContainerBuilder();

    builder.Register(c => new ShopAPDbContext()).AsImplementedInterfaces().InstancePerBackgroundJob().InstancePerLifetimeScope();
    builder.RegisterType<ShopAPRepository>().As<IShopAPRepository>().InstancePerBackgroundJob().InstancePerLifetimeScope();
    builder.RegisterType<ShopAPService>().As<IShopAPService>().InstancePerBackgroundJob().InstancePerLifetimeScope();

    builder.AddAutoMapper(typeof(InvoiceMappingProfile).Assembly);
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    var container = builder.Build();

    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

    Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);
}

And my Startup.cs:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var container = DependencyConfiguration.Configure(app);
        SignalRConfiguration.Configure(app, container);
        HangFireDashboardConfig.Configure(app);
    }
}

public static class DependencyConfiguration
{
    public static IContainer Configure(IAppBuilder app)
    {
        var builder = new ContainerBuilder();
        builder.RegisterHubs(typeof(SignalRConfiguration).Assembly);
        var container = builder.Build();
        app.UseAutofacMiddleware(container);
        return container;
    }
}

public static class SignalRConfiguration
{
    public static void Configure(IAppBuilder app, IContainer container)
    {
        HubConfiguration config = new HubConfiguration();
        config.Resolver = new AutofacDependencyResolver(container);

        app.Map("/messages", map =>
        {
            map.UseCors(CorsOptions.AllowAll);
            var hubConfiguration = new HubConfiguration
            {
                EnableDetailedErrors = true,
                EnableJavaScriptProxies = false
            };
            map.RunSignalR(hubConfiguration);
        });
    }
}

The constructors of my service layer look like this:

public ShopAPService()
{
    _shopAPRepository = new ShopAPRepository();
    _mapper = new Mapper((IConfigurationProvider)typeof(InvoiceMappingProfile).Assembly);
    _hubContext = null;  // what here?
}

public ShopAPService(IShopAPRepository shopAPRepository, IMapper mapper, IHubContext hubContext)
{
    _shopAPRepository = shopAPRepository;
    _mapper = mapper;
    _hubContext = hubContext;
}

I know I need to pass an instance of the IHubContext into the service but so far I haven't been succesful. As I know, the first constructor is what is used when the service is called from anything other than the controller?


FIRST REVISION

Ok, I understand that everything should go into a single container. Based on feedback and looking at those links, I create a container and pass that along. Here is my revised Startup:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        //var config = System.Web.Http.GlobalConfiguration.Configuration;
        var container = GetDependencyContainer();

        RegisterWebApi(app, container);
        RegisterSignalR(app, container);

        GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
        HubConfiguration config = new HubConfiguration();
        config.Resolver = new AutofacDependencyResolver(container);

        //config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);

        HangFireDashboardConfig.Configure(app);
    }

    private IContainer GetDependencyContainer()
    {
        return AutofacConfig.RegisterModules();
    }

    private void RegisterWebApi(IAppBuilder app, IContainer container)
    {
        var configuration = new HttpConfiguration
        {
            DependencyResolver = new AutofacWebApiDependencyResolver(container)
        };

        WebApiConfig.Register(configuration);

        app.UseAutofacMiddleware(container);
        app.UseAutofacWebApi(configuration);
        app.UseWebApi(configuration);
    }

    private void RegisterSignalR(IAppBuilder app, IContainer container)
    {
        var configuration = new HubConfiguration
        {
            Resolver = new AutofacDependencyResolver(container)
        };

        app.MapSignalR(configuration);
    }
}

And my AutofacConfig builds the container and does most of the registering:

internal class AutofacConfig
{
    public static IContainer RegisterModules()
    {
        var builder = new ContainerBuilder();

        builder.Register(c => new ShopAPDbContext()).AsImplementedInterfaces().InstancePerBackgroundJob().InstancePerLifetimeScope();
        builder.RegisterType<ShopAPRepository>().As<IShopAPRepository>().InstancePerBackgroundJob().InstancePerLifetimeScope();
        builder.RegisterType<ShopAPService>().As<IShopAPService>().InstancePerBackgroundJob().InstancePerLifetimeScope();

        builder.RegisterAutoMapper(typeof(InvoiceMappingProfile).Assembly);
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // Register Autofac resolver into container to be set into HubConfiguration later
        builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();

        // Register ConnectionManager as IConnectionManager so that you can get hub context via IConnectionManager injected to your service
        builder.RegisterType<ConnectionManager>().As<IConnectionManager>().SingleInstance();

        builder.RegisterHubs(Assembly.GetExecutingAssembly());

        var container = builder.Build();

        return container;
    }
}

But, I still am not able to get the reference to the Hub in my Service (which is in a seperate project than the API but in the same solution.

My Service method is called from HangFire and has no reference to the Hub. How can I reference it in my parameterless constructor?

public partial class ShopAPService : IShopAPService
{
    private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    readonly IShopAPRepository _shopAPRepository;
    readonly IMapper _mapper;
    private IHubContext _hubContext;

    public ShopAPService()
    {
        _shopAPRepository = new ShopAPRepository();
        _mapper = new Mapper((IConfigurationProvider)typeof(InvoiceMappingProfile).Assembly);
        _hubContext = connectionManager.GetHubContext<MessageHub>();
    }

    public ShopAPService(IShopAPRepository shopAPRepository, IMapper mapper, IHubContext hubContext)
    {
        _shopAPRepository = shopAPRepository;
        _mapper = mapper;
        _hubContext = hubContext;
    }
}

解决方案

You can't resolve IHubContext directly. But you can resolve generic implementation of this interface. More details described here and here.

I just create very simple implementation of OWIN (Stratup.cs). I have installed Autofac.SignalR nugget package and used method RegisterHubs for registration and method MapSignalR for mapping. It is standard approach and it is enghoy to resolve typed Hub implementation.

But if you wish resolve context more correct then you need to add two more registrations: AutofacDependencyResolver and ConnectionManager (more info available here).

Please review full sample:

using Autofac;
using Autofac.Integration.SignalR;
using Autofac.Integration.WebApi;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
using System.Reflection;
using System.Web.Http;

[assembly: OwinStartup(typeof(Startup))]
namespace Sample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var container = GetDependencyContainer();

            RegisterWebApi(app, container);
            RegisterSignalR(app, container);
        }

        private IContainer GetDependencyContainer()
        {
            var builder = new ContainerBuilder();

            AutofacConfig.RegisterModules(builder);
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            builder.RegisterHubs(Assembly.GetExecutingAssembly());

            // Register Autofac resolver into container to be set into HubConfiguration later
            builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
            // Register ConnectionManager as IConnectionManager so that you can get hub context via IConnectionManager injected to your service
            builder.RegisterType<ConnectionManager>().As<IConnectionManager>().SingleInstance();

            var container = builder.Build();

            return container;
        }

        private void RegisterWebApi(IAppBuilder app, IContainer container)
        {
            var configuration = new HttpConfiguration
            {
                DependencyResolver = new AutofacWebApiDependencyResolver(container)
            };

            WebApiConfig.Register(configuration);

            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(configuration);
            app.UseWebApi(configuration);
        }

        private void RegisterSignalR(IAppBuilder app, IContainer container)
        {
            var configuration = new HubConfiguration
            {
                Resolver = new AutofacDependencyResolver(container)
            };

            app.MapSignalR(configuration);
        }
    }
}

My Hub is standard and simple:

public class MaintenanceHub : Hub
{
    public MaintenanceHub(IMaintenanceLogProvider maintenanceLogProvider)
    {
        maintenanceLogProvider.TaskProgressStatusEvent += (s, e) => GetTaskLogStatus(e);
    }

    public void GetTaskLogStatus(LongTaskProgressStatus taskProgressStatus)
    {
        Clients.All.getTaskLogStatus(taskProgressStatus);
    }
}

After registration you can use Hub inject context. Something like it (available 2 options, you can use only one what is you wish):

public AccountController(IConnectionManager connectionManager, MaintenanceHub maintenanceHub)
{
    var context = connectionManager.GetHubContext<MaintenanceHub>();
    var hub = maintenanceHub;
}

这篇关于使用Autofac将SignalR IHubContext注入服务层的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆