2016-05-24 4 views
2

J'essaie de convertir une partie de la logique utilisée dans de nombreux endroits en une expression qui peut être réutilisée, centralisée et exécutée dans le contexte de SQL Serveur.Comment créer une expression sur une collection enfant dans Entity Framework Code First

Je peux faire cela lors de l'interrogation des tables directement sur le contexte EF en leur passant une expression. Le problème que j'ai est d'essayer de le faire sur les collections enfants de la table car elles sont exprimées comme ICollection dans le code EF First.

Un exemple de table simple défini dans Code First;

public class Table 
{ 
    public int TableId { get; set; } 
    public virtual ICollection<ChildTable> Children { get; set; } 
} 

Comment j'interroge et comment j'aimerais l'interroger;

var records = context 
    .Table 
    .Select(table => new 
    { 
     ChildRecordCount = table.Children.Count(child => !child.IsArchived), // This works. 
     AltChildRecordCount = table.Children.Count(HowToExpressThisInCSharp()),// Cannot fathom how to do this. 
    }); 

J'ai essayé différentes méthodes (voir ci-dessous) qui renvoient un Func, mais ils provoquent EF à l'échec que j'attendre à (SQL ne sait rien de ma méthode C#). Alors, comment la méthode en ligne plus simple est-elle traduite en arbre d'expression (je peux voir qu'elle est exécutée sur SQL Server)?

static Func<Menu, bool> HowToExpressThisInCSharp() 
    { 
     return x => !x.IsArchived; 
    } 
+1

Le problème et une solution possible sont décrites ici http://www.albahari.com/nutshell/linqkit.aspx –

+0

@IvanStoev merci, j'oublièrent LinqKit et il semble idéal. Pouvez-vous ajouter votre commentaire comme réponse afin que je puisse le marquer comme étant répondu? –

Répondre

1

Le problème est que l'expression en question fait partie d'un arbre d'expression (table => new { ... }) et est juste « enregistré » à l'intérieur, ce qui provoque exception non pris en charge lors de l'exécution. En général, LINQ to Entities n'aime pas la composabilité de l'expression et vous oblige à utiliser des expressions "plates" résolues au moment de la compilation. Le problème est abordé dans LINQKit avec Compile/Expand (et plus généralement AsExpandable). L'appliquer à votre scénario serait quelque chose comme ceci:

static Expression<Func<Menu, bool>> HowToExpressThisInCSharp() 
{ 
    return x => !x.IsArchived; 
} 

var criteria = HowToExpressThisInCSharp(); 
var records = context 
    .Table 
    .AsExpandable() 
    .Select(table => new 
    { 
     ChildRecordCount = table.Children.Count(child => !child.IsArchived), 
     AltChildRecordCount = table.Children.Count(criteria.Compile()), 
    }); 
3

Utilisez la méthode d'extension Queryable.AsQueryable. C'est exactement pour votre cas d'utilisation.

static Expression<Func<Menu, bool>> HowToExpressThisInCSharp() 
{ 
    return x => !x.IsArchived; 
} 

et

var records = context 
.Table 
.Select(table => new 
{ 
    ChildRecordCount = table.Children.Count(child => !child.IsArchived), // This works. 
    AltChildRecordCount = table.Children.AsQueryable().Count(HowToExpressThisInCSharp()), // this works too :) 
});