无法使用LINQ to Entities和LinqKit/PredicateBuilder进行重构

dan*_*wig 6 linq-to-entities expression-trees predicatebuilder entity-framework-4 linqkit

我一直在尝试将LINQ表达式重构为一个方法,并且已经遇到了" Internal .NET Framework Data Provider error 1025."和" The parameter 'xyz' was not bound in the specified LINQ to Entities query expression."异常.

以下是实体模型的相关部分(使用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; }
}
Run Code Online (Sandbox Code Playgroud)

基本关系模型是这样的:

Place (1) <-----> (0..*) PlaceName (0..*) <-----> (0..1) Language
Run Code Online (Sandbox Code Playgroud)

我想创建一个查询,将给出一个搜索时term,试图找到Place它的实体OfficialName与开始term或谁拥有PlaceNameTextAsciiEquivalent与搜索开始term.(Language不是我遇到麻烦的地方,虽然它是查询的一部分,因为PlaceNames应该只匹配CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName.)

以下代码确实有效:

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)
            )
        )
    );
}
Run Code Online (Sandbox Code Playgroud)

我接下来要做的是重构NonOfficialNameMatches方法将name => ...表达式提取到一个单独的方法中,以便其他查询可以重用它.这是我尝试过的一个例子,它不起作用并抛出异常" 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)
                )
            );
}
Run Code Online (Sandbox Code Playgroud)

当我没有.AsExpandable()NonOfficialNameMatches,我得到" Internal .NET Framework Data Provider error 1025."例外.

我在这里遵循了其他建议,例如调用.Expand()谓词的几种组合,但总是以上述异常之一结束.

甚至可以使用LINQ to Entities with LinqKit/PredicateBuilder将此表达式分解为单独的方法吗?如果是这样,怎么样?我究竟做错了什么?

Dan*_*ker 7

以下方法应该有效:

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();
}
Run Code Online (Sandbox Code Playgroud)

编辑:添加其他说明

PlaceNameMatches方法与您编写时一样有效.您的问题在于您如何使用该方法.如果你想要分解表达式的部分,请遵循我在上面的方法中所做的3个步骤.

  1. 将局部变量设置为方法创建的表达式.

  2. 将另一个局部变量设置为调用局部变量表达式的新表达式.

  3. 调用LinkKit Expand方法:这将扩展任何Invoked表达式