Impossible de refactoriser l'utilisation de LINQ to Entities et LinqKit / PredicateBuilder

entity-framework-4 expression-trees linqkit linq-to-entities predicatebuilder

Question

J'essayais de refactoriser une expression LINQ dans une méthode et je rencontrais à la fois " Internal .NET Framework Data Provider error 1025. " et " The parameter 'xyz' was not bound in the specified LINQ to Entities query expression. " des exceptions.

Voici les parties pertinentes du modèle d'entité (avec EF 4.2 / LINQ to Entities):

public class Place : Entity
{
    public string OfficialName { get; protected internal set; }
    public virtual ICollection<PlaceName> { get; protected internal set; }
}

public class PlaceName : Entity
{
    public string Text { get; protected internal set; }
    public string AsciiEquivalent { get; protected internal set; }
    public virtual Language TranslationTo { get; protected internal set; }
}

public class Language : Entity
{
    public string TwoLetterIsoCode { get; protected internal set; }
}

Le modèle relationnel de base est le suivant:

Place (1) <-----> (0..*) PlaceName (0..*) <-----> (0..1) Language

Je suis en train de créer une requête qui, lorsqu'il est administré une recherche term , essayer de trouver Place des entités dont OfficialName commence par le term ou qui a un PlaceName dont le Text ou AsciiEquivalent commence par la recherche term . (La Language n'est pas l'endroit où je rencontre des problèmes, bien qu'elle fasse partie de la requête, car les PlaceName doivent correspondre uniquement à CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName .)

Le code suivant fonctionne :

internal static IQueryable<Place> WithName(this IQueryable<Place> queryable, 
    string term)
{
    var matchesName = OfficialNameMatches(term)
        .Or(NonOfficialNameMatches(term));
    return queryable.AsExpandable().Where(matchesName);
}

private static Expression<Func<Place, bool>> OfficialNameMatches(string term)
{
    return place => place.OfficialName.StartsWith(term);
}

private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
    var currentLanguage = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
    return place => place.Names.Any(
        name =>
        name.TranslationToLanguage != null
        &&
        name.TranslationToLanguage.TwoLetterIsoCode == currentLanguage
        &&
        (
            name.Text.StartsWith(term)
            ||
            (
                name.AsciiEquivalent != null
                &&
                name.AsciiEquivalent.StartsWith(term)
            )
        )
    );
}

Ce que j'essaie de faire ensuite est de refactoriser la méthode NonOfficialNameMatches pour extraire l'expression name => ... en une méthode séparée, afin qu'elle puisse être réutilisée par d'autres requêtes. Voici un exemple que j'ai essayé, qui ne fonctionne pas et génère l'exception " The parameter 'place' was not bound in the specified LINQ to Entities query expression. ":

private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
    return place => place.Names.AsQueryable().AsExpandable()
        .Any(PlaceNameMatches(term));
}

public static Expression<Func<PlaceName, bool>> PlaceNameMatches(string term)
{
    var currentLanguage = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
    return name =>
            name.TranslationToLanguage != null
            &&
            name.TranslationToLanguage.TwoLetterIsoCode == currentLanguage
            &&
            (
                name.Text.StartsWith(term)
                ||
                (
                    name.AsciiEquivalent != null
                    &&
                    name.AsciiEquivalent.StartsWith(term)
                )
            );
}

Lorsque je n'ai pas la chaîne .AsExpandable() dans NonOfficialNameMatches , j'obtiens l'exception " Internal .NET Framework Data Provider error 1025. .".

J'ai suivi d' autres conseils ici , comme plusieurs combinaisons d'invocation de .Expand() sur les prédicats, mais aboutissons toujours à l'une des exceptions susmentionnées.

Est-il même possible de factoriser cette expression dans une méthode séparée utilisant LINQ to Entities avec LinqKit / PredicateBuilder? Si c'est le cas, comment? Qu'est-ce que je fais mal?

Réponse acceptée

La méthode ci-dessous devrait fonctionner:

private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
    Expression<Func<PlaceName, bool>> placeNameExpr = PlaceNameMatches(term);
    Expression<Func<Place, bool>> placeExpr =
        place => place.Names.Any(name => placeNameExpr.Invoke(name));
    return placeExpr.Expand();
}

EDIT: Ajouter des explications supplémentaires

La méthode PlaceNameMatches fonctionne telle que vous l'avez écrite. Vos problèmes étaient dans la façon dont vous avez utilisé la méthode. Si vous souhaitez décomposer des parties d'une expression, suivez les 3 étapes décrites dans la méthode ci-dessus.

  1. Définissez une variable locale sur l'expression créée par une méthode.

  2. Définissez une autre variable locale sur une nouvelle expression qui appelle l'expression de la variable locale.

  3. Appelez la méthode LinkKit Expand : cela développera toutes les expressions invoquées



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi