«Глубокая загрузка» с LoadWith - PullRequest
1 голос
/ 20 августа 2010

У меня есть структура таблицы, каждая таблица с полем первичного ключа с именем "ID" и именами внешнего ключа, совпадающими с первичным ключом их родительской таблицы. Поэтому в таблицах ниже есть отношения, в которых их первичный ключ отображается в другой таблице, а первое поле в любой таблице - это его первичный ключ:

Category
--------
CategoryID
Title


CategoryList
------------
CategoryListID
CategoryID
ListID


List
----
ListID
Title


DataPoint
---------
DataPointID
RecordedDateTime


DataPointValue
--------------
DataPointValueID
DataPointID
TheValue

Выше приведено соединение «многие ко многим» между Category и List через CategoryList. Это также соединение «один ко многим» из списка в DataPoint, из DataPoint в DataPointValue.

Используя C # / LINQ и учитывая список значений CategoryID, я бы хотел получить:

Все записи списка, прикрепленные к категории, для которой у меня есть идентификаторы. С этими записями в Списке я хотел бы взять самую последнюю 1 DataPoint в соответствии с порядком RecordedDateTime Descending. Оттуда я хотел бы получить все данные DataPointValue, прикрепленные к DataPoint.

LINQ, который у меня есть:

Контекст DBDataContext = новый DBDataContext (ConnectionString);

        context.LoadOptions = new DataLoadOptions();
        context.LoadOptions.LoadWith<DataPoint>(p => p.DataPoints.OrderByDescending(p.RecordedDataTime).FirstOrDefault());

        // this next line is how I get the list of category IDs, but don't worry about that...
        List<int> categoryIDs = (from TreeNode n in nodes
                                 select Int32.Parse(n.Value)).Distinct().ToList();

        var lists = from i in context.List
                                         join ci in context.CategoryLists on i.ListID equals ci.ListID
                                         join p in context.DataPoints on i.ListID equals p.ListID
                                         join v in context.DataPointValues on p.DataPointID equals v.DataPointID
                                         where categoryIDs.Contains(ci.CategoryID)
                                         orderby i.Title ascending
                                         select new
                                         {
                                             List = i,
                                             DataPoint = p,
                                             DataPointValues = p.DataPointValues
                                         };

Но это, очевидно, не работает - LoadWith вызывает у меня проблемы. Может кто-нибудь объяснить, как построить LoadWith так, чтобы он вызывал как можно меньше запросов SQL для извлечения этого (предположительно большого) объема данных, пожалуйста?

Большое спасибо,

Мт.

1 Ответ

3 голосов
/ 30 сентября 2010

Вы спрашивали об этом месяц назад, но в любом случае вот ответ ...

Здесь есть несколько проблем:

  • Как только вы установите свойство LoadOptions в контексте, вы не сможете его изменить. Вам следует создать и настроить объект DataLoadOptions, а когда вы закончите, назначьте его контексту.

  • LoadWith указывает, какие потомки автоматически загружаются с родителем. Таким образом, loadOptions.LoadWith<DataPoint>(p => p.DataPointValues) будет автоматически загружать дочерние объекты DataPoint и не будет ждать, пока не будет получено свойство DataPoint.DataPointValues ​​(или как вы его называете) LoadWith делает загрузку не ленивой (нетерпеливой).

  • AssociateWith позволяет фильтровать и упорядочивать дочерние элементы, которые автоматически загружают отношения. Например, loadOptions.AssociateWith<DataPoint>(p => p.DataPointValues.OrderByDescending(v => v.TheValue)) будет сортировать DataPointValues ​​по значению.

И, наконец, я бы, вероятно, разбил ваш запрос на два, просто чтобы было проще.

// first setup a DataPoint -> DataPointValue relationship in your DBML
// then set up the DataPointValues to automatically load with DataPoint:

dataLoadOptions.LoadWith<DataPoint>(dp => dp.DataPointValues);

// then assign the load options to the context here

// First query
List<int> listIDs = context.CategoryLists
        .Where(cl => categoryIDs.Contains(cl.CategoryListID))
        .Select(cl => cl.ListID)
        .ToList();


// Second query(ies) - this isn't the most elegant, but simple is usually better :)
List<DataPoint> dataPoints = new List<DataPoint>();

foreach (int listID in listIDs)
{
    DataPoint foundDP = context.DataPoints
        .Where(dp => listIDs.Contains(dp.ListID))
        .OrderByDescending(dp => dp.RecordedDateTime)
        .Take(1)
        .SingleOrDefault();

        // Remember, at this point DataPointValues will already be loaded into the DataPoint

    if (foundDP != null)
        dataPoints.Add(foundDP);
}

В любом случае, это длинный ответ, который вам может понадобиться, а может и не понадобиться! Ну, это практика для меня, я думаю. Надеюсь, это поможет.

EDIT:

Извините, начал думать об этом ...

Вы могли бы сделать это вместо этого (чище, быстрее):

loadOptions.LoadWith<List>(l => l.DataPoints);
loadOptions.AssociateWith<List>(l => l.DataPoints.OrderByDescending(dp => dp.RecordedDateTime).Take(1));
loadOptions.LoadWith<DataPoint>(dp => dp.DataPointValues);

// assign the LoadOptions here,     
// then:

List<DataPoint> dataPoints = context.CategoryLists
        .Where(cl => categoryIDs.Contains(cl.CategoryID))
        .Select(cl => cl.List.DataPoints)
        .ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...