【问题标题】:Creating a separate SignalR Hub instance for each client and passing parameters为每个客户端创建单独的 SignalR Hub 实例并传递参数
【发布时间】:2018-04-15 04:01:11
【问题描述】:

我正在尝试创建一个页面,该页面可以在数据库发生更改时实时更新数据。我用过 SignalR。只有一个集线器,这里是集线器的代码:

public class MyHub : Hub
{
    public static int prodId { get; set; }

    public void setProdID(int pid)
    {
        prodId = pid;
    }

    public BidDetailViewModel GetChanges()
    {
        string conStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
        SqlConnection connection = new SqlConnection(conStr);

        SqlDependency.Start(conStr);
        string query = @"select Id,
                                BidDate,
                                BidAmount,
                                BidStatusId,
                                BidderId,
                                ProductId 
                                from [dbo].[Bids] 
                                where ProductId = " + prodId + 
                                " order by BidDate desc ";
        SqlCommand command = new SqlCommand(query, connection);
        command.Notification = null;
        SqlDependency dependency = new SqlDependency(command);

        //If Something will change in database and it will call dependency_OnChange method.
        dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
        connection.Open();
        SqlDataReader dr = command.ExecuteReader();

        var bid = new Bid();

        while (dr.Read())
        {
            bid.Id = dr.GetInt32(0);
            bid.BidDate = DateTime.Now; //fake value, dont need it
            bid.BidAmount = dr.GetFloat(2);
            bid.BidStatusId = dr.GetInt32(3);
            bid.BidderId = dr.GetString(4);
            bid.ProductId = dr.GetInt32(5);

            //Break after reading the first row
            //Using this becasue we can not use TOP(1) in the query due to SignalR restrictions
            break;
        }
        connection.Close();
        var vm = new BidDetailViewModel
        {
            HighestBid = bid,
            NoOfBids = -1
         //NoOfBids is no longer needed, assigning a random value of -1
         //will remove it later, just testing rn
        };

        return vm;
    }

    private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
    {
        if (e.Type == SqlNotificationType.Change) SendNotifications();
    }

    private void SendNotifications()
    {
        BidDetailViewModel vm = GetChanges();
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();

        //Will update all the client with new bid values
        context.Clients.All.broadcastMessage(vm);
    }
}

我在两个不同的页面中调用集线器。第一个是/Home/About,第二个是/Home/About2。它们都具有相同的 JS 代码,除了一个更改,/Home/About 中传递的 prodId 是 40/Home/About241。 JS 代码是:(这是/Home/About 的代码,您可以看到它正在发送40。同样,/Home/About2 具有完全相同的代码,但带有41)。

<script>
    $(function () {
        // Declare a proxy to reference the hub.
        var ew = $.connection.myHub;
        //This method will fill all the Messages in case of any database change.
        ew.client.broadcastMessage = function (response) {
            //Changing HTML content here using document.getElementById()
        };

        //This method will fill all the Messages initially
        $.connection.hub.start().done(function () {
            ew.server.setProdID(40);
            //Hardcoding the prodID for now, will get it using document.getElementById() later
            //ProdId will be different for each page
            ew.server.getChanges().done(function(response) {
               //Changing HTML content here using document.getElementById()
            });
        });
    });
</script>

如您所见,我通过调用SetProdID 函数在MyHub 集线器中设置ProdId。我已将 ProdId 设置为静态成员,因此我可以设置它而无需创建集线器的对象。现在如果访问/Home/About,它将prodId 设置为40(如预期的那样),我从查询中得到结果。但是,当我访问\Home\About2 时,它会将prodId 设置为41(再次,正如预期的那样),我从查询中得到与参数41 对应的结果。

现在,如果我在数据库中更新 ID 为 41 的项目,/Home/About/Home/About2 页面都会实时更新,但数据与 ID 为 41 的项目相对应。我知道这篇文章真的很长,但我的问题是,SignalR 不应该为两个不同的页面制作一个完全独立的 MyHub1 实例吗?这是因为我将ProdId 设为静态成员吗​​?如果是这样,我如何设置它而不使其成为静态成员?我基本上想要同一个MyHub 的多个实例,每个实例对应一个完全不同的prodId。这可能吗?还是我必须编写多个集线器(MyHub1、MyHub2、.... MyHubn)。编写多个集线器的问题是我不知道会有多少项目。

我也尝试将prodId 作为参数传递给GetChanges() 方法,但我不确定在调用GetChanges() 方法的sendNotification() 方法中传递什么。

public class MyHub : Hub
{
    public BidDetailViewModel GetChanges(int pid)
    {
        ............
        string query = @"select Id,
                                BidDate,
                                BidAmount,
                                BidStatusId,
                                BidderId,
                                ProductId 
                                from [dbo].[Bids] 
                                where ProductId = " + pid + 
                                " order by BidDate desc ";
        ...............
    }

    private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
    {
        if (e.Type == SqlNotificationType.Change) SendNotifications();
    }

    private void SendNotifications()
    {
        //What do I pass here?
        BidDetailViewModel vm = GetChanges();
        ..............
    }
}

我不知道如何从这里开始,任何指针将不胜感激!另外,感谢您阅读这篇长文。

【问题讨论】:

    标签: asp.net-mvc-5 signalr signalr-hub signalr.client


    【解决方案1】:

    SignalR 集线器是瞬态的。这意味着 SignalR 为每个请求创建一个新的集线器实例。请注意,因此您无法在集线器实例中保留集线器特定状态(例如,在非静态属性中),因为一旦集线器方法完成,集线器将被释放,您存储的值将丢失。

    您的问题是您将 id 存储在静态变量中。静态成员在给定类的所有实例之间共享,因此如果您在一个实例中更改它,其他实例将看到新值。您可以在此article 中阅读有关静态类和成员的更多信息。

    解决问题的一种方法是将状态保存在上下文中的静态变量中,该变量是 ConcurrentDictionary,您在其中使用 connectionId 作为键。您可以使用Context.ConnectionId 获取标识调用当前集线器方法的客户端的connectionId。

    【讨论】:

    • 谢谢!我认为静态成员是问题所在。我对 SignalR 还很陌生,您能否向我解释一下如何将 prodId 传递到集线器,这样我就不必将其设为静态?
    • 见我添加的最后一段。您可以使用连接 ID 来识别连接,您可以通过调用 Context.ConnectionId 在集线器方法中获取该连接 ID。您现在可以将设置存储在字典中(必须是 ConcurrentDictionary),您可以在其中使用此连接 ID 作为键。这个字典可以是一个静态变量,因为每个连接都会更新/读取它拥有的插槽,而不是对实例的引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多