向Linq-to-SQL对象添加功能以执行常见选择

时间:2008-10-16 08:55:13

标签: c# .net linq-to-sql

previous question中,我问过如何在linq to sql对象中创建“Computed properties”。那里提供的答案足以满足特定情况,但现在我在另一个案例中遇到了类似的问题。

我有一个项目的数据库,必须通过一些步骤。我希望在我的数据库中有一个函数来检索我可以构建的项目的当前步骤。例如:

var x = db.Items.Where(item => item.Steps.CurrentStep().Completed == null);

获取当前步骤的代码是:

Steps.OrderByDescending(step => step.Created).First();

所以我尝试将扩展方法添加到 EntitySet< Step> ,它返回了一个 Step ,如下所示:

public static OrderFlowItemStep CurrentStep(this EntitySet<OrderFlowItemStep> steps)
{
    return steps.OrderByDescending(o => o.Created).First();
}

但是当我尝试在顶部执行查询时,我收到一条错误消息,指出 CurrentStep()函数没有转换为SQL。有没有办法以任何方式将此功能添加到Linq-to-SQL,还是每次都必须手动编写查询?我试着先把整个查询写出来,但它很长,如果我改变了获取项目活动步骤的方法,我必须再次检查所有代码。

我猜测CurrentStep()方法必须返回某种类型的Linq表达式,但我不知道如何实现它。

2 个答案:

答案 0 :(得分:1)

问题是CurrentStep是一种常规方法。因此,Expression包含对该方法的调用,当然SQL不能执行任意.NET方法。

您需要将代码表示为表达式。我在这里有一个深入的例子:http://www.atrevido.net/blog/2007/09/06/Complicated+Functions+In+LINQ+To+SQL.aspx

不幸的是,C#3.0编译器有很大的遗漏,你无法生成对Expressions的调用。 (即,你不能写“x =&gt; MyExpression(x)”)。解决它要么需要手动编写表达式,要么使用委托作为占位符。 Jomo Fisher有一篇关于manipulating Expression trees的有趣帖子。

如果没有真正完成它,我可能接近它的方法是使CurrentStep函数接受你想要添加的谓词(“Completed == null”)。然后你可以创建一个完整的表达式&gt;断言交到哪里。我很懒,所以我打算用String和Char做一个例子(String包含Chars,就像Item包含Steps一样):

using System;
using System.Linq;
using System.Linq.Expressions;

class Program {
    static void Main(string[] args) {
        Console.WriteLine(StringPredicate(c => Char.IsDigit(c)));
        var func = StringPredicate(c => Char.IsDigit(c)).Compile();
        Console.WriteLine(func("h2ello"));
        Console.WriteLine(func("2ello"));
    }

    public static Expression<Func<string,bool>> StringPredicate(Expression<Func<char,bool>> pred) {
        Expression<Func<string, char>> get = s => s.First();
        var p = Expression.Parameter(typeof(string), "s");
        return Expression.Lambda<Func<string, bool>>(
            Expression.Invoke(pred, Expression.Invoke(get, p)),
            p);
    }
}

因此,使用StringPredicate创建表达式来创建“func”。例如,我们编译它以在本地执行它。在您的情况下,您将整个谓词传递给“Where”,以便将其转换为SQL。

“get”表达式是您放置“扩展”内容的地方(OrderByWhatever,First等)。然后将其传递给给您的谓词。

如果看起来很复杂,请不要担心;最初是排序。如果你以前没有做过这种事情,那将花费一些时间(我第一次做这种事情,花了几个小时才能做到正确:| ..现在它变得更容易了)。另外,正如我所提到的,你可以编写一个辅助方法来为你重写(所以你不需要直接使用Expression.Whatever方法),但是我没有看到任何例子,也没有真正的还需要它。

答案 1 :(得分:0)

结帐my answer至“switch statement in linq”并查看是否指出了正确的方向......

我展示的技术让我超越了可怕的“无法转换为SQL”错误。