WebAPI + OWIN + SignalR + Autofac

7

我已经苦苦挣扎了几周,这个问题让我很困惑。

我的应用程序配置了OWIN后端、Web API和Autofac DI背景下的Handfire Jobs。我已经查看了Stackoveflow上关于这个问题的所有问题,但是似乎没有什么作用。

我的应用程序关于OWIN/Hangfire/WebAPI都运行得很好。直到涉及到SignalR推送消息。

如果我从js客户端调用任何通知中心终点,推送消息就可以正常工作,并且我可以在任何其他连接的客户端上接收推送消息。但是当我想从我的api控制器或hangfire job发送消息时,它永远不会到达任何客户端。

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);
    }

因为我的数据库设置了InstancePerRequest作用域,所以我必须使用不同的容器。

所有我的服务都在通知中心类中进行解析,没有任何问题。唯一的问题是,当我尝试通过Hangfire服务或甚至从API控制器使用hub上下文发送消息时,它从未到达任何客户端。

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用于从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"));
    }
}

Hangfire任务是否已启动? - Stefan
1
是的,它触发了。做它需要做的事情。调用signalR帮助程序实例,然后调用hub上下文,但什么也没有发生,我们甚至在那里有正确的hub实例。 - user2539602
我甚至尝试使用Clients.All,以便每个人都能收到通知,但是在Hangfire服务和API控制器中什么也没有发生。 - user2539602
好的,明白了。所以SignalRHelper中的所有调用都没有被客户端接收到。虽然看起来不是这种情况,但我猜测您在SignalRHelper类中收到了一个新的HubContext实例。 - Stefan
@Stefan - 是的,那是我的第一个假设,但我在构造函数上放了调试器断点,Autofac 只为这些创建了单个实例,因为调试器断点只被触发一次。 :( - user2539602
显示剩余3条评论
1个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
0
这是否与Autofac为您的调用创建新的生命周期范围有关,但您希望继续使用现有的范围?或许可以检查一下Autofac注册是否有单例/实例化生命周期范围。顺便说一下,您是否已经注册了任何静态类?它们可能会使您的范围保持活动时间过长。 我看到您正在使用多个ContainerBuilder——在我们这里不这样做,我们每个应用程序都有一个“庞大”的ContainerBuilder。我很好奇您为什么这样做?为了满足我的好奇心,您可以尝试使用单个ContainerBuilder,并在该单个构建器上注册所有内容吗?(虽然看起来这是SignalR和Autofac的模式) 文档中说:“ OWIN集成中的常见错误是使用GlobalHost。” 看起来你正在做这件事。

我不得不使用另一个容器构建器,因为主容器构建器使用DBContext作为每个请求的实例,当我在Signalr DI和Hangfire DI中使用相同的容器时,会立即抛出异常,因为这个范围在那里不可用。 - user2539602
我现在已经对启动文件进行了更改,但问题仍然存在。请查看此链接:https://filemanager.dynamitsolutions.com/index.php/s/5X71bHUBiwr4u6q - user2539602
一旦我们涉及到细节,我很高兴编辑我的回答。让我们试着先帮助他,好吗? - increddibelly
在上面的链接中,您会找到一个非常简化的Startup.cs文件和另一个类,作为演示,我想向客户推送通知。 - user2539602
我从代码中删除了全局主机内容,现在使用单个容器。仍然存在相同的问题。如果我使用全局主机,则一切正常,但是我无法在hub类中解析任何服务。 - user2539602

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,