web-dev-qa-db-fra.com

Comment utiliser les prédicats dans LINQ to Entities pour les objets Entity Framework

J'utilise LINQ to Entities pour les objets Entity Framework dans ma couche d'accès aux données.

Mon objectif est de filtrer autant que possible la base de données, sans appliquer de logique de filtrage aux résultats en mémoire.

Pour cela, Business Logic Layer transmet un prédicat à Data Access Layer.

Je veux dire

Func<MyEntity, bool>

Donc, si j'utilise ce prédicat directement, comme

public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
    return qry = _Context.MyEntities.Where(x => isMatched(x));
}

Je reçois l'exception

[System.NotSupportedException] --- {"Le type de nœud d'expression LINQ 'Invoke' n'est pas pris en charge dans LINQ to Entities."}

Solution dans cette question suggère d'utiliser la méthode AsExpandable () de ( LINQKit bibliothèque.

Mais encore une fois, en utilisant

public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
    return qry = _Context.MyEntities.AsExpandable().Where(x => isMatched(x));
}

Je reçois l'exception

Impossible de convertir un objet de type 'System.Linq.Expressions.FieldExpression' en type 'System.Linq.Expressions.LambdaExpression'

Existe-t-il un moyen d'utiliser le prédicat dans la requête LINQ to Entities pour les objets Entity Framework, afin qu'il soit correctement transformé en une instruction SQL.

Je vous remercie.

22
bairog

Vous n'avez pas besoin de LinqKit pour cela. N'oubliez pas d'utiliser

Expression<Func<MyEntity, bool>>

au lieu de

Func<MyEntity, bool>

Quelque chose comme ça:

public IQueryable<MyEntity> GetAllMatchedEntities(Expression<Func<MyEntity, Boolean>> predicate)
{
    return _Context.MyEntities.Where(predicate);
}

Vous devez utiliser Expression car Linq to Entities doit traduire votre lambda en SQL.

Lorsque vous utilisez Func, votre lambda est compilé en IL, mais lorsque vous utilisez Expression, c'est un arbre d'expression que Linq to Entities peut traverser et convertir.

Cela fonctionne avec les expressions que Linq to Entities comprend.

S'il échoue, votre expression fait quelque chose que Linq to Entities ne peut pas traduire en SQL. Dans ce cas, je ne pense pas que LinqKit vous aidera.

Éditer:

Aucune conversion n'est nécessaire. Définissez simplement la méthode GetAllMatchedEntities avec un paramètre Expression et utilisez-la de la même manière qu'avec un paramètre Func. Le compilateur fait le reste.

Il existe trois façons d'utiliser GetAllMatchedEntities.

1) Avec une expression lambda en ligne:

this.GetAllMatchedEntities(x => x.Age > 18)

2) Définissez votre expression comme un champ (peut également être une variable)

private readonly Expression<Func<MyEntity, bool>> IsMatch = x => x.Age > 18;
...then use it
this.GetAllMatchedEntities(IsMatch)

3) Vous pouvez créer votre expression manuellement. La réduction est plus de code et vous manquez les vérifications de compilation.

public Expression<Func<MyEntity, bool>>  IsMatchedExpression()
{
    var parameterExpression = Expression.Parameter(typeof (MyEntity));
    var propertyOrField = Expression.PropertyOrField(parameterExpression, "Age");
    var binaryExpression = Expression.GreaterThan(propertyOrField, Expression.Constant(18));
    return Expression.Lambda<Func<MyEntity, bool>>(binaryExpression, parameterExpression);
}
44
andres.chort

Les méthodes utilisées dans Linq to Entities doivent être mappées canoniquement par le fournisseur Linq pour fonctionner. Étant donné que le fournisseur Linq, EF dans votre cas, n'a pas pu mapper votre prédicat sur une méthode interne, il a généré une erreur.

Pour les scénarios LINQ, les requêtes sur Entity Framework impliquent le mappage de certaines méthodes CLR avec des méthodes sur la source de données sous-jacente via des fonctions canoniques. Tout appel de méthode dans une requête LINQ to Entities qui n'est pas explicitement mappé à une fonction canonique entraînera la levée d'une exception NotSupportedException au moment de l'exécution

Source: méthode CLR pour le mappage de fonction canonique ( http://msdn.Microsoft.com/en-us/library/bb738681.aspx )

Vous pouvez essayer de prendre les méthodes qui [~ # ~] sont [~ # ~] mappées et les enchaîner dans votre expression Linq, ou utiliser un stocké procédure. Mais jusqu'à ce qu'EF prenne en charge la totalité du CLR, il vous restera à trouver une solution de rechange.

Sur le plan positif, chaque version semble ajouter un peu plus à la liste canonique.

À lire comme solution de rechange possible: http://msdn.Microsoft.com/en-us/library/dd456857.aspx

3
Gayot Fow