ContainsKey为字符串返回false

时间:2018-12-17 04:20:34

标签: c# .net

我基本上是在尝试将一些组织及其ID和名称缓存在字典中。我不断收到错误消息:Item with Same Key has already been added。这对我来说很奇怪,因为第一个if块会检查字典是否包含键,如果存在则退出该方法。

在下面的(简化)代码下,为什么会出现错误?

    public async Task<IList<Stat>> GetStats(DateTime start, DateTime end, string role, long tenantId)
    {
    ...
        var tasks = new List<Task>();
        foreach (var org in query)
            tasks.Add(GetOrgName(org));

        await Task.WhenAll(tasks);

        return query;

    }

    private Dictionary<string, string> Orgs = new Dictionary<string, string>();

    private async Task GetOrgName(Organization org)
    {
        if (Orgs.ContainsKey(org.Id))
        {
            org.Name = Orgs[org.Id];
            return;
        }

        var result = await _directoryService.GetOrganization(org.Id);

        if (result != null)
            org.Name = result.DisplayName;
        else
            org.Name = org.Id;

        Orgs.Add(org.Id, org.Name);
    }

5 个答案:

答案 0 :(得分:2)

此代码看起来很危险

    if (org != null)
        org.Name = org.DisplayName;
    else
        org.Name = org.Id;

如果org == null我认为您的else子句将引发NullReference异常。但是你不明白

此功能可能存在问题

    var org = await _directoryService.GetOrganization(org.Id);

请注意,此命令意味着org可以被修改,并且可能不包含其原始的Id

例如:如果它从不返回空值,则只是简单的空白对象,您可能会遇到Item with Same Key has already been added异常

我认为可以通过仔细检查来解决

    if (!Orgs.ContainsKey(org.Id))
    {
        Orgs.Add(org.Id, org.Name);
    }

答案 1 :(得分:2)

所以问题在于字典可以同时由多个任务访问。因此,在一项任务正在检查名称是否存在的同时,另一项正在添加名称。另外,由于字典不是线程安全的,因此您在当前的实现中可能会遇到随机异常。

如果您更改此设置:

var tasks = new List<Task>();
foreach (var org in query)
    tasks.Add(GetOrgName(org))

收件人:

foreach (var org in query)
   await GetOrgName(org);

您不会遇到这个问题。

另一种选择是对缓存使用线程安全集合。 但是,如果查询包含1000条内容,则您的代码可能会同时对_directoryService.GetOrganization进行1000次调用。可能是个问题?

答案 2 :(得分:1)

由于字典由多个线程访问,因此应使用ConcurrentDictionary而不是Dictionary。否则可能会发生,两个线程试图同时添加同一密钥,从而导致您提到的异常。此外,字典不是线程安全的,如果由多个线程访问,则字典可能会损坏。

答案 3 :(得分:1)

在您进行最新编辑时,这似乎不是多线程问题,而仅仅是多个异步调用的问题-您很可能正在等待并针对同一GetOrganizationorg.Id进行多次查询当第一个尚未完成时(因此词典尚未更新)。 因此,您很可能不需要ConcurrentDictionary。您可以改为执行以下操作:

private Dictionary<string, Task> Orgs = new Dictionary<string, Task>();

private Task GetOrgName(Organization org)
{
    Task nameTask;
    if (!Orgs.TryGetValue(org.Id, out nameTask)
    {
        nameTask = GetOrganization(.org);
        Orgs.Add(org.Id, nameTask);
    }

    return nameTask;
}

private async Task GetOrganization(Organization org)
{
    // Consider using .ConfigureAwait(false) here...
    var result = await _directoryService.GetOrganization(org.Id);
    if (result != null)
        org.Name = result.DisplayName;
    else
        org.Name = org.Id;
}

注意Dictionary的签名。

答案 4 :(得分:0)

使字典搜索不区分大小写。您可以参考以下内容:

private async Task GetOrgName(Organization org)
{
    if (Orgs.ContainsKey(org.Id.ToUpper()))
    {
        org.Name = Orgs[org.Id.ToUpper()];
        return;
    }

    var result = await _directoryService.GetOrganization(org.Id.ToUpper());

    if (result != null)
        org.Name = result.DisplayName;
    else
        org.Name = org.Id;

    Orgs.Add(org.Id.ToUpper(), org.Name);
}