Asp.net mvc 5 为每个客户端创建单独的SignalR Hub实例并传递参数

Asp.net mvc 5 为每个客户端创建单独的SignalR Hub实例并传递参数,asp.net-mvc-5,signalr,signalr-hub,signalr.client,Asp.net Mvc 5,Signalr,Signalr Hub,Signalr.client,我正在尝试创建一个页面,当数据库中发生任何更改时,该页面将实时更新数据。我用过信号器。只有一个集线器,以下是集线器的代码: public class MyHub : Hub { public static int prodId { get; set; } public void setProdID(int pid) { prodId = pid; } public BidDetailViewModel GetChanges()

我正在尝试创建一个页面,当数据库中发生任何更改时,该页面将实时更新数据。我用过信号器。只有一个集线器,以下是集线器的代码:

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);
    }
}
如您所见,我正在通过调用
SetProdID
函数在
MyHub
hub中设置
ProdId
。我已经将
ProdId
设置为一个静态成员,这样我就可以在不创建中心对象的情况下进行设置。现在,如果访问
/Home/About
,它会将
prodId
设置为
40
(如预期的那样),然后我从查询中获得结果。但是,当我访问
\Home\About2
时,它将
prodId
设置为
41
(同样,正如预期的那样),并从查询中获得与参数
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>
现在,如果我在数据库中用id
41
更新项目,则
/Home/About
/Home/About2
页面都会实时更新,但会使用与id
41
项目对应的数据。我知道这篇文章很长,但我的问题是,signaler不应该为两个不同的页面制作一个完全独立的
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();
        ..............
    }
}

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

信号机集线器是暂时的。这意味着signar为每个请求创建一个新的hub实例。请注意,因此您无法在hub实例中保持特定于hub的状态(例如,在非静态属性中),因为一旦hub方法完成,hub将被释放,并且存储的值将丢失

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


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

谢谢!我认为静态成员是问题所在。我对SignalR是个新手,你能给我解释一下如何将prodId传递到hub,这样我就不必让它是静态的吗?请参阅我添加的最后一段。您可以使用连接id标识连接,您可以通过调用
Context.ConnectionId
在集线器方法中获取连接id。现在可以将设置存储在字典(必须是ConcurrentDictionary)中,并将此连接Id用作密钥。此字典可以是静态变量,因为每个连接都将更新/读取其拥有的插槽,而不是实例的引用。