0

asp.net Web サイトにログインする組織があり、各メンバーがログインするときに、SignalR Hub OnConnected オーバーライドで OrganizationMembers という名前の静的 ConcurrentDictionary に ConnectionId と OrganizationId を追加します。これを行うのは、その組織 (SignalR グループ) にのみ関連するデータを含むページ コールバック関数にデータを送信できるようにするためです。OrganizationId は、データの送信先の SignalR グループを表します。

また、OnConnnected メソッドで、OrganizationId と OrganizationId.ToString() を、一意の組織 ID を表す別の ConcurrentDictionary に追加します。OrganizationId.ToString() を格納する理由については後で説明します。一意の組織 ID を保存して、メソッドを呼び出して何度もスリープするバックグラウンド タスクで、一意の組織 ID を列挙し、それに関連する各組織 (SignalR グループ) データのみを送信できるようにします。

OnDisconnected Hub オーバーライドでは、接続を削除した後、OrganizationMembers ConcurrentDictionary の OrganizationId 値をチェックして、その OrganizationId を持つメンバーが最後に切断されたかどうかを確認し、そうであれば UniqueOrganizations ディクショナリから削除します。私は辞書 Values.Contains() が O(n) であることを知っているので、これを避けたいと思います。

これは、タスクが UniqueOrganizations を列挙するときに、たとえば同じ組織の 5 人のメンバーがログインしたが、後ですべてのメンバーを閉じる場合に、不必要にデータを送信しようとしている組織 (SignalR グループ) が存在しないようにするためです。そのグループ データを SignalR コールバック経由で送信しようとはしません。

確かに私は SignalR Hub の内部の仕組みを知らないので、すべて切断されている組織 (SignalR グループ) のメンバーに不必要にデータを送信しようとしても問題にならない場合があります。

これは SignalR Hub を考えすぎですか? 最後の組織 (SignalR グループ) メンバーがハブから切断されたかどうかを判断する必要はなく、UniqueOrganizations から OrganizationId を削除する必要はありませんか?

これで問題ない場合、O(n) であるため、Values.Contains() 辞書を回避するにはどうすればよいですか?

// the key is the ConnectionId of an Organization Member
// the value is the OrganizationId of the Member
protected static ConcurrentDictionary<string, int> _organizationMembers;
public static ConcurrentDictionary<string, int> OrganizationMembers {
    get {
        if(_organizationMembers == null) {
            _organizationMembers = new ConcurrentDictionary<string, int>();
        }

        return _organizationMembers;
    }
}

// the key is the OrganizationId to send the specific data to
// the value is the OrganizationId converted to string
protected static ConcurrentDictionary<int, string> _uniqueOrganizations;
public static ConcurrentDictionary<int, string> UniqueOrganizations {
    get {
        if(_uniqueOrganizations == null) {
            _uniqueOrganizations = new ConcurrentDictionary<int, string>();
        }

        return _uniqueOrganizations;
    }
}


// Hub Code

public override Task OnConnected() {
    string connectionId = Context.ConnectionId;
    string organizationId = Context.Request.QueryString["organizationId"];
    int organizationIdValue = int.Parse(organizationId);

    OrganizationMembers.TryAdd(connectionId, organizationIdValue);
    UniqueOrganizations.TryAdd(organizationIdValue, organizationId);

    // the organizationId represents the SignalR group
    Groups.Add(connectionId, organizationId);
    return base.OnConnected();
}


public override Task OnDisconnected() {
    string organizationId = string.Empty;
    int organizationIdValue;
    string connectionId = Context.ConnectionId;

    OrganizationMembers.TryRemove(connectionId, out organizationIdValue);

    // if that happens to be the last Member connection to be removed
    // then remove the OrganizationId from the unique OrganizationIds
    // so it won't be a wasted enumeration and useless callback

    // I want to avoid this O(n) Contains()
    if(!OrganizationMembers.Values.Contains(organizationIdValue)) {
        UniqueOrganizations.TryRemove(organizationIdValue, out organizationId);
    }

    Groups.Remove(connectionId, organizationId);
    return base.OnDisconnected();
}


// Task code
foreach(int organizationIdValue in DataCache.UniqueOrganizations.Keys) {
// this is why I also stored the OrganizationId as a string so I wouldn't have to
// convert it to a string each time the dictionary is enumerated.
// I can just have the organizationId string ready to use as the SignalR Group.
    string organizationId = UniqueOrganizations[organizationIdValue];

    try {
        string organizationData = GetOrganizationData(organizationIdValue);
        _clients.Group(organizationId).sendToOrganizationData(organizationData);
    }
    catch(Exception ex) {
        _clients.Group(organizationId).sendToOrganizationError();
    }
}
4

1 に答える 1