2014-11-08 49 views
0

下面是LINQ到SQL一個難倒的:LINQ2SQL組和取消在同一查詢

string p = prefix ?? ""; 
string d = delimiter ?? ""; 
var filegroups = from b in folder.GetFiles(data) 
        where b.Uri.StartsWith(p) && b.Uri.CompareTo(marker ?? "") >= 0 
        group b by data.DataContext.GetFileFolder(p, d, b.Uri); 
//var folders = from g in filegroups where g.Key.Length > 0 select g; 
//var files = from g in filegroups where g.Key.Length == 0 select g; 
var files = filegroups.SelectMany(g => g.Key.Length > 0 
    ? from b in g.Take(1) select new FilePrefix { Name = g.Key } 
    : from b in g select new FilePrefix { Name = b.Uri, Original = b }); 

var retval = files.Take(maxresults); 

文件夾不能嵌套(在我的掌握),但文件名可以包含斜線和任何所以更深的文件夾結構可仿真

folder.GetFiles是一個簡單的LINQ當量(IOrderedQueryable)到select * from files where [email protected] order by Uri

前綴是一個過濾器說法回報只有那些有...
定界符啓動文件的路徑分隔符,如「/」
標記是分頁 - 開始在指定點返回

data.DataContext.GetFileFolder映射到一個sql標量函數:返回整個字符串直到包括在前綴字符串後面出現的下一個分隔符 RETURN substring(@uri, 0, charindex(@delimiter, @uri, len(@prefix)) + len(@delimiter))這是爲了排除故障 - 原來是一個客戶端where子句,它正確映射到TSQL。我只是希望做一個函數會改變最終圖中的內容,但不會。

在上面,文件組

,並註釋掉文件夾和文件,作爲一切工作的預期

目標是打數據庫只有一次。我希望在一次返回時根據對FilePrefix對象的解釋來顯示子文件夾和文件(文件夾具有空「原始」值)

問題在於最終的selectmany引發「無法格式化節點」 ClientQuery'作爲SQL執行。「

我強烈懷疑,如果它不是用於TSQL翻譯的話,它可以很好地工作,但從邏輯上看,爲什麼它不執行數據庫工作,然後選擇FilePrefixes客戶端作爲最後一步?

現在已經很晚了;)但是明天我會通過滑動一個ToList()或類似的東西在那裏類似的東西來恢復雙擊數據庫,從而導致最後一步成爲完整的客戶端(kludge)。但是,如果任何人對如何在一次數據庫命中時完成這項任務有所瞭解(缺少寫入存儲過程),我很樂意聽到它!

該kludge的缺點是,如果數據庫擊中結果的記錄遠遠超過這個記錄,最終Take(maxresults)可能會很昂貴。而後來的Skip(maxresults).Take(1),我沒有引用,標記下一頁,會傷害兩倍。

非常感謝你

+0

你的代碼的目的是什麼? – 2014-11-08 13:42:07

+0

webapi雲存儲解決方案。返回的fileprefix對象將被擴展爲格式並序列化與linq對象佈局不完全匹配的響應。規定的響應混合文件夾和文件。 文件夾正在被分組模擬,文件組內的文件正在被刪除並且沒有被返回(因爲它們在子文件夾中)。在零長度組鍵中找到的文件位於'this'文件夾中,正如預期的那樣,正在返回 – thewyzard 2014-11-08 21:57:02

+0

和yup,將'filegroups.SelectMany'更改爲'filegroups.AsEnumerable()。SelectMany'成功。 – thewyzard 2014-11-08 22:25:18

回答

0

Welp,它看起來像2數據庫命中是必要的。我開始注意到調用圖將第三級操作符轉換爲IIF,這導致我認爲在SQL方面,IIF可能不喜歡子查詢作爲參數。

string p = prefix ?? ""; 
string d = delimiter ?? ""; 
var filegroups = from b in folder.GetFiles(data) 
        where b.Uri.StartsWith(p) && b.Uri.CompareTo(marker ?? "") >= 0 
        group b by data.DataContext.nx_GetFileFolder(p, d, b.Uri); 
var folders = from g in filegroups where g.Key.Length > 0 select g.Key; 
var files = from b in folder.GetFiles(data) 
      where b.Uri.StartsWith(p) && b.Uri.CompareTo(marker ?? "") >= 0 
       && data.DataContext.nx_GetFileFolder(p, d, b.Uri).Length == 0 
      select b; 

folders = folders.OrderBy(f => f).Take(maxresults + 1); 
files = files.OrderBy(f => f.Uri).Take(maxresults + 1); 

var retval = folders.AsEnumerable().Select(f => new FilePrefix { Name = f }) 
      .Concat(files.AsEnumerable().Select(f => new FilePrefix { Name = f.Uri, Original = f })) 
      .OrderBy(b => b.Name).Take(maxresults + 1); 

int count = 0; 
foreach (var bp in retval) 
{ 
    if (count++ < maxresults) 
     yield return bp; 
    else 
     newmarker.Name = bp.Name; 
} 
yield break; 

少了幾分優雅的......我離開文件組和文件夾,但改寫了文件查詢擺脫組(產生清潔SQL,可能更有效)的。

Concat在這種新方法中仍然給我帶來麻煩,所以我踢了AsEnumerable調用,這就是將這個調用分解成2個到數據庫的點。

我在sql中保留了maxresults來限制流量,所以最壞的情況是我想通過網絡傳輸兩倍的數據。 +1將獲得下一條記錄,以便可以通知用戶從下一頁開始的位置。而且我使用了迭代器模式,所以我不必再循環來獲取下一條記錄。