尝试使用 Expression<Func<..>> 从方法调用 Any() 时出现类型错误

Type error trying to call Any() using Expression<Func<..>> from method

我正在创建一个从我的 EF 实体到我的 BLL 实体的可重用映射表达式:

private Expression<Func<Person, bool>> GetNameFilter(string name)
{
    return person => person.Profile.UserName == name;
}

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
    return perk => new Bll.Perk
        {
            Id = perk.PerkId,
            Name = perk.PerkName,
            Description = perk.PerkDescription,

            //Compilation Error
            Owned = perk.Players.Any(GetNameFilter(name)) 
        };
}

而且我在注意到的行上遇到编译错误。错误内容为:

ICollection does not contain a definition for 'Any' and the best extension method overload 'Queryable.Any(IQueryable, Expression>)' requires a receiver of type 'IQueryable'

但是当我直接将这个表达式推入时不会发生这种情况:

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
    return perk => new Bll.Perk
        {
            Id = perk.PerkId,
            Name = perk.PerkName,
            Description = perk.PerkDescription,

            //No Compilation Error
            Owned = perk.Players.Any(person => person.Profile.UserName == name)
        };
}

为什么会这样?两个表达式的类型相同。

使用下面找到的 LinqExpression 解决方案,我现在在运行时遇到以下错误:

An exception of type 'System.InvalidCastException' occurred in LinqKit.dll but was not handled in user code

Additional information: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.

internal FutureQuery<Perk> GetPlayerInfoPerks(string username)
{
    return Master
            .Perks
            .VisiblePerks
            .Select(Master.Perks.MapPerkPerk(username))
            .Future();
}

这是因为使用了 EntityFramework.Future 库吗?​​

Why is this happening? The type of both expressions is the same.

两个表达式的类型 相同 - 它们只是在视觉上看起来相同。以下是有效的:

dbContext.Persons.Any(GetNameFilter(name))

这不是:

perk.Players.Any(GetNameFilter(name))

为什么?因为第一个期望 Expression<Func<...>> 而第二个 - 只是 Func<..>IQueryable<T>IEnumerable<T> 方法之间具有相同名称的典型区别)。希望你能看出区别。当您直接键入它时,C# 编译器会施展魔法发出一个或另一个,但当您手动执行时,您应该使用正确的。

问题已由 LinqKit 包和 Invoke / Expand 自定义扩展方法(更普遍的是 AsExpandable)解决。

对于您的具体示例,LinqKit 解决方案可能是这样的:

using LinqKit;

...

internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
    // LinqKit requires expressions to be in variables
    var nameFilter = GetNameFilter(name);
    return Linq.Expr((Ef.Perk perk) => new Bll.Perk
    {
        Id = perk.PerkId,
        Name = perk.PerkName,
        Description = perk.PerkDescription,
        Owned = perk.Players.Any(p => nameFilter.Invoke(p)) 
    }).Expand();
}