WebAPI + OWIN + SignalR + Autofac - PullRequest
       45

WebAPI + OWIN + SignalR + Autofac

0 голосов
/ 04 сентября 2018

Я боролся с этим вопросом уже несколько недель.

У меня есть приложение, в котором я сконфигурировал owin backend с web api и autofac DI с фоновыми заданиями ручной стрельбы. Я почти все вопросы об Stackoveflow рассмотрел, но, похоже, ничего не работает. Мое приложение, касающееся OWIN / Hangfire / WebAPI, похоже, работает нормально. Пока речь не идет о push-сообщениях SignalR.

Если я вызываю любую конечную точку концентратора уведомлений из push-сообщений js-клиента, все в порядке, и я могу получать push-сообщения на любом другом подключенном клиенте. Но когда я хочу отправить сообщение с моего контроллера API или работу с Hangfire, он никогда не достигает ни одного клиента.

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

Мне пришлось использовать другой контейнер, потому что я установил для db значение InstancePerRequest.

Все мои сервисы разрешаются в классе уведомлений, проблем там нет. Единственная проблема заключается в том, что когда я пытаюсь отправить сообщение из службы Hangfire или даже из контроллера API, используя контекстный концентратор, он никогда не достигает ни одного клиента.

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

1 Ответ

0 голосов
/ 07 сентября 2018

Может ли это быть связано с тем, что Autofac создает новую жизненную область для вашего звонка, но вы ожидали, что продолжите использовать существующую область? Может быть, проверьте ваши регистрации автофака для singleinstance / instanceperlifetimescope Просто говорю, но вы зарегистрировали какие-либо статические классы? Они могут поддерживать ваш прицел слишком долго.

Я вижу, что вы используете несколько контейнерных сборщиков - мы здесь не делаем этого, у нас есть один «массивный» контейнерный конструктор для каждого приложения. Мне любопытно, почему ты это делаешь? Чтобы удовлетворить мое любопытство, не могли бы вы попробовать использовать один контейнеростроитель и зарегистрировать все на этом единственном сборщике? (Хотя, похоже, это шаблон для SignalR и autofac)

Документация гласит : «Распространенной ошибкой в ​​интеграции OWIN является использование GlobalHost.»

Похоже, вы делаете именно это.

...