Edit: most of the time it works fine the error is exceptional.

On the line where this method is called the error report says it threw a KeyNotFoundException" im mapping an enum to a string (!im not updating the dictionary in any way its filled while getting declared!):

public Task<bool> UpdateStatusAsync(Some object, Status status)
{
    var somerequest = new Somerequest 
    {
        ...
        Status = MapDomainStatusToAclStatus(status.Type),
        ...
    };

    .............
}

Here is the mapping function:

private string MapDomainStatusToAclStatus(DomainStatus domainStatus)
{
    if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
    {
        return DictionaryDomainACLStatus[domainStatus];
    }

    // Some refactor todo comment.
    switch (domainStatus)
    {
        case DomainStatus.Aborted:
            return "Some string";
    }

    Log.Info($"[MapDomainStatusToAclStatus] DomainStatus={domainStatus} cant be mapped to ACL status");
    return String.Empty;
}

Is that even possible?

Edit:

Since i got some replies about possible race condition I would like to add that the dictionary is declared like this:

 public static readonly Dictionary<DomainStatus, string> { values }

Edit2: My dictionary declaration:

public static readonly Dictionary<DomainStatus, string> DictionaryDomainACLStatus= new Dictionary<DomainStatus, string>
    {
            {DomainStatus.Approved, "TEXT" },
            {DomainStatus.Declined, "TEXT2" }
    };

No create update delete operations occur later on in the code.

3

There are 3 best solutions below

1
Darren Lewis On

Your static dictionary is shared across all threads and therefore not thread safe. Review if it really needs to be static or look at the ConcurrentDictionary which is thread safe.

1
Sefe On

Your problem is that you are within concurrent code, which means that multiple threads of code may run at the same time. So you have to make sure that concurrent operations are atomic, i.e. that that the operation is always executed as one step in a multi-threaded environment and another thread can not interrupt it.

The code...

if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
{
    return DictionaryDomainACLStatus[domainStatus];
}

...is clearly not atomic, because it consists of two separate steps. Between ContainsKey and the indexer, another thread can modify the dictionary, which leads to your KeyNotFoundException if the modification is a Remove.

The first thought coming to mind would be to replace these two steps by...

if (DictionaryDomainACLStatus.TryGetValue(domainStatus, out string value)) {
    return value;
}

This will not help you though, since TryGetValue is in itself not atomic, which means it can be interrupted by another thread internally.

A common solution to this problem is to introduce a critical section with a lock:

lock (myCriticalSection) {
    if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
    {
        return DictionaryDomainACLStatus[domainStatus];
    }
}

Any access of the dictionary has to be in the performed within a lock of the same critical section.

Another option would be to use a ConcurrentDictionary, which provides atomic operations. This will only work though if all accesses to the dictionary can be performed in one (atomic) step. That's why usually you will be better off with a lock.

0
Viking On

I found out the error lies in another piece of code and I overlooked it. Thank you all for thinking along with me. I will definately be more precise while debugging.