Autofac을 사용하여 SignalR IHubContext를 서비스 계층에 삽입
.NET Core가 아닌 Framework 4.72를 실행하는 앱에서 SignalR IHubContext를 Web API 2.x 서비스에 삽입하려고합니다. 내 솔루션은 웹, 서비스, 데이터의 세 가지 프로젝트로 나뉩니다. SignalR 허브는 웹 계층에 있습니다. 서비스 계층에서 실행되는 백그라운드 코드가 있으며 완료되면 허브를 통해 메시지를 보내야합니다. 이 백그라운드 작업은 컨트롤러에 의해 시작되지 않습니다.
내 Global.asax는 꽤 표준입니다.
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));
}
내 DIConfig에는 다음이 포함됩니다.
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);
}
그리고 내 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);
});
}
}
내 서비스 계층의 생성자는 다음과 같습니다.
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;
}
IHubContext의 인스턴스를 서비스에 전달해야한다는 것을 알고 있지만 지금까지 성공하지 못했습니다. 아시다시피, 첫 번째 생성자는 컨트롤러가 아닌 다른 곳에서 서비스를 호출 할 때 사용되는 것입니까?
첫 번째 개정
좋아, 나는 모든 것이 하나의 컨테이너에 들어가야한다는 것을 이해한다. 피드백과 해당 링크를 기반으로 컨테이너를 만들고 전달합니다. 수정 된 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);
}
}
그리고 내 AutofacConfig는 컨테이너를 빌드하고 대부분의 등록을 수행합니다.
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;
}
}
그러나 여전히 내 서비스에서 허브에 대한 참조를 가져올 수 없습니다 (API와 별도의 프로젝트에 있지만 동일한 솔루션에 있음).

내 서비스 메서드는 HangFire에서 호출되며 허브에 대한 참조가 없습니다. 매개 변수없는 생성자에서 어떻게 참조 할 수 있습니까?
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;
}
}
답변
IHubContext
직접 해결할 수 없습니다 . 그러나이 인터페이스의 일반적인 구현을 해결할 수 있습니다. 여기 와 여기에 자세한 내용이 설명 되어 있습니다 .
OWIN
(Stratup.cs) 의 매우 간단한 구현을 만듭니다 . Autofac.SignalR 너깃 패키지를 설치 하고 RegisterHubs 메서드를 등록 에 사용 하고 MapSignalR 메서드 를 매핑에 사용했습니다. 이는 표준 접근 방식이며 형식화 된 허브 구현을 해결하는 것이 좋습니다.
그러나 컨텍스트를 더 정확하게 해결하려면 AutofacDependencyResolver 및 ConnectionManager (자세한 내용은 여기 ) 라는 두 가지 등록을 더 추가해야합니다 .
전체 샘플을 검토하십시오.
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는 표준이며 간단합니다.
public class MaintenanceHub : Hub
{
public MaintenanceHub(IMaintenanceLogProvider maintenanceLogProvider)
{
maintenanceLogProvider.TaskProgressStatusEvent += (s, e) => GetTaskLogStatus(e);
}
public void GetTaskLogStatus(LongTaskProgressStatus taskProgressStatus)
{
Clients.All.getTaskLogStatus(taskProgressStatus);
}
}
등록 후 허브 주입 컨텍스트를 사용할 수 있습니다. 비슷한 것 (2 가지 옵션을 사용할 수 있으며 원하는 것을 하나만 사용할 수 있음) :
public AccountController(IConnectionManager connectionManager, MaintenanceHub maintenanceHub)
{
var context = connectionManager.GetHubContext<MaintenanceHub>();
var hub = maintenanceHub;
}
