简体   繁体   English

ContainsKey为字符串返回false

[英]ContainsKey Returns false for string

I'm basically attempting to cache some organizations in a dictionary with their id and name. 我基本上是在尝试将一些组织及其ID和名称缓存在字典中。 I keep getting an error: Item with Same Key has already been added . 我不断收到错误消息: Item with Same Key has already been added This is strange to me since the first if block checks if the dictionary contains the key and exits the method if it does. 这对我来说很奇怪,因为第一个if块会检查字典是否包含键,如果存在则退出该方法。

Why am I getting the error given the (simplified) code below? 给定下面的(简化)代码,为什么会出现错误?

    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);
    }

This code looks dangerous 该代码看起来很危险

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

If org == null I think your else clause will fire NullReference exception. 如果org == null我认为您的else子句将触发NullReference异常。 But you don't get it 但是你不明白

Problem may be in this function 问题可能出在此功能上

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

Note that this command means org can be modified and It may not contain its original Id 请注意,此命令意味着可以修改org ,并且可能不包含其原始Id

For example: If this never returns null value, just simple blank object You may get Item with Same Key has already been added exception 例如:如果此方法从不返回空值,则仅是简单的空白对象您可能已获得Item with Same Key has already been added异常

I think this can be fixed by double check 我认为可以通过双重检查来解决

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

So the problem is that the dictionary is accessed by multiple tasks at the same time. 因此,问题在于字典可以同时由多个任务访问。 So while one task is checking if the name exists another is adding it. 因此,在一项任务正在检查名称是否存在的同时,另一项正在添加名称。 Also since dictionary is not thread safe you might run into random exceptions with your current implementation. 另外,由于字典不是线程安全的,因此您当前的实现可能会遇到随机异常。

If you change this: 如果您更改此设置:

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

to: 至:

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

You wont have that problem. 你不会有那个问题。

Another option would be to use a thread safe collection for the cache. 另一种选择是对缓存使用线程安全集合。 But if query contains lets say 1000 items, you code might make a thousand concurrent calls to _directoryService.GetOrganization . 但是,如果查询包含1000个项目,那么您的代码可能会同时调用_directoryService.GetOrganization Which might be a problem? 哪个可能是个问题?

Since the dictionary is accessed by multiple threads, you should use ConcurrentDictionary instead of Dictionary. 由于字典由多个线程访问,因此应使用ConcurrentDictionary而不是Dictionary。 Otherwise it can happen, that two threads are trying to add the same key at the same time, leading to the exception you mentioned. 否则可能会发生,两个线程试图同时添加同一密钥,从而导致您提到的异常。 Moreover, Dictionary is not thread-safe and can be corrupted if accessed by mutiple threads. 此外,字典不是线程安全的,如果由多个线程访问,则字典可能会损坏。

With your latest edit, looks like it's not a multi-threaded problem but just a problem with multiple async calls - you're most likely awaiting and doing multiple queries to GetOrganization for the same org.Id when the first one hasn't finished yet (so the dictionary has not been updated yet). 在您的最新编辑中,这似乎不是多线程问题,而仅仅是多个异步调用的问题-当第一个尚未完成时,您很可能正在等待并针对同一个org.IdGetOrganization进行多个查询。 (因此词典尚未更新)。 So you most likely don't need ConcurrentDictionary . 因此,您很可能不需要ConcurrentDictionary You can do something like the following instead: 您可以改为执行以下操作:

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;
}

Note the signature for the Dictionary . 注意Dictionary的签名。

Make dictionary searching and adding to case insensitive. 使字典搜索和添加到大小写不敏感。 You can refer following: 您可以参考以下内容:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM