[英]WebAPI + OWIN + SignalR + Autofac
I have been struggling on this issue for weeks now. 几个星期以来,我一直在努力解决这个问题。
I have an app where i have configured owin backend with web api and autofac DI with background handfire jobs. 我有一个应用程序,我已配置owin后端与web api和autofac DI与后台handfire工作。 I have alsmost looked at every question on Stackoveflow regarding this but nothing seems to work. 我几乎关注了Stackoveflow上的每一个问题,但似乎没有任何效果。 My app regarding OWIN/Hangfire/WebAPI all seems to work okay. 关于OWIN / Hangfire / WebAPI的我的应用程序似乎都运行正常。 Until it comes to SignalR push messages. 直到SignalR推送消息。
If i call any notification hub endpoint from js client push messages go okay and i can receive push messages on any other connected client. 如果我从js客户端推送消息调用任何通知集线器端点就可以了,我可以在任何其他连接的客户端上接收推送消息。 But when i wan to send message from my api controller or hangfire job it never reaches to any client. 但是,当我想从我的api控制器或hangfire工作发送消息时,它永远不会到达任何客户端。
Startup.cs Startup.cs
public void Configuration(IAppBuilder app)
{
//var signalRHelper = new SignalRHelper(GlobalHost.ConnectionManager.GetHubContext<NotificationHub>());
var constants = new Constants();
constants.Set(ConstantTypes.AllyHrNoReplyEmailAddress, Util.Constants.AllyHrNoReplyEmailAddress);
constants.Set(ConstantTypes.SendGridKey, Util.Constants.SendGridKey);
constants.Set(ConstantTypes.EncryptionKey, Util.Constants.EncryptionKey);
constants.Set(ConstantTypes.ApiUrl, Util.Constants.ApiUrl);
constants.Set(ConstantTypes.RootFolder, Util.Constants.RootFolder);
constants.Set(ConstantTypes.FrontEndUrl, Util.Constants.FrontEndUrl);
GlobalConfiguration.Configuration
.UseSqlServerStorage("AllyHrDb");
var config = System.Web.Http.GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
var jobBuilder = new ContainerBuilder();
var signalRBuilder = new ContainerBuilder();
var hubConfig = new HubConfiguration();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.Register(x => constants);
builder.RegisterModule(new ServiceModule());
jobBuilder.Register(x => constants);
jobBuilder.RegisterModule(new HangfireServiceModule());
signalRBuilder.RegisterModule(new SignalRServiceModule());
signalRBuilder.Register(x => constants);
signalRBuilder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
signalRBuilder.RegisterType<ConnectionManager>().As<IConnectionManager>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<NotificationHub>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<SignalRHelper>().PropertiesAutowired().ExternallyOwned().SingleInstance();
signalRBuilder.Register(context => context.Resolve<IDependencyResolver>().Resolve<IConnectionManager>().GetHubContext<NotificationHub, INotificationHub>()).ExternallyOwned().SingleInstance();
var hubContainer = signalRBuilder.Build();
builder.RegisterInstance(hubContainer.Resolve<IConnectionManager>());
builder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
builder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
builder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
jobBuilder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
jobBuilder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
jobBuilder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
var container = builder.Build();
var jobContainer = jobBuilder.Build();
var idProvider = new SignalRCustomUserIdProvider();
hubConfig.Resolver = new AutofacDependencyResolver(hubContainer);
hubConfig.Resolver.Register(typeof(IUserIdProvider), () => idProvider);
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
map.RunSignalR(hubConfig);
});
GlobalConfiguration.Configuration.UseAutofacActivator(jobContainer);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseHangfireServer();
app.UseHangfireDashboard();
ConfigureAuth(app);
app.UseWebApi(config);
}
I had to use different container because i have db set to InstancePerRequest scope. 我不得不使用不同的容器,因为我将db设置为InstancePerRequest范围。
All my services are being resolved in notification hub class, no problems there. 我的所有服务都在通知集线器类中解决,没有问题。 The only issues is when i try and send message from hangfire service or even from api controller using hub context it never reaches to any client. 唯一的问题是当我尝试从hangfire服务或甚至从使用集线器上下文的api控制器发送消息时,它永远不会到达任何客户端。
NotificationHub.cs NotificationHub.cs
public interface INotificationHub
{
/// <summary>
///
/// </summary>
void pushNotification(string message);
/// <summary>
///
/// </summary>
/// <param name="model"></param>
void getNotification(object model);
void getMessage(object model);
}
/// <summary>
/// Notification Hub
/// </summary>
[HubName("NotificationHub")]
[Authorize]
public class NotificationHub : Hub<INotificationHub>
{
/// <summary>
///
/// </summary>
public static IHubContext<INotificationHub> GlobalContext { get; private set; }
private readonly IChatMessagingService _chatMessagingService;
private readonly IUserService _userService;
private Guid LoggedInUserId
{
get
{
var claims = ((ClaimsIdentity)Context.User.Identity).Claims.ToArray();
var userIdClaim = claims.FirstOrDefault(x => x.Type.Equals("UserId"));
if (userIdClaim == null) return Guid.Empty;
return Guid.Parse(userIdClaim.Value);
}
}
/// <summary>
/// Consructor
/// </summary>
/// <param name="lifetimeScope"></param>
/// <param name="context"></param>
public NotificationHub(ILifetimeScope lifetimeScope, IHubContext<INotificationHub> context)
{
GlobalContext = context;
try
{
var childScope = lifetimeScope.BeginLifetimeScope();
_chatMessagingService = childScope.Resolve<IChatMessagingService>();
_userService = childScope.Resolve<IUserService>();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
/// <summary>
/// Notifications
/// </summary>
public void Notifications()
{
Clients.All.pushNotification("AllyHr" + LoggedInUserId);
}
/// <summary>
/// Send Message
/// </summary>
/// <param name="model"></param>
public void SendMessage(SendChatMessageBindingModel model)
{
var chatMessage = _chatMessagingService.SendMessageToGroup(LoggedInUserId, model.GroupId, model.Message);
var recipientIds = _chatMessagingService.GetChatMembersByGroupId(LoggedInUserId, model.GroupId);
var stringUserIds = new List<string>();
var chatGroup = _chatMessagingService.GetChatGroupById(model.GroupId);
foreach (var recipientId in recipientIds)
{
stringUserIds.Add(recipientId.ToString());
}
Clients.Users(stringUserIds).getNotification(new
{
message = "A new Message is Recieved in Chat Group: " + chatGroup.Name,
groupId = chatGroup.Id
});
var chatMessageVm = chatMessage.Map<ChatMessage, ChatMessageViewModel>();
chatMessageVm.Sender = _userService.Get(chatMessageVm.SenderId).Map<User, UserViewModel>();
stringUserIds.Add(LoggedInUserId.ToString());
Clients.Users(stringUserIds).getMessage(chatMessageVm);
}
}
signalRhelper.cs use to call from api or from Hangfire services signalRhelper.cs用于从api或Hangfire服务调用
public class SignalRHelper
{
public IConnectionManager ConnectionManager { get; set; }
public IHubContext<INotificationHub> HubContext { get; set; }
/// <summary>
/// Send Notifications to Users
/// </summary>
/// <param name="message"></param>
/// <param name="userIds"></param>
public void GetNotification(object message, IList<string> userIds)
{
HubContext.Clients.Users(userIds).getNotification(message);
}
/// <summary>
/// Get LoggedInUser Id for SignalR
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static Guid GetLoggedInUserId(IPrincipal user)
{
var claim = GetLoggedinUserClaim(user);
if (claim == null) return Guid.Empty;
return Guid.Parse(claim.Value);
}
private static Claim GetLoggedinUserClaim(IPrincipal user)
{
var claim = ((ClaimsIdentity)user.Identity).Claims.ToArray();
return claim.FirstOrDefault(x => x.Type.Equals("UserId"));
}
}
Could this be related to Autofac creating a new lifetimescope for your call, but you were expecting to continue using the existing scope? 这可能与Autofac为您的通话创建新的生命周期显示有关,但您是否期望继续使用现有范围? Maybe check your autofac registrations for singleinstance / instanceperlifetimescope Just saying, but have you registered any static classes? 也许检查你的autinstac注册singleinstance / instanceperlifetimescope只是说,但你有没有注册任何静态类? They can keep your scope alive for far too long. 它们可以让你的范围保持很长时间。
I see you're using multiple containerbuilders - that's not something we do over here, we have one 'massive' containerbuilder for each app. 我看到你正在使用多个容器构建器 - 这不是我们在这里做的事情,我们为每个应用程序都有一个“大规模”容器构建器。 I'm curious why you're doing that? 我很好奇为什么你这样做? To satisfy my curiosity, could you try using a single containerbuilder and registering everything on that single builder? 为了满足我的好奇心,您是否可以尝试使用单个容器构建器并在该单个构建器上注册所有内容? (Although it looks like this is a pattern for SignalR and autofac) (虽然看起来这是SignalR和autofac的模式)
The documentation says : " a common error in OWIN integration is the use of GlobalHost." 文档说 :“OWIN集成中的常见错误是使用GlobalHost。”
It looks like you're doing exactly that. 看起来你正是这样做的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.