Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

436
Visualizações
ConcurrentDictionary.GetOrAdd - Agregar solo si no es nulo

Estoy usando ConcurrentDictionary para almacenar datos en caché con acceso paralelo y, a veces, los elementos nuevos se pueden almacenar en db y no se cargan en caché. Esta es la razón por la que uso GetOrAdd

 public User GetUser(int userId) { return _user.GetOrAdd(userId, GetUserFromDb); } private User GetUserFromDb(int userId) { var user = _unitOfWork.UserRepository.GetById(userId); // if user is null, it is stored to dictionary return user; }

Pero, ¿cómo puedo verificar si el usuario se obtuvo de db y almacenó el usuario en el diccionario solo si el usuario no es nulo?

Posiblemente pueda eliminar nulo de ConcurrentDictionary inmediatamente después de GetOrAdd, pero no parece seguro para subprocesos y no es una solución muy elegante. Insertar y quitar inútil del diccionario. ¿Tienes alguna idea de cómo hacerlo?

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

Aquí hay una solución hacky, espero que algo mejor sea posible. Haga GetUserFromDb lance si no se encuentra el usuario. Esto aborta el almacenamiento en el diccionario. Haga GetUser la excepción. Esto está usando excepciones para controlar el flujo, lo cual no es agradable.

over 4 years ago · Santiago Trujillo Relatório

0

public User GetUser(int userId) { var user = _user.GetOrAdd(userId, GetUserFromDb); if (user == null) _user.TryRemove(userId, out user); }

También puede envolver eso en un método de extensión:

 public static TValue GetOrAddIfNotNull<TKey, TValue>( this ConcurrentDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) where TValue : class { var value = dictionary.GetOrAdd(key, valueFactory); if (value == null) dictionary.TryRemove(key, out value); return value; }

Entonces su código se verá así:

 public User GetUser(int userId) { var user = _user.GetOrAddIfNotNull(userId, GetUserFromDb) }

ACTUALIZAR

Según el comentario de @usr, puede haber un caso en el que:

  1. El subproceso 1 ejecuta GetOrAdd , agrega null al diccionario y hace una pausa.
  2. El usuario se agrega a la base de datos.
  3. El subproceso 2 ejecuta GetOrAdd y recupera valores null del diccionario en lugar de acceder a la base de datos.
  4. El subproceso 1 y el subproceso 2 ejecutan TryRemove y eliminan el registro del diccionario.

Con este tiempo, el subproceso 2 se volverá null en lugar de acceder a la base de datos y obtener el registro del usuario. Si este caso extremo es importante para usted y aún desea usar ConcurrentDictionary , entonces puede usar lock en el método de extensión:

 public static class ConcurrentDictionaryExtensions { private static readonly object myLock = new object(); public static TValue GetOrAddIfNotNull<TKey, TValue>( this ConcurrentDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) where TValue : class { lock (myLock) { var value = dictionary.GetOrAdd(key, valueFactory); if (value == null) dictionary.TryRemove(key, out value); return value; } } }
over 4 years ago · Santiago Trujillo Relatório

0

Estoy ampliando la solución @NikolaiSamteladze para incluir el bloqueo verificado dos veces para que otros subprocesos puedan omitir la adquisición del bloqueo después de la actualización del diccionario.

 public static class ConcurrentDictionaryExtensions { private static readonly object myLock = new object(); public static TValue GetOrAddIfNotNull<TKey, TValue>( this ConcurrentDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) where TValue : class { TValue value; if (!dictionary.TryGetValue(key, out value)) { lock (myLock) { value = dictionary.GetOrAdd(key, valueFactory); if (value == null) dictionary.TryRemove(key, out value); } } return value; } }
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda