Pergunta

Eu tenho tentado refatorar uma expressão LINQ em um método e encontrei os dois "Internal .NET Framework Data Provider error 1025." e "The parameter 'xyz' was not bound in the specified LINQ to Entities query expression."exceções.

Aqui estão as partes relevantes do modelo de entidade (usando 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; }
}

O modelo relacional básico é este:

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

Estou tentando criar uma consulta que, quando for feita uma pesquisa term, tente encontrar Place entidades cujas OfficialName começa com o term OU quem tem PlaceName cujo Text ou AsciiEquivalent começa com a pesquisa term. (Language não é onde estou tendo problemas, embora faça parte da consulta, porque PlaceNames deve corresponder apenas ao CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName.)

O seguinte código funciona:

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

O que estou tentando fazer a seguir é refatorar o NonOfficialNameMatches método para extrair o name => ... expressão em um método separado, para que possa ser reutilizada por outras consultas.Aqui está um exemplo que tentei, que não funciona e lança a exceção "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 eu não tenho o .AsExpandable() cadeia em NonOfficialNameMatches, então eu recebo o "Internal .NET Framework Data Provider error 1025."exceção.

eu tenho seguido outros conselhos aqui como várias combinações de invocação .Expand() nos predicados, mas sempre termina com uma das exceções acima mencionadas.

É possível fatorar essa expressão em um método separado usando LINQ to Entities com LinqKit/PredicateBuilder? Se sim, como?O que estou fazendo de errado?

Foi útil?

Solução

O método abaixo deve funcionar:

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

EDITAR:Adicionando explicações adicionais

O PlaceNameMatches método funciona como você escreveu.Seus problemas estavam em como você usou o método.Se você quiser fatorar partes de uma expressão siga os 3 passos que fiz no método acima.

  1. Defina uma variável local para a expressão criada por um método.

  2. Defina outra variável local para uma nova expressão que invoque a expressão da variável local.

  3. Ligue para o LinkKit Expand método:isso expandirá quaisquer expressões invocadas

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top