問題の説明
機能要件
ビジネスは、サーバー側がクライアントにいくつかの定期的なアップデートをプッシュ実装するために、プログラムのアイデアは、次のとおりです。クライアントは、そのConnectionIdを記録ConnectionIdクライアントに時限時限タスクを作成するためにタイマーを使用するように接続すると、メッセージを送信します。
異常な位置
説明ObjectDisposedExceptionクライアント接続Clients.Client(CONNID)またはタイミングタスクで他のメソッドを取得します。
異常な位置コード:
public async void LoopTask(object state)
{
// state是ConnectionId
string connId = state.ToString();
// 任何方式获取客户端连接都会冒ObjectDisposedException
var client = Clients.Client(connId);
string arg = "this is an argument";
await client.SendAsync("Test", arg);
}
問題の概要のコード:
public class TestHub : Hub
{
// 定时任务字典。Dictionary不是线程安全的,实际应用中应该加锁或者使用ConcurrentDictionary
private static Dictionary<string, Timer> Tasks { get; set; }
static TestHub()
{
Tasks = new Dictionary<string, Timer>();
}
public override Task OnDisconnectedAsync(Exception exception)
{
string connId = Context.ConnectionId;
if(Tasks.TryGetValue(connId,out Timer timer))
{
timer.Dispose();
}
return base.OnDisconnectedAsync(exception);
}
public override Task OnConnectedAsync()
{
string connId = Context.ConnectionId;
// 0毫秒后开始每5秒执行一次
Timer timer = new Timer(LoopTask, connId, 0, 5000);
Tasks.Add(connId, timer);
return base.OnConnectedAsync();
}
public async void LoopTask(object state)
{
string connId = state.ToString();
// 任何方式获取客户端连接都会冒ObjectDisposedException
var client = Clients.Client(connId);
string arg = "this is an argument";
await client.SendAsync("Test", arg);
}
}
ソリューション
ハブが一時的であるので、それが完了すると、要求をオフに処分します、すべてのリクエストでの動作は、オブジェクト・ハブ(クライアント、グループなど)を取得クライアント接続の内部で直接使用することはできません。
Asp.netのコアを使用して、クライアント接続のクラスをオープンするには、ここだけではIHubContextの依存性の注入は、クラスの中に注入されてくる、操作IHubContextを持つクラス。
Asp.netとAsp.netコアは同じではないことに留意する必要があるAsp.netコアに、このようにして得られたことができない一方で、Asp.netは、IHubContextにGlobalHostで取得することができます。
コードの概要を変更した後:
public class ClientConnection : IDisposable
{
// 连接标识,也可以是userId等,反正能定位到客户端就行
public string ConnectionId { get; set; }
// HubContext,用于获取客户端连接
private IHubContext<TestHub> Context { get; set; }
// 定时任务
private Timer Timer { get; set; }
public ClientConnection(IHubContext<TestHub> hubContext, string connId)
{
this.ConnectionId = connId;
this.Context = hubContext;
// 0毫秒后开始每5秒执行一次
Timer = new Timer(LoopTask, null, 0, 5000);
}
public async void LoopTask(object state)
{
var client = Context.Clients.Client(this.ConnectionId);
string arg = "this is an argument";
await client.SendAsync("Test", arg);
}
public void Dispose()
{
this.Timer.Dispose();
}
}
public class TestHub : Hub
{
// 用于获取依赖注入对象
private IServiceProvider Service { get; set; }
// 这里其实不应该把连接保存在Hub中,demo的话随便了(记得加锁)
private static List<ClientConnection> Connections;
static TestHub()
{
Connections = new List<ClientConnection>();
}
public TestHub(IServiceProvider service)
{
Service = service;
}
public override Task OnDisconnectedAsync(Exception exception)
{
string connId = Context.ConnectionId;
var conn = Connections.Find(s => s.ConnectionId == connId);
if(conn != null)
{
Connections.Remove(conn);
conn.Dispose();
}
return base.OnDisconnectedAsync(exception);
}
public override Task OnConnectedAsync()
{
string connId = Context.ConnectionId;
// 这里不用在StartUp里面AddScoped也可以获取到
IHubContext<TestHub> context = Service.GetService(typeof(IHubContext<TestHub>)) as IHubContext<TestHub>;
ClientConnection conn = new ClientConnection(context, connId);
Connections.Add(conn);
return base.OnConnectedAsync();
}
}
問題の原因
問題の原因は、一時的なハブのインスタンスは、コントローラのように、インスタンスを生成するための要求は、もっともらしく思われることです。ハブが一時的であるが、それは非常にハブを考えるように簡単に、混乱している、(WebSocketの接続長)接続は永続的で管理ものの、永続的です。
最後に、提案されたプロセスピットへのまたはそれらの多くは機械翻訳であり、言葉はより曖昧ですが、フォーカスが具体的に記載され、これは賞賛を与えることができますが、より多くのドキュメントのは、実際には、Microsoftのドキュメントは本当に良い仕事をして確認してください。