Qué agregar para la parte de actualización en ConcurrentDictionary AddOrUpdate

109

Estoy tratando de volver a escribir un código usando el Diccionario para usar ConcurrentDictionary. He revisado algunos ejemplos, pero todavía tengo problemas para implementar la función AddOrUpdate. Este es el 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;

No sé qué agregar para la parte de actualización:

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

Cualquier indicador sería apreciada.

usuario438331
fuente

Respuestas:

220

Debe pasar un Funcque devuelve el valor que se almacenará en el diccionario en caso de una actualización. Supongo que en su caso (ya que no distingue entre agregar y actualizar) esto sería:

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

Es decir, Funcsiempre devuelve el sessionId, de modo que tanto Add como Update establecen el mismo valor.

Por cierto: hay una muestra en la página de MSDN .

M4N
fuente
4
Estaba luchando seriamente para encontrar una función para agregar o actualizar al mismo valor. thaks
Zapnologica
2
Buena respuesta. Solo a partir de la firma de AddOrUpdate () que se muestra en Visual Studio, solo puede adivinar el significado de los 2 parámetros. Sin embargo, en el caso específico sobre el que pregunta @ user438331, creo que la solución en mi respuesta usando un indexador simple es mejor.
Niklas Peter
7
Como señala @NiklasPeter ( stackoverflow.com/a/32796165/8479 ), es mejor que use el indexador normal para sobrescribir el valor, ya que en su caso no está interesado en el valor existente, si lo hay. Mucho más legible.
Rory
3
Recomiendo cambiar su respuesta para señalar a los usuarios la respuesta de @NiklasPeter. Es una solución mucho mejor.
Will Calderwood
63

Espero no haberme perdido nada en tu pregunta, pero ¿por qué no así? Es más fácil, atómico y seguro para subprocesos (ver más abajo).

userDic[authUser.UserId] = sessionId;

Almacene un par clave / valor en el diccionario incondicionalmente, sobrescribiendo cualquier valor para esa clave si la clave ya existe: use el definidor del indexador

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

El indexador también es atómico. Si pasa una función en su lugar, es posible que no sea:

Todas estas operaciones son atómicas y seguras para subprocesos con respecto a todas las demás operaciones en ConcurrentDictionary. La única advertencia sobre la atomicidad de cada operación es para aquellas que aceptan un delegado, a saber, AddOrUpdate y GetOrAdd. [...] estos delegados se invocan fuera de las cerraduras

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

Niklas Peter
fuente
2
Sí, atómico en el sentido de que sucede de una vez y no puede suceder ni ser interrumpido a medias. Sin embargo, no es seguro que alguien más pueda cambiarlo a otra cosa justo antes de que usted lo haga, en cuyo caso se pierde el cambio y usted no sabe que sucedió, si solo desea cambiarlo si el valor es el que espera, entonces esto no hará eso por ti.
trampster
26

Terminé implementando un método de extensión:

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
fuente
1

Para aquellos que estén interesados, actualmente estoy implementando un caso que es un gran ejemplo para usar el "oldValue" también conocido como valor existente en lugar de forzar uno nuevo (personalmente no me gusta el término "oldValue" ya que no es tan antiguo cuando se creó hace unos pocos pasos de procesador desde dentro de un hilo paralelo).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
Nicolas
fuente
6
si no desea cambiar el valor existente se debe utilizar GetOrAdd()en lugar msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory
1
Hm sí, tienes razón, GetOrAdd () es más simple y suficiente en este caso, ¡gracias por esta pista!
Nicolas