6

He intentado refactorizar una expresión LINQ en un método, y me he estado ejecutando en las excepciones "Internal .NET Framework Data Provider error 1025." y "The parameter 'xyz' was not bound in the specified LINQ to Entities query expression.".No se puede refactorizar utilizando LINQ para Entidades y LinqKit/PredicateBuilder

Estas son las partes relevantes del modelo de entidad (usando EF 4.2/LINQ a Entidades):

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

El modelo relacional básica es la siguiente:

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

Estoy tratando de crear una consulta que, cuando se da una búsqueda term, intente encontrar entidades cuyo OfficialName comienza con term O que tiene PlaceName cuyo Text o AsciiEquivalent comienza con la búsqueda term. (Language no es donde yo estoy teniendo problemas, a pesar de que es parte de la consulta, porque PlaceName s sólo deben coincidir para que la CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName.)

El código siguiente Cómo 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) 
      ) 
     ) 
    ); 
} 

Lo que trato de hacer a continuación es refactorizar el método NonOfficialNameMatches para extraer la expresión name => ... en un método separado, para que pueda ser reutilizada por otras consultas. He aquí un ejemplo que he tratado, que no funciona y produce la excepción "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) 
       ) 
      ); 
} 

Cuando no tengo la cadena .AsExpandable() en NonOfficialNameMatches, entonces me sale el' Internal .NET Framework Data Provider error 1025." excepción.

He seguido other advice here como varias combinaciones de invocación de .Expand() en los predicados, pero siempre acabo con una de las excepciones antes mencionadas.

¿Es posible factorizar esta expresión en un método separado utilizando LINQ para Entidades con LinqKit/PredicateBuilder? Si es así, ¿cómo? ¿Qué estoy haciendo mal?

Respuesta

7

El método siguiente debería 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(); 
} 

EDIT: Adición de explicaciones adicionales

El método PlaceNameMatches funciona como usted lo escribió. Tus problemas estaban en cómo usaste el método. Si desea factorizar partes de una expresión, siga los 3 pasos que hice en el método anterior.

  1. Establezca una variable local a la expresión creada por un método.

  2. Establezca otra variable local a una nueva expresión que invoca la expresión de la variable local.

  3. Llame al método Expand LinkKit: esta se ampliará cualquier invoca expresiones

+0

+100, gracias Estaba empezando a pensar en esta pregunta nunca son respondidas. La explicación adicional ayuda también. – danludwig

Cuestiones relacionadas