2011-02-15 7 views
4

j'ai mis en place un référentiel générique pour Entity Framework 4. Voici une version abêtis, où AllAppContainer est le contexte de l'objet EF4:Entity Framework, Motif de dépôt générique et étrange génération SQL

public class Repository<T> where T : class 
{ 
    protected AllAppContainer objectContext; 
    protected ObjectSet<T> entitySet; 

    public Repository() 
    { 
     objectContext = new AllAppContainer(); 
     entitySet = objectContext.CreateObjectSet<T>(); 
    } 

    public int QueryCount(Func<T, bool> predicate) 
    { 
     int queryCount = entitySet.Count(predicate); 
     return queryCount; 
    } 
} 

La seule méthode est QueryCount(), que je veux agir comme un sélectionnez Count (*) ... où ligne de SQL (ne pas retourner les enregistrements réels).

Droit devant? Vous penseriez ... Tout d'abord, nous allons faire une version non-dépôt de la même chose, d'effectuer un comptage sur les entités de l'article:

AllAppContainer allAppContainer = new AllAppContainer(); 
int nonRepCount = allAppContainer.Items.Count(item => item.Id > 0); 

SQL Server Profiler dit le SQL généré est:

SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Items] AS [Extent1] 
    WHERE [Extent1].[Id] > 0 
) AS [GroupBy1] 

Woo-hoo! But!

Maintenant, nous allons appeler la même chose en utilisant mon référentiel QueryCount:

Repository<Item> repository = new Repository<Item>(); 
int repCount = repository.QueryCount(item => item.Id > 0); 

Voici le SQL généré:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[SmallField] AS [SmallField] 
FROM [dbo].[Items] AS [Extent1] 

Oui, EF retourne l'ensemble des données, puis en appelant Count() sur elle en mémoire.

Pour le plaisir que j'ai essayé de changer la ligne correspondante dans le référentiel QueryCount à:

int queryCount = new AllAppContainer().CreateObjectSet<T>().Count(predicate); 

et la ligne non-dépôt à:

int nonRepCount = allAppContainer1.CreateObjectSet<Item>().Count(item => item.Id > 0); 

mais le SQL généré pour chacun d'eux est le même que précédemment .

Maintenant, pourquoi tout ce dépôt-retourne-tout-correspondance-enregistrements-puis-comptes se passe, quand ce n'est pas pour le non-dépôt? Et est-il possible de faire ce que je veux via mon dépôt générique, c'est-à-dire compter à DB. Je ne peux pas prendre le coup de performance du compte en mémoire.

Répondre

7

vous devez utiliser Expression<Func<TSource, bool>> predicate pour votre Count sinon le cadre utilise l'Enumerable.Count<TSource> Method (IEnumerable<TSource>, Func<TSource, Boolean>) qui obtient toute la collection de DB pour pouvoir appeler pour chaque élément, de sorte que votre méthode doit être:

public int QueryCount(Expression<Func<T, Boolean>> predicate) 
{ 
    int queryCount = entitySet.Count(predicate); 
    return queryCount; 
} 
+1

+1 voir aussi http://stackoverflow.com/questions/4855399 et http://stackoverflow.com/questions/2675536 - Je dirais presque que c'est un bug. –

+0

Wow, je ne suis pas au travail maintenant, mais je vais courir demain pour vérifier cela. Merci pour la réponse rapide K. Et merci pour les pointeurs BlueRaja - la chose frustrante était de ne pas connaître les termes à rechercher sur ce problème: le mien et les deux autres questions ont des titres/sujets complètement différents, mais apparemment proviennent de la même chose. – user603563

+2

@BlueRaja: Que voulez-vous appeler un bug? Ne pas entièrement comprendre la différence entre IEnumerable et IQueryable n'est pas un bug. C'est une connaissance clé que vous devez avoir pour utiliser correctement Linq. –

Questions connexes