2010-03-11 34 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 
    ); 
} 
相关问题