Facebookのような、リアルタイム通知をユーザーに送信できるWebアプリケーションを考えてみましょう。
asp.net SignalR を使用して、ユーザーが切断または再接続した場合でも、どのユーザーにどの接続IDが属しているかを追跡する最良の方法は何ですか?
次のブログ投稿をご覧ください。
ASP.NET SignalR接続の実際のアプリケーションユーザーへのマッピング
簡単に言うと、OnConnected
メソッドでユーザーに接続IDを追加し、OnDisconnected
メソッドでその接続を削除します。アプリケーションユーザーは複数の接続を持つことができることに注意してください。したがって、ユーザーと接続IDの間には1対多の関係が必要です。上記のリンク先の投稿では、サンプルを使用して詳細に説明しています。
私は内部アプリのためにこれを行いました。私が行った方法は、ユーザーが接続すると、サーバーにユーザーに登録を依頼するというものです。このようにして、ユーザーが接続され、そのsignalR connnectionIDだけでなく、他の情報(ユーザー名など)も教えてくれることがわかります。
彼らが再接続するとき、私は彼らにもう一度それをするように頼みます。
再接続しても、SignalRはクライアントごとに同じconnectionIDを維持します。再接続は初期接続と同じではありません。新しい接続は新しいクライアントを示しますが、再接続は同じクライアント上で行われます。
私のアプリでは、どのユーザーとどのconnectionIDが何をしているのかを追跡する別のスレッドセーフディクショナリを維持しました。このようにして、「ああ、ユーザーABCにメッセージを送信」と言って、その接続IDを調べることができます。次に、そのconnectionIDのsignalRでハブのクライアントオブジェクトを操作します。このようにすると、複数の接続で同じ「ユーザー」を持つこともできます。ユーザー "abc"が2つのブラウザタブで開いているとします。厳密にconnectionIDで行った場合、技術的には2人の異なるユーザーになります。ただし、ローカルコレクショングループのユーザーと接続の種類を維持することにより、同じユーザーに対して複数の接続を確立できます。
この方法でこれを行う場合は、サイトが再起動したときに発生する処理をサイトが処理し、すべての接続情報が失われるようにする必要があります。私にとって、誰かが再接続するとき、私は彼らに再び彼ら自身を再確認するように頼みます。これにより、サーバーが心配なくオンラインになったときに、ローカル辞書を再構築できます。すべてのクライアントに情報を送信するように要求しているため、オーバーヘッドは大きくなりますが、ユーザーのケースによっては、負荷を処理するために、ずらしたり、まとめたり、その他の方法で分散したりできます。
ただし、一般に、ローカル情報を取得するか(ユーザーに提供を要求するかどうか)、またはhttpコンテキストセッション情報を取得する場合は、自分で追跡する必要があります。
まあ、私は別のアプローチを使用して、ApplicationUserクラスを次のように拡張しました。
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.Microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
//public int ApplicationUserId { get; set; }
//public string Name { get; set; }
//public string Address { get; set; }
//public string City { get; set; }
//public string State { get; set; }
//public string Zip { get; set; }
[Required]
public string Email { get; set; }
[Required]
public override string UserName { get; set; }
[NotMapped]
public string ConnectionId { get; set; }
[NotMapped]
public string ChattingUserConnectionId { get; set; }
//public string HomeTown { get; set; }
//public DateTime? BirthDate { get; set; }
}
そして私のハブでは私はそのようなことをしています:
public class ChatHub : Hub
{
#region Data Members
private static ApplicationDbContext applicationDbContext = new ApplicationDbContext();
private static UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(applicationDbContext));
private static List<ApplicationUser> connectedUsers = new List<ApplicationUser>();
ユーザーがチャットに接続すると、ユーザー名でApplicationUserオブジェクトを取得し、connectedUsersリストに追加します。彼が切断すると、私は彼を削除します。
EFオブジェクトなどのランダムな例外に遭遇したため、静的オブジェクトに設定する代わりに、アクセスするたびにApplicationDbContextとUserManagerを作成しました。
private ApplicationUser GetCurrentUser()
{
ApplicationDbContext applicationDbContext = new ApplicationDbContext();
UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(applicationDbContext));
var userName = Context.User.Identity.GetUserName();
var user = userManager.FindByName<ApplicationUser>(userName);
return user;
}
編集:
ハブコードには、ユーザーの子オブジェクトの読み込みに問題があります。 asp.netテンプレートでも使用されるこのコードはより適切に機能し、ApplicationDBContextは必要ありません。
private ApplicationUserManager _userManager
{
get
{
return HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
var user = _userManager.FindByName<ApplicationUser, string>(userName);
ASP.NETのSignalRチュートリアルセクションに関する非常に優れた記事があります。以下の紹介段落を含めました。
SignalRユーザーの接続へのマッピング
ハブに接続する各クライアントは、一意の接続IDを渡します。この値は、ハブコンテキストのContext.ConnectionIdプロパティで取得できます。アプリケーションでユーザーを接続IDにマップし、そのマッピングを維持する必要がある場合は、次のいずれかを使用できます。
- ユーザーIDプロバイダー(SignalR 2)
- 辞書などのメモリ内ストレージ
- 各ユーザーのSignalRグループ
データベーステーブルやAzureテーブルストレージなどの永続的な外部ストレージこれらの各実装について、このトピックで説明します。 HubクラスのOnConnected、OnDisconnected、およびOnReconnectedメソッドを使用して、ユーザー接続ステータスを追跡します。
UserIDプロバイダー
ASP.NETの標準メンバーシッププロバイダー(IPrincipal.Identity.Name
)次の操作を行うだけで、ユーザーアカウントに基づいてクライアントにアクセスできます。独自のユーザーシステムがある場合は、独自のプロバイダーを作成できます。
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}