Base solution for your next web application
Open Closed

abp.web.signalr in application project #5149


User avatar
0
OriAssurant created

I'm using MVC5.* and AngularJS for our project. I have a shopping cart module for user to add items into it . I'm trying to archive that an item could be added to all opened windows if user add one item in one of opened windows. I have couple questions on the way of using AbpCommonHub to implement signalr hub.

(1) Can I use AbpCommonHub in Application project (instead of Web Project)? If I can, do I still need to register in Start_Up.cs in the web project? (2) If could, could I add AbpCommonHub as a property in one of my Services?

public class ShoppingCartService : MyProjectAppServiceBase, IShoppingCartService{
    private static IHubContext CommonHub
    {
        get
        {
            return GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();
        }
    }

    public void BroadCastAddItem(IReadOnlyList<IOnlineClient> clients)
{
    foreach (var client in clients)
    {
        var signalRClient = CommonHub.Clients.Client(client.ConnectionId);
        if (signalRClient == null)
        {
            Logger.Debug("Can not get user " + client.UserId + " from SignalR hub!");
            continue;
        }
    
        signalRClient.getAddItemMessage("A new Item added to Shopping Cart!"));
    }
}
}

*:Because I already inherit base class MyProjectAppServiceBase, I could not add AbpCommonHub as base class. Is it okay to add AbpCommonHub as a private field?

(3) Could I use localhost:6240/signalr to connect to the server?

Any hint would be greatly appreciated! Thank you in advance.


