2010-08-19 4 views
1

J'ai 2 requêtes similaires:Comment mettre en cache 'IQueryable <>. First' résultat?

ICmOption optionRes = CmOptionRepository<ICmOption> 
      .GetAll() 
      .Where(option => option.Name == strCommandName && option.Data == strCommandOption) 
      .FirstOrDefault() 
      ; 

IErrorType errorType = ErrorTypeRepository<IErrorType> 
        .GetAll() 
        .Where(et => et.ComponentId == (int)component.Id && et.ComponentErrorCode == strErrorCode) 
        .First() 
        ; 

Dans les deux cas, les données constantes de DB sont extraites. Pour cette raison, je veux mettre en cache les résultats de ces requêtes ...

la solution la plus simple pour une demande est:

public IErrorType GetErrorType(IComponent component, string strErrorCode) 
    { 
     IErrorType errorType; 

     string strKey = string.Concat(component.Id, "_", strErrorCode); 
     lock (Dict) 
     { 
      if (Dict.ContainsKey(strKey)) 
      { 
       errorType = Dict[strKey]; 
      } 
      else 
      { 
       errorType = Repository 
        .GetAll() 
        .Where(et => et.ComponentId == (int)component.Id && et.ComponentErrorCode == strErrorCode) 
        .First() 
        ; 
       Dict.Add(strKey, errorType); 
      } 
     } 
     return errorType; 
    } 

    private static Dictionary<string, IErrorType> Dict { get { return _dict; } } 

    private static readonly Dictionary<string, IErrorType> _dict 
     = new Dictionary<string, IErrorType>(); 

je besoin de la même entité pour la 2e, et quelques autres sont à venir. .. Donc je veux créer une classe (CachableRepository) qui acceptera les paramètres, vérifier si l'objet pour eux est déjà mis en cache, sinon - obtenir des données de DB et mettre en cache. Le problème est: Je ne vois pas comment créer une clé pour le cache pour différents paramètres, et comment créer une fonction lambda pour ces paramètres. ...

Si vous avez des idées ou des suggestions, partagez-les.

Merci beaucoup!

Répondre

1

ma propre solution 'rapide':

internal class CacheManager<TIEntity> 
    where TIEntity : IEntity 
{ 
    internal TIEntity GetObject(string strKey, Func<TIEntity> funcGetEntity) 
    { 
     TIEntity entity; 
     lock (Dict) 
     { 
      if (Dict.ContainsKey(strKey)) 
      { 
       entity = Dict[strKey]; 
      } 
      else 
      { 
       entity = funcGetEntity(); 
       Dict.Add(strKey, entity); 
      } 
     } 
     return entity; 
    } 

    private Dictionary<string, TIEntity> Dict { [DebuggerStepThrough] get { return _dict; } } 

    private readonly Dictionary<string, TIEntity> _dict = new Dictionary<string, TIEntity>(); 
} 


    public IErrorType GetErrorType(IComponent component, string strErrorCode) 
    { 
     string strKey = string.Concat(component.Id, "_", strErrorCode); 
     IErrorType errorType = _sCacheManager.GetObject(
      strKey, 
      () => Repository 
       .GetAll() 
       .Where(et => et.ComponentId == (int)component.Id && et.ComponentErrorCode == strErrorCode) 
       .First() 
      ); 
     return errorType; 
    } 

    private static CacheManager<IErrorType> _sCacheManager = new CacheManager<IErrorType>(); 

S'il vous plaît laissez-moi savoir si vous voyez une meilleure option.

Merci beaucoup!

+1

Cela devrait faire, mais je voudrais éviter le blocage lors de la lecture du dictionnaire. Vous pouvez essayer de lire à partir du dictionnaire - si échoue, puis acquérir le verrou, puis essayez de relire. Une autre suggestion consiste à utiliser la méthode TryGet plutôt que ContainsKey. Actuellement, vous effectuez deux recherches dans le dictionnaire par lecture de cache. – VinayC

+0

Eh bien, l'implémentation de TryGetValue fait un this.FindEntry (clé) d'abord, puis une valeur = this.entries [index] .value si trouvé, donc, fondamentalement, c'est aussi 2 consultations. – murki

1

J'utilise cette méthode un peu partout pour gérer les objets de mise en cache dans le cache ASP.NET, il pourrait être modifié pour mettre en cache dans un dictionnaire à la place.

public static T GetOrInsert<T>(string cacheKey, Func<T> creator) 
{ 
    object cacheItem = HttpRuntime.Cache.Get(cacheKey); 
    if (cacheItem is T) 
    { 
     return (T)cacheItem; 
    } 
    else 
    { 
     T newItem = creator(); 
     HttpRuntime.Cache.Insert(cacheKey, newItem); 

     return newItem; 
    } 
} 

Vous pouvez alors l'utiliser comme

public IErrorType GetErrorType(IComponent component, string strErrorCode) 
{ 
    string strKey = string.Concat(component.Id, "_", strErrorCode); 
    return CacheUtil.GetOrInsert<IErrorType>(strKey, 
      () => Repository 
       .GetAll() 
       .Where(et => et.ComponentId == (int)component.Id && et.ComponentErrorCode == strErrorCode) 
       .First() 
    ); 
} 
+0

Couple d'améliorations dans votre solution: 1) en utilisant T cacheItem = HttpRuntime.Cache.Get (cacheKey) en tant que T - nécessite une contrainte T: Class though. 2) modifier le cacheKey dans la méthode GetOrInsert pour y inclure le nom de type T sinon vous aurez des collisions si la même clé est utilisée sur deux types. – VinayC

+0

@VinayC Je ne suis pas sûr si l'utilisation de 'comme T' gagne vraiment quelque chose ici, bien que vous ayez raison d'utiliser' is' pour que T soit un type de valeur. (2) est un bon point, mes clés de cache incluent normalement le nom de la méthode/classe d'appel pour éviter les collisions. – stevemegson

+0

'en tant que T' permettra d'économiser un type de vérification compatible qui se produit dans votre cas - l'un avec l'opérateur est et l'autre avec l'opérateur de casting. Comme la syntaxe T ferait type compatible check & casting ensemble. – VinayC

Questions connexes