2010-03-11 31 views
0

我正在尋找一個使用單個SELECT來獲取整個LINQ對象層次結構的特定問題的答案。如何使用單個LINQ查詢將所有父對象選擇到DataContext中?

起初我試圖用LoadOptions填充儘可能多的LINQ對象,但是AFAIK這個方法只允許使用LoadWith在一個查詢中鏈接單個表。因此,我發明了一個解決方案,強制設置實體的所有父對象哪一個列表將被提取,儘管存在多個SELECTS進入數據庫的問題 - 單個查詢導致兩個SELECTS在相同的LINQ環境中具有相同的參數。

對於這個問題我已經簡化了此查詢到流行的發票例如:

public static class Extensions 
{ 
     public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> func) 
     { 
      foreach(var c in collection) 
      { 
       func(c); 
      } 
      return collection; 
     } 
} 

public IEnumerable<Entry> GetResults(AppDataContext context, int CustomerId) 
{ 
    return 
    (
     from entry in context.Entries 
     join invoice in context.Invoices on entry.EntryInvoiceId equals invoice.InvoiceId 
     join period in context.Periods on invoice.InvoicePeriodId equals period.PeriodId 
     // LEFT OUTER JOIN, store is not mandatory 
     join store in context.Stores on entry.EntryStoreId equals store.StoreId into condStore 
     from store in condStore.DefaultIfEmpty() 
     where 
      (invoice.InvoiceCustomerId = CustomerId) 
     orderby entry.EntryPrice descending 
     select new 
     { 
      Entry = entry, 
      Invoice = invoice, 
      Period = period, 
      Store = store 
     } 
    ).ForEach(x => 
     { 
      x.Entry.Invoice = Invoice; 
      x.Invoice.Period = Period; 
      x.Entry.Store = Store; 
     } 
    ).Select(x => x.Entry); 
} 

在調用此函數,並通過結果集穿越,例如:

var entries = GetResults(this.Context); 
int withoutStore = 0; 
foreach(var k in entries) 
{ 
    if(k.EntryStoreId == null) 
     withoutStore++; 
} 

結果查詢數據庫的外觀喜歡(單個結果取自):

SELECT 
    [t0].[EntryId], 
    [t0].[EntryInvoiceId], 
    [t0].[EntryStoreId], 
    [t0].[EntryProductId], 
    [t0].[EntryQuantity], 
    [t0].[EntryPrice], 
    [t1].[InvoiceId], 
    [t1].[InvoiceCustomerId], 
    [t1].[InvoiceDate], 
    [t1].[InvoicePeriodId], 
    [t2].[PeriodId], 
    [t2].[PeriodName], 
    [t2].[PeriodDateFrom], 
    [t4].[StoreId], 
    [t4].[StoreName] 
FROM 
    [Entry] AS [t0] 
    INNER JOIN [Invoice] AS [t1] ON [t0].[EntryInvoiceId] = [t1].[InvoiceId] 
    INNER JOIN [Period] AS [t2] ON [t2].[PeriodId] = [t1].[InvoicePeriodId] 
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t3].[StoreId], [t3].[StoreName] 
    FROM [Store] AS [t3] 
    ) AS [t4] ON [t4].[StoreId] = ([t0].[EntryStoreId]) 
WHERE (([t1].[InvoiceCustomerId]) = @p0) 
ORDER BY [t0].[InvoicePrice] DESC 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [186] 
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1 

SELECT 
    [t0].[EntryId], 
    [t0].[EntryInvoiceId], 
    [t0].[EntryStoreId], 
    [t0].[EntryProductId], 
    [t0].[EntryQuantity], 
    [t0].[EntryPrice], 
    [t1].[InvoiceId], 
    [t1].[InvoiceCustomerId], 
    [t1].[InvoiceDate], 
    [t1].[InvoicePeriodId], 
    [t2].[PeriodId], 
    [t2].[PeriodName], 
    [t2].[PeriodDateFrom], 
    [t4].[StoreId], 
    [t4].[StoreName] 
FROM 
    [Entry] AS [t0] 
    INNER JOIN [Invoice] AS [t1] ON [t0].[EntryInvoiceId] = [t1].[InvoiceId] 
    INNER JOIN [Period] AS [t2] ON [t2].[PeriodId] = [t1].[InvoicePeriodId] 
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t3].[StoreId], [t3].[StoreName] 
    FROM [Store] AS [t3] 
    ) AS [t4] ON [t4].[StoreId] = ([t0].[EntryStoreId]) 
WHERE (([t1].[InvoiceCustomerId]) = @p0) 
ORDER BY [t0].[InvoicePrice] DESC 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [186] 
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1 

問題是爲什麼有兩個查詢,我怎樣才能沒有這樣的黑客獲取LINQ對象?

回答

1

爲什麼不多次撥打LoadWith

DataLoadOptions文檔,它說:

每次調用LoadWith檢查是否有循環[...]

(在上避免循環的部分。)

+0

肯定LoadWith的作品 - 我不能重複爲什麼我以前的嘗試使用多個LoadWith失敗,謝謝你救了我不必要的遊蕩。 – too

+0

對於記錄 - 對於LoadWith正常工作,查詢必須包含所有需要提取的表,即使它們是由上下文的LoadOptions中的LoadWith指令指定的,它也不會自動地加入所有父表。 – too

1

好...我認爲你遍歷你的「可查詢」兩次:在ForEach擴展和「命令式」foreach()塊中... 您是否嘗試將ForEach的實現更改爲...

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> func) 
    { 
     foreach (var c in collection) 
     { 
      func(c); 
      yield return c; 
     } 
    } 
0

對於其他人尋求這一問題的精確解,請考慮以下的縮短和工作的代碼版本在單一SELECT獲取LINQ對象層次。我將GetResults函數的返回類型更改爲IQueryable,因爲集合可以通過LINQ更改跟蹤機制進行正確跟蹤,從而允許使用集合中的更改更新數據庫。

public void InitContext(AppDataContext context) 
{ 
    DataLoadOptions options = new DataLoadOptions(); 
    options.LoadWith<Entry>(x => x.Invoice); 
    options.LoadWith<Entry>(x => x.Store); 
    options.LoadWith<Invoice>(x => x.Period); 
    context.DataLoadOptions = options; 
} 

public IQueryable<Entry> GetResults(AppDataContext context, int customerId) 
{ 
    return 
    (
     from entry in context.Entries 
     join invoice in context.Invoices on entry.EntryInvoiceId equals invoice.InvoiceId 
     join period in context.Periods on invoice.InvoicePeriodId equals period.PeriodId 
     // LEFT OUTER JOIN, store is not mandatory 
     join store in context.Stores on entry.EntryStoreId equals store.StoreId into condStore 
     from store in condStore.DefaultIfEmpty() 
     where 
      (invoice.InvoiceCustomerId == customerId) 
     orderby entry.EntryPrice descending 
     select entry 
    ); 
} 
相關問題