Impossibile refactoring utilizzando LINQ to Entities e LinqKit / PredicateBuilder

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

Domanda

Ho cercato di ridefinire un'espressione LINQ in un metodo e ho eseguito sia il " Internal .NET Framework Data Provider error 1025. " sia " The parameter 'xyz' was not bound in the specified LINQ to Entities query expression. "eccezioni.

Ecco le parti rilevanti del modello dell'entità (utilizzando 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; }
}

Il modello relazionale di base è questo:

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

Sto tentando di creare una query che, quando viene fornito un term ricerca, cerca di trovare le entità Place cui OfficialName inizia con il term OR che ha un PlaceName cui Text o AsciiEquivalent inizia con il term ricerca. (La Language non è dove ho problemi, anche se fa parte della query, perché PlaceName deve corrispondere solo a CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName .)

Il seguente codice funziona :

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)
            )
        )
    );
}

Quello che sto cercando di fare è il refactoring del metodo NonOfficialNameMatches per estrarre il name => ... expression in un metodo separato, in modo che possa essere riutilizzato da altre query. Ecco un esempio che ho provato, che non funziona e genera l'eccezione " 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)
                )
            );
}

Quando non ho la catena .AsExpandable() in NonOfficialNameMatches , ottengo l'eccezione " Internal .NET Framework Data Provider error 1025. ".

Ho seguito altri consigli qui come diverse combinazioni di invocazione di .Expand() sui predicati, ma .Expand() sempre con una delle eccezioni di cui sopra.

È persino possibile calcolare questa espressione in un metodo separato usando LINQ alle entità con LinqKit / PredicateBuilder? Se é cosi, come? Che cosa sto facendo di sbagliato?

Risposta accettata

Il seguente metodo dovrebbe funzionare:

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: aggiunta di ulteriori spiegazioni

Il metodo PlaceNameMatches funziona come l'hai scritto. I tuoi problemi riguardavano il modo in cui hai utilizzato il metodo. Se vuoi calcolare parti di un'espressione segui i 3 passaggi che ho fatto nel metodo sopra.

  1. Imposta una variabile locale sull'espressione creata da un metodo.

  2. Imposta un'altra variabile locale su una nuova espressione che richiama l'espressione di variabile locale.

  3. Chiama il metodo LinkKit Expand : questo espande tutte le espressioni Invoked



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché