2013-02-18 7 views
1

J'utilise généralement un référentiel générique pour mettre mes requêtes EF en attente, donc je dois écrire du code limité et utiliser également la mise en cache. Le code source du référentiel peut être trouvé here.Combinaison Inclut avec Entity Framework

La requête backbone dans le code est celle ci-dessous. FromCache<T>() est une méthode d'extension IEnumerable<T> qui utilise HttpContext.Cache pour stocker la requête en utilisant une représentation stringifiée de l'expression lambda en tant que clé.

public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null) 
    where T : class, new() 
    { 
     // Check for a filtering expression and pull all if not. 
     if (expression == null) 
     { 
      return this.context.Set<T>() 
         .AsNoTracking() 
         .FromCache<T>(null) 
         .AsQueryable(); 
     } 

     return this.context.Set<T>() 
          .AsNoTracking<T>() 
          .Where<T>(expression) 
          .FromCache<T>(expression) 
          .AsQueryable<T>(); 
    } 

Alors que tout cela fonctionne, il est soumis au problème N + 1 pour les tables connexes car si je devais écrire une requête comme ceci:

var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false) 
           .Include(p => p.Author); 

Le Include() aura aucun effet sur ma requête car il a déjà été exécuté pour être mis en cache. Maintenant, je sais que je peux forcer Entity Framework à utiliser le chargement passionné dans mon modèle en supprimant le préfixe virtuel sur mes propriétés de navigation, mais cela me semble être le mauvais endroit pour le faire car vous ne pouvez pas prédire les types de requêtes va faire. Pour moi, cela ressemble à quelque chose que je ferais dans une classe de contrôleur. Qu'est-ce que je me demande est de savoir si je peux passer une liste d'inclus dans ma méthode Any<T>() que je pourrais alors parcourir quand je fais l'appel?

Répondre

1

ofDid-vous dire quelque chose comme ...

IQueryable<T> AnyWithInclude<T,I>(Expression<Func<T,bool>> predicate, 
            Expression<Func<T,I>> includeInfo) 
{ 
    return DbSet<T>.where(predicate).include(includeInfo); 
} 

l'appel

Context.YourDbSetReference.AnyWithInclude(t => t.Id==someId, i => i.someNavProp); 

En réponse à la question supplémentaire sur la collecte.

Je me suis rendu compte tard, il y avait une surcharge sur la propriété. Vous pouvez simplement passer une chaîne

Cela peut fonctionner mais appeler n'est pas facile. Eh bien, je le trouve dur.

IQueryable<T> GetListWithInclude<I>(Expression<Func<T, bool>> predicate, 
             params Expression<Func<T, I>>[] IncludeCollection); 

donc j'ai essayé

public virtual IQueryable<T> GetListWithInclude(Expression<Func<T, bool>> predicate, 
               List<string> includeCollection) 
    { var result = EntityDbSet.Where(predicate); 
     foreach (var incl in includeCollection) 
     { 
      result = result.Include(incl); 
     } 
     return result; 
    } 

et appelé avec

var ic = new List<string>(); 
ic.Add("Membership"); 
var res = context.DbSte<T>.GetListWithInclude(t=>t.UserName =="fred", ic); 

travaillé comme auparavant.

+0

Fantastique! Cela ressemble tellement à ce que je cherche. Merci! –

+0

btw j'utilise quelque chose de similaire pour les projections ;-) –

+0

Pourriez-vous développer votre réponse pour correspondre plus étroitement à la méthode que j'utilise actuellement? Je me perds quelque peu à traduire. Aussi serait-il possible de passer une collection de includes comme argument? –

0

Dans un souci de clarté, j'ajoute la solution que j'ai trouvée en me basant sur la réponse de @ soadyp.

public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null, 
         params Expression<Func<T, object>>[] includeCollection) 
    where T : class, new() 
{ 
    IQueryable<T> query = this.context.Set<T>().AsNoTracking().AsQueryable<T>(); 

    if (includeCollection.Any()) 
    { 
     query = includeCollection.Aggregate(query, 
        (current, include) => current.Include(include)); 
    } 

    // Check for a filtering expression and pull all if not. 
    if (expression != null) 
    { 
     query = query.Where<T>(expression); 
    } 

    return query.FromCache<T>(expression, includeCollection) 
       .AsQueryable<T>(); 
} 

Utilisation:

// The second, third, fourth etc parameters are the strongly typed includes. 
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false, 
              p => p.Author);