具有访问'this'的非静态Expression<Func<X>>

8

我有一个名为Item的数据库表,并使用linq-to-sql进行访问。

我可以为Item定义一个自定义方法IsSpecial(),如果Item.id的平方根是偶数,则返回true:

partial class Item
{
    public static Expression<Func<Item, bool>> IsSpecial = (i => Math.Sqrt(i.Id)%2==0);
}

那么我可以在linq-to-sql查询中使用该属性,例如:
 datacontext.Item.Where(Item.IsSpecial)

现在出于美观的原因,我想将IsSpecial变为非静态,并修改它以便我可以像这样调用它:
 datacontext.Item.Where(i => i.IsSpecial())

理想情况下,这也应该允许组合语句,而上述(可行的)句法不允许此操作:
 datacontext.Item.Where(i => i.IsSpecial() && i.Id >100)

如何正确定义这个方法的语法?

以下内容是不起作用的:

partial class Item
{
    public Expression<Func<bool>> IsSpecial = ( () => Math.Sqrt(this.Id)%2==0 );
    // 'this' keyword not available in current context
}

编辑: 我开始怀疑我正在要求语法根本不允许

我想我可以接受 datacontext.Item.Where(Item.IsSpecial).Where(i => i>100)


顺便提一下,datacontext.Item.Where(i => Item.IsSpecial(i)) 无法编译。 - leppie
为什么你要定义一个类型为Func的IsSpecial属性,而不是定义一个IsSpecial方法?或者只是一个类型为bool的IsSpecial属性? - Ann L.
datacontext.Item.Where(Item.IsSpecial) 可以编译通过。 - leppie
@leppie:谢谢,我搞砸了那里;我已经修复了语法。 - HugoRune
1
@Ann L.:因为本地方法无法在Linq-to-SQL查询中使用。该语句不是在C#中执行的,而是被翻译成了SQL。 - HugoRune
@HugoRune:混合代码和表达式总是很困难的。 :) - leppie
3个回答

4
partial class Item
{
    public static Expression<Func<Item, bool>> IsSpecial = (i => Math.Sqrt(i.Id)%2==0);
}
建议:添加readonly关键字。

Then I can use that property in a linq-to-sql query like this:

datacontext.Item.Where(Item.IsSpecial)

没错,因为Where接受一个类型为Expression<Func<Item, bool>>的参数,这也正是Item.IsSpecial的类型。

Now for aesthetic reasons, I want to make IsSpecial nonstatic and modify it so I can call it like this:

datacontext.Item.Where(i => i.IsSpecial())
这个代码不能正常工作的原因是因为IsSpecial不是一个函数,它是一个表达式树。 ()只能应用于函数。表达式树描述了一个函数,但不是一个函数。您可以使用expression.Compile()创建一个真正的函数:
datacontext.Item.Where(i => (IsSpecial.Compile()) (i))

然而,这并不起作用,因为Where再次使用表达式树,并且IsSpecial.Compile()实际上没有被调用。LINQ to SQL尝试将其转换为SQL,但由于它不认识Expression.Compile,所以会抛出异常。
然而,如果您可以在LINQ to SQL看到它之前替换(IsSpecial.Compile()) ...
这就是LINQKit的用处:
它提供了一些表达式树操作来使其正常工作。
datacontext.Item.AsExpandable().Where(i => (IsSpecial.Compile()) (i))
.AsExpandable()会在datacontext.Item周围创建一个包装器,以预过滤表达式。

Ideally this would also allow combining of statements, which the above (working) snytax does not allow:

datacontext.Item.Where(i => i.IsSpecial() && i.Id >100)

没有问题:

datacontext.Item.AsExpandable().Where(i => (IsSpecial.Compile()) (i) && i.Id > 100)

这看起来非常有前途,我会去看看,谢谢!我对为此再包含另一个库持怀疑态度,但是链接到linqkit也让我发现了http://tomasp.net/blog/linq-expand.aspx,它似乎更详细地讨论了这个问题。 - HugoRune

2

您可以直接使用IsSpecial:

partial class Item
{
     public static Expression<Func<Item, bool>> IsSpecial = (i => Math.Sqrt(i.Id)%2==0);
}

datacontext.Item.Where(Item.IsSpecial)

+1 第一个实际可以编译的解决方案。不过我当时确实对同样的事情发表了评论 :) - leppie
很抱歉,在我的初始问题中复制语法时搞砸了,这是我本该写的内容。理想情况下,我希望将其更改为类似于(NOT VALID:) datacontext.Item.Where(i=>i.isSpecial() && i.Id>100 && [more properties] 的形式,但是我开始怀疑我所要求的是否不可能实现。 - HugoRune

1

尝试将IsSpecial的赋值放在构造函数中,并让所有其他构造函数委托给它,或者您可以使用部分类似乎OnCreated来将表达式分配给IsSpecial。 在partial class Item内部。

partial void OnCreated()
{
     IsSpecial = () => Math.Sqrt(this.Id)%2==0;
}

这样做将始终分配IsSpecial,并允许访问"this"。


这个方法在某种程度上是有效的,因为我可以用this在表达式中使用。然而,Linq会坚持在.Where子句内部使用的表达式中传递一个参数,所以()=>xyz不应该起作用,它需要是(item)=>xyz,这意味着我必须忽略传递的参数,整个过程变得混乱。然而,这个建议在解决相关问题方面帮助了我很多,所以谢谢! - HugoRune
根据您之前的评论,@HugoRune,我认为(实际上我不确定),按照您所说的,您想要的功能可能可以通过以下方式实现:datacontext.Item.Where(i=>i.IsSpecial() && i.Id>100 && [more properties] - JKor
是的,我也这么想,但事实证明这种方式不可行。撇开“这个”问题不谈,即使我使用常量而不是“this”,它仍然无法工作。原因是IsSpecial是一个表达式树,而不是一个简单的函数或lambda。调用IsSpecial()不是有效的语法。I=>i.IsSpecial && i.Id>100也不是有效的语法,你不能像这样连接表达式树。令人困惑的是,如果我使用一个简单的委托或函数而不是Expression<func>>,它将编译并与普通Linq一起工作,但Linq-to-SQL是一个不同的东西。 - HugoRune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接