2011-10-24 47 views
0

我在WP7(芒果)上使用C#。我嘗試,因爲我收到一個錯誤使用特殊查詢:Linq - 如何在查詢中使用函數

Method 'Int32 orderBirthday(System.DateTime)' has no supported translation to 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); 
. 
. 
. 
} 

回答

0

如果你這樣做很容易:

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

通知的.ToArray()加入Items。你基本上把結果帶入記憶中,你的功能可以工作。

+1

但是,如果你這樣做,你會失去Linq的流式傳輸能力......如果有很多結果,這可能會導致OutOfMemoryException。你應該使用AsEnumerable而不是ToArray –

+0

哇....它的工作很好:-) 非常感謝你! – John

+1

請注意,當'Items'表中的記錄數量開始增加時,這將開始執行不良,因爲您會將該表的* complete *內容加載到內存中。 – Steven

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(); 
+0

我有使用這種方法,工作正常!謝謝! – John

0

兩種方式:

其中之一:使用ToEnumerable()將其從Linq2SQL拉到Linq2Objects,然後在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。

0

如果不想加載內存中的所有項目,並且希望數據庫執行計算,則可以編寫一個存儲過程來執行復雜計算並使用ADO或EF調用過程。