Linq - 如何在查询中使用函数

时间:2011-10-24 10:06:51

标签: c# linq linq-to-sql

我在WP7(Mango)上使用C#。我尝试使用特殊查询,因为我收到错误:

  

方法'Int32 orderBirthday(System.DateTime)'不受支持   转换为SQL。

是的,我知道...... Linq不能使用我的功能,但我不知道正确的方法......

我有一个包含namebirthday列的数据库表。在我的查询中,我将计算下一个生日的天数(来自所有项目),然后我将以“降序”命令。

static int orderBirthday(DateTime Birthday)
    {
        DateTime today = DateTime.Today;
        DateTime birthday = Birthday;
        DateTime next = new DateTime(today.Year, birthday.Month, birthday.Day);

        if (next < today)
            next = next.AddYears(1);

        int numDays = (next - today).Days;

        // No Conversion
        return numDays;
    }

 public void LoadCollectionsFromDatabase()
    {

        DateTime today = DateTime.Today;

        var toDoItemsInDB = from ToDoItem todo in toDoDB.Items
                            let daysToBirthday = orderBirthday(todo.ItemDate)
                            orderby daysToBirthday ascending
                            select todo;

        // Query the database and load all to-do items.
        AllToDoItems = new ObservableCollection<ToDoItem>(toDoItemsInDB);
.
.
.
}

4 个答案:

答案 0 :(得分:1)

您必须从数据库中提取所有内容并在本地对其进行排序(如Enigmativity),或者找到一种在LINQ语句本身中表达排序操作的方法。由于您将排序行为提取到自己的函数中,因此您可能希望重用此逻辑。在这种情况下,最好的办法是创建一个过滤IQueryable

的函数

以下是如何执行此操作的示例:

public static IOrderedQueryable<Item> OrderByBirthday(
    this IQueryable<Item> items)
{
    return
        from item in items
        let today = DateTime.Today
        let birthday = item.ItemDate
        let next = new DateTime(today.Year, birthday.Month, birthday.Day)
        let next2 = next < today ? next.AddYears(1) : next
        orderby (next - today).Days
        select item;
}

您可以按如下方式使用该方法:

var toDoItemsInDB = OrderByBirthday(toDoDB.Items);

或者您可以将其用作扩展方法:

var toDoItemsInDB = toDoDB.Items.OrderByBirthday();

答案 1 :(得分:0)

如果你这样做很容易:

    var toDoItemsInDB = from ToDoItem todo in toDoDB.Items.ToArray()
                        let daysToBirthday = orderBirthday(todo.ItemDate)
                        orderby daysToBirthday ascending
                        select todo.;

请注意.ToArray()添加到Items。你基本上把结果带到了内存中,你的功能可以起作用。

答案 2 :(得分:0)

两种方式:

一:使用ToEnumerable()将其从Linq2SQL拉到Linq2Object,然后在C#级别使用orderBirthday

优点是代码和维护简单,缺点是 效率低(取决于你正在做什么。

二:在SQL中编写一个等效函数,假设它被称为dbo.orderBirthday。使您的orderBirthday方法成为datacontext派生类的非静态方法,然后将您的方法标记为具有等效的SQL函数:

[Function(Name="dbo.orderBirthday",IsComposable=true)] //IsComposable is true for functions that can be used within queries, false for stored procedures that must be called on their own.
public int OrderBirthday([Parameter(Name="@birthday",DbType="datetime") DateTime birthday)
{
    return Helper.OrderBirthday(birthday); // just to show that we can keep the static version around if we want and call into it. Alternatively we could just move the whole body here.
}

这里C#代码用于非Linq2SQL上下文,SQL代码用于在Linq2SQL上下文中编写SQL查询。

优点:可以在SQL中保持更长时间。缺点:同一方法的两个版本可能会失去同步并导致错误。

也可以让C#代码始终调用SQL代码:

[Function(Name="dbo.orderBirthday",IsComposable=true)]
public int OrderBirthday([Parameter(Name="@birthday",DbType="datetime") DateTime birthday)
{
    return (int)ExecuteMethodCall(this, (MethodInfo)MethodInfo.GetCurrentMethod(), birthday).ReturnValue;
}

优点:保留一个版本(SQL)作为唯一版本,因此它不会与C#版本不同步。缺点:即使在处理与SQL无关的对象时也要调用SQL。

答案 3 :(得分:0)

如果您不想加载内存中的所有项目,并且希望数据库执行计算,则可以编写一个可以执行复杂计算的存储过程,并使用ADO或EF调用该过程。

相关问题