11 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @OriAssurant ,

    1. Yes, you can but you need to reference Microsoft.AspNet.SignalR in the Application project. Yes, you need to register it.

    I couldn't understand the other two questions, sorry. Why don't you use a similar way we did for Chat feature. You can define a ShoppingCartHub similar to <a class="postlink" href="https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Chat/SignalR/ChatHub.cs">https://github.com/aspnetzero/aspnet-ze ... ChatHub.cs</a>.

    On the client side, when a user adds an item to cart, instead of calling the app service, you need to communicate with the ShoppingCartHub. Then it will notify all open browsers (including the one which users is shopping on) to add this new item to user's cart.

    You can take a look at how we send chat message to another user.

  • User Avatar
    0
    OriAssurant created

    <cite>ismcagdas: </cite> Hi @OriAssurant ,

    1. Yes, you can but you need to reference Microsoft.AspNet.SignalR in the Application project. Yes, you need to register it.

    I couldn't understand the other two questions, sorry. Why don't you use a similar way we did for Chat feature. You can define a ShoppingCartHub similar to <a class="postlink" href="https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Chat/SignalR/ChatHub.cs">https://github.com/aspnetzero/aspnet-ze ... ChatHub.cs</a>.

    On the client side, when a user adds an item to cart, instead of calling the app service, you need to communicate with the ShoppingCartHub. Then it will notify all open browsers (including the one which users is shopping on) to add this new item to user's cart.

    You can take a look at how we send chat message to another user.

    Thank you very much, ismcagdas. Let me try with the ChatHub way:)

  • User Avatar
    0
    aaron created
    Support Team

    You need to login with your GitHub account to access the private repo. You can invite yourself here: <a class="postlink" href="https://aspnetzero.com/LicenseManagement">https://aspnetzero.com/LicenseManagement</a>

  • User Avatar
    0
    OriAssurant created

    <cite>aaron: </cite> You need to login with your GitHub account to access the private repo. You can invite yourself here: <a class="postlink" href="https://aspnetzero.com/LicenseManagement">https://aspnetzero.com/LicenseManagement</a>

    Gotcha, thank you!

  • User Avatar
    0
    OriAssurant created

    I've registered the shoppingCartHub on the same site as ChatHub: localhost:6240/signalr. It's successful registered as console.log("shoppingCartHub", shoppingCartHub ) returns me the hub object at client side and I could see a message in the console: SignalR: Triggering client hub event 'sendEventData' on hub 'AbpCommonHub'. But not sure why the callback function was not get called. Could you help me take a look where I did incorrectly?

    Server Side. Is it correct to use AbpCommonHub instead of ShoppingCartHandler in the return type of _hubContext ? _hubContext = GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();?

    [HubName("shoppingCartHub")]
    public class ShoppingCartHandler : AbpCommonHub, IEventHandler<EventData>
    {
        private IHubContext _hubContext;
    
        public ShoppingCartHandler(IOnlineClientManager onlineClientManager, IClientInfoProvider clientInfoProvider) : base(onlineClientManager, clientInfoProvider)
        {
            _hubContext = GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();
        }
    
        public void HandleEvent(EventData eventData)
        {
            _hubContext.Clients.All.sendEventData(eventData.Entry);
        }
    }
    

    Client Side:

    $.connection.hub.url = abp.signalr.url || '/signalr';
            $.connection.hub.logging = true;
            var shoppingCartHub = $.connection.shoppingCartHub;
            console.log("shoppingCartHub", shoppingCartHub );     //this line could run.
            shoppingCartHub.on("sendEventData", function (message) { console.log("called inside event", message);})
    
            $.connection.hub.start().done(function () {
                console.log('ShoppingCart Hub has started');   //this line could run.
            });
            $.connection.hub.error(function (err) {
                console.log("ShoppingCart Hub ERROR : " + err);
            });
    

    Autogenerated proxy: Seems it's only contains handleEvent method.

    proxies['shoppingCartHub'] = this.createHubProxy('shoppingCartHub'); 
            proxies['shoppingCartHub'].client = { };
            proxies['shoppingCartHub'].server = {
                    handleEvent function (eventData) {
                    return proxies['shoppingCartHub'].invoke.apply(proxies['shoppingCartHub'], $.merge(["HandleEvent"], $.makeArray(arguments)));
                 },
    
                register: function () {
                    return proxies['shoppingCartHub'].invoke.apply(proxies['shoppingCartHub'], $.merge(["Register"], $.makeArray(arguments)));
                 }
            };
    
  • User Avatar
    0
    aaron created
    Support Team

    That looks correct. Where are you calling HandleEvent?

  • User Avatar
    0
    OriAssurant created

    I'm using EventBus.Trigger to pass the message from the ShoppingCartService application service to the shoppingCartHub, and in the HandleEvent method I called SignalR dynamic method sendEventData.

    public class ShoppingCartService : MyProjectAppServiceBase, IShoppingCartService{
           ...
          public async Task EditCartData(ShoppingCartInput shoppingCartInput){
                   ...
                  EventBus.Trigger(new EventData() { ItemId = itemId});
          }
        }
    

    According to the comment in following page, I changed my client side a little: <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1891">https://github.com/aspnetboilerplate/as ... ssues/1891</a>

    I've been having this issue too. I think I may have narrowed it down to abp.signalr.js connecting to the server before the client side events have been established. It seems you need to call $.connection.hub.start() after you have set up your events. I'm guessing it is so that it can do some bindings.

    If I set abp.signalr.autoConnect = false and then manually call abp.signalr.connect() at the end of my controller, all clients start receiving calls from the server as expected.

    Updated Client Side

    abp.signalr = abp.signalr || {};
            abp.signalr.autoConnect = false;
    
            $.connection.hub.url = abp.signalr.url || '/signalr';
            $.connection.hub.logging = true;
    
            var shoppingCartHub= $.connection.shoppingCartHub;
            shoppingCartHub.client.sendEventData= onEntryCreatedorUpdated;
    
            abp.signalr.connect();
            console.log("abp.signalr after connect", abp.signalr);
    

    It's still not working... One thing I noticed from console.log("abp.signalr after connect", abp.signalr); is shoppingCartHub is not in the Connection List and is in the Proxies list. Here is the snapshot: [attachment=0:2fz49oxu]2018-05-23_22-30-34.jpg[/attachment:2fz49oxu] Is that the reason why the callback function is not reached?

  • User Avatar
    0
    OriAssurant created

    I found the issue.. I need to stop the hub and restart to allow the event handlers to be registered.

    But when I declare a new hub context at the server side, and use it to broadcast the event data. _hubContext = GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>(); _hubContext.Clients.All.sendEventData(eventData.Entry); The hub context is differentfrom the context used by abp's signalr hub context so the event is not associated to the event handler at client side. <ins>Could you advice if there is a way for me to use the same hub context with abp's signalr?</ins>

    For abp's Chat and Notification functions, I saw messges are starting from **hub.server.sendMessage(message), and then in SendMessage method, you just need to use 'Clients.All.getMessage(anotherMessage)' because the hub context is already there and you are always using the same hub context.

    [HubName("shoppingCartHub")]
    public class ShoppingCartHandler : AbpCommonHub, IEventHandler<EventData>
    {
        private IHubContext _hubContext;
    
        public ShoppingCartHandler(IOnlineClientManager onlineClientManager, IClientInfoProvider clientInfoProvider) : base(onlineClientManager, clientInfoProvider)
        {
            _hubContext = GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();
        }
    
        public void HandleEvent(EventData eventData)
        {
            _hubContext.Clients.All.sendEventData(eventData.Entry);
        }
    }
    
  • User Avatar
    0
    aaron created
    Support Team

    The hub context is different from the context used by abp's signalr hub context so the event is not associated to the event handler at client side.

    That's the same as how Abp.Web.SignalR's SignalRRealTimeNotifier gets CommonHub:

    private static IHubContext CommonHub
    {
        get
        {
            return GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();
        }
    }
    
  • User Avatar
    0
    OriAssurant created

    <cite>aaron: </cite>

    The hub context is different from the context used by abp's signalr hub context so the event is not associated to the event handler at client side.

    That's the same as how Abp.Web.SignalR's SignalRRealTimeNotifier gets CommonHub:

    private static IHubContext CommonHub
    {
       get
       {
           return GlobalHost.ConnectionManager.GetHubContext<AbpCommonHub>();
       }
    }
    

    I finally removed the EventBus backplane and directly call the event handler method from front end... now it works as they will definitely share the same hub context. Thank you Aaron.

  • User Avatar
    0
    aaron created
    Support Team

    That's great :)