ユーザーにリアルタイムの通知を送信できるFacebookなどのWebアプリケーションについて考えてみます。
ユーザーが切断したり、後で再接続したりした場合でも、 asp.net SignalRを使用して、どの接続IDがどのユーザーに属しているかを追跡するための最良の方法は何ですか?
ユーザーにリアルタイムの通知を送信できるFacebookなどのWebアプリケーションについて考えてみます。
ユーザーが切断したり、後で再接続したりした場合でも、 asp.net SignalRを使用して、どの接続IDがどのユーザーに属しているかを追跡するための最良の方法は何ですか?
次のブログ投稿を確認してください。
ASP.NETSignalR接続の実際のアプリケーションユーザーへのマッピング
簡単に言うと、メソッドのユーザーに接続IDを追加し、OnConnected
メソッドのその接続を削除します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);
}
}