2011-10-04 41 views
2

有誰知道爲什麼這會導致計算器例外:在使用IQueryable的加入導致的StackOverflow異常

public IQueryable<Category> LoadCategories(bool onlyCatsWithProducts, ...) 
{ 
    var db = new DbDataContext(); 
    var res = db.Categories.AsQueryable(); 

    if (onlyCatsWithProducts) 
     res = from p in db.Products 
       from c in res 
       where p.CategoryID == c.ID 
       select c; 
    ... 
    return res; 
} 

更新

改變了示例代碼來證明爲什麼我賦值給一個變量,然後稍後重新分配。基本上,這是因爲我正在編寫一個函數來從數據庫返回類別,並且我接受了幾個參數(例如onlyCatsWithProducts),其中每個參數只有在有值時纔對結果集進行過濾。請注意,這仍然不是我的實際代碼,因爲我的查詢更復雜,我只是想展示重現錯誤所需的最簡單的查詢。

+0

這是實際的代碼嗎?如果是這樣,我猜猜有一個錯字...什麼是v? –

+0

謝謝修復的錯字。我只是簡化了我的代碼,所以不是我的實際代碼。 – Michael

+0

你不應該需要'.AsQueryable()'。您可能想要嘗試將輸出類型顯式設置爲底層接口,而不是使用'var'或'AsQueryable()'。我不確定這是否在這裏遞歸地分配表達式樹時解決了您的StackOverflow問題。 –

回答

2

邁克爾,在回答了自己的問題,他說他不知道爲什麼他交換from的順序固定他的問題。

這樣引起的堆棧溢出:

var res = db.Categories.AsQueryable(); 
res = from p in db.Products 
     from c in res 
     where p.CategoryID == c.ID 
     select c; 

像這次沒:

var res = db.Categories.AsQueryable(); 
res = from c in res 
     from p in db.Products 
     where p.CategoryID == c.ID 
     select c; 

這裏的原因。這兩種以上的查詢得到翻譯,由編譯器,該代碼:

var res = db.Categories.AsQueryable(); 
var q = db.Products 
    .SelectMany(p => res, (p, c) => new { p, c }) 
    .Where(x => x.p.CategoryID == x.c.ID) 
    .Select(x => x.c); 

和分別此:

var res = db.Categories.AsQueryable(); 
var q = res 
    .SelectMany(c => db.Products, (c, p) => new { c, p }) 
    .Where(x => x.p.CategoryID == x.c.ID) 
    .Select(x => x.c); 

前者包含拉姆達p => res其基本上捕獲到res變量的引用。由於res被重新分配,每次查詢運行時都會引用它自己的重新分配版本和爆炸堆棧溢出!

在第二個res在任何lamdbas之外,因此不會捕獲該引用並且僅使用原始引用 - 即res = db.Categories.AsQueryable(),並且執行查詢時這不會更改。

它很可能是一樣容易使用:

var res = from c in db.Categories 
      from p in db.Products 
      where p.CategoryID == c.ID 
      select c; 

我希望這有助於清理髮生了什麼。

0

爲什麼創建一個變量,然後在賦值中使用變量後直接重新賦值該變量?您的查詢可能會做某種無限遞歸,導致StackOverFlowException。嘗試是這樣的:

var res = 
    from c in DB.Instance.Categories 
    from p in DB.Instance.Products 
    where p.CategoryID == c.ID 
    select c; 

更新:

嘗試類似下面。我認爲你需要避免在res的任務中使用res

IQueryable<Category> res; 

if (onlyCatsWithProducts) 
    res = from p in db.Products 
      from c in db.Categories.AsQueryable() 
      where p.CategoryID == c.ID 
      select c; 
+0

更改了問題以反映變量的用途。你的代碼確實有效,但我的函數需要大量的參數(不僅僅是我的例子中的一個),它會根據傳入的內容來改變查詢,因此在一個查詢中完成所有操作將會使它更復雜得多。我可以做到這一點,如果它是應用的第一個過濾器,但我的三個過濾器有複雜的聯接,需要這種語法,所以這是所有的排列,這是不是很好的一些if else語句。 – Michael

+0

看我的更新.. – Mig

0

道歉,發佈後我發現交換訂單似乎解決了這個問題。還是不知道爲什麼,但:

var db = new DbDataContext(); 
var res = db.Categories.AsQueryable(); 
res = from c in res 
     from p in db.Products 
     where p.CategoryID == c.ID 
     select c;