Как выбрать все родительские объекты в DataContext, используя один запрос LINQ? - PullRequest
0 голосов
/ 11 марта 2010

Я ищу ответ на конкретную проблему выборки всей иерархии объектов LINQ с помощью одного SELECT.

Сначала я пытался заполнить как можно больше объектов LINQ, используя LoadOptions, но 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 без таких хаков?

Ответы [ 3 ]

1 голос
/ 11 марта 2010

Ну ... я думаю, что вы дважды просматриваете свой «запрашиваемый»: в расширении 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;
        }
    }
1 голос
/ 11 марта 2010

Почему бы не позвонить LoadWith несколько раз?

В документации DataLoadOptions написано:

Каждый вызов LoadWith проверяет, имеют ли циклы [...]

(В разделе об избежании циклов.)

0 голосов
/ 23 марта 2010

Для тех, кто ищет точное решение этой проблемы, рассмотрите следующую сокращенную и рабочую версию кода для извлечения иерархии объектов LINQ в одном SELECT. Я изменил тип возврата функции 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
    );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...