O que adicionar para a parte de atualização em ConcurrentDictionary AddOrUpdate

109

Estou tentando reescrever algum código usando o Dicionário para usar o ConcurrentDictionary. Eu revisei alguns exemplos, mas ainda estou tendo problemas para implementar a função AddOrUpdate. Este é o código original:

    dynamic a = HttpContext;
    Dictionary<int, string> userDic = this.HttpContext.Application["UserSessionList"] as Dictionary<int, String>;

   if (userDic != null)
   {
      if (useDic.ContainsKey(authUser.UserId))
      {
        userDic.Remove(authUser.UserId);
      }
   }
  else
  {
     userDic = new Dictionary<int,string>();
  }
  userDic.Add(authUser.UserId, a.Session.SessionID.ToString());
  this.HttpContext.Application["UserDic"] = userDic;

Não sei o que adicionar para a parte de atualização:

userDic.AddOrUpdate(authUser.UserId,
                    a.Session.SessionID.ToString(),
                    /*** what to add here? ***/);

Quaisquer sugestões serão apreciadas.

user438331
fonte

Respostas:

220

Você precisa passar um Funcque retorna o valor a ser armazenado no dicionário em caso de atualização. Acho que no seu caso (já que você não faz distinção entre adicionar e atualizar) isso seria:

var sessionId = a.Session.SessionID.ToString();
userDic.AddOrUpdate(
  authUser.UserId,
  sessionId,
  (key, oldValue) => sessionId);

Ou seja, Funcsempre retorna o sessionId, de forma que Add e Update definem o mesmo valor.

BTW: há um exemplo na página do MSDN .

M4N
fonte
4
Eu estava lutando seriamente para apenas encontrar uma função para adicionar ou atualizar para o mesmo valor. thaks
Zapnologica
2
Boa resposta. Apenas a partir da assinatura de AddOrUpdate () exibida no Visual Studio, você só pode adivinhar o significado dos 2 parâmetros. No entanto, no caso específico, que @ user438331 pergunta, acho que a solução em minha resposta usando um indexador simples é melhor.
Niklas Peter
7
Como @NiklasPeter aponta ( stackoverflow.com/a/32796165/8479 ), é melhor usar apenas o indexador normal para sobrescrever o valor, pois no seu caso você não está interessado no valor existente, se houver. Muito mais legível.
Rory
3
Eu recomendo mudar sua resposta para apontar aos usuários a resposta de @NiklasPeter. É uma solução muito melhor.
Will Calderwood
63

Espero não ter perdido nada na sua pergunta, mas por que não assim? É mais fácil, atômico e thread-safe (veja abaixo).

userDic[authUser.UserId] = sessionId;

Armazene um par de chave / valor no dicionário incondicionalmente, sobrescrevendo qualquer valor para aquela chave se a chave já existir: Use o configurador do indexador

(Consulte: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx )

O indexador também é atômico. Se você passar uma função, pode não ser:

Todas essas operações são atômicas e seguras com thread em relação a todas as outras operações no ConcurrentDictionary. A única advertência quanto à atomicidade de cada operação é para aquelas que aceitam um delegado, ou seja, AddOrUpdate e GetOrAdd. [...] esses delegados são invocados fora das fechaduras

Veja: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx

Niklas Peter
fonte
2
Sim atômico porque acontece de uma só vez e não pode acontecer nem ser interrompido. No entanto, não é seguro porque outra pessoa pode alterá-lo para outra coisa um pouco antes de você, caso em que a alteração é perdida, e você não sabe o que aconteceu, se quiser apenas alterá-lo se o valor for o que você esperava então isso não vai fazer isso por você.
trampster de
26

Acabei implementando um método de extensão:

static class ExtensionMethods
{
    // Either Add or overwrite
    public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
    {
        dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
    }
}
Steve Cook
fonte
1

Para aqueles que estão interessados ​​em, atualmente estou implementando um caso que é um ótimo exemplo para usar o valor "oldValue" também conhecido como valor existente em vez de forçar um novo (pessoalmente, não gosto do termo "oldValue" porque não é esse antigo quando foi criado há apenas alguns cliques do processador em um thread paralelo).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
Nicolas
fonte
6
se você não quiser alterar o valor existente você deve usar GetOrAdd()em vez msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory
1
Hm sim, você está certo, GetOrAdd () é mais simples e suficiente neste caso - obrigado por esta dica!
Nicolas