Создание фильтра по родителю как общей части в LINQ to SQL - PullRequest
0 голосов
/ 01 апреля 2011

Привет. Здесь вопрос. Предположим, у меня есть таблица Orders и две дочерние таблицы (на которые указывает внешний ключ), назовем их Suborders и Workhours.

Теперь я хотел бы получить набор Подзаказов и Рабочих часов из базы данных, где родительский элемент удовлетворяет некоторым критериям. Например (я использую лямбда-нотацию, так как мне удобнее):

var orders = dc.Orders.Where(a => (a.status == STATUS_WORK || a.status == STATUS_READY) && a.month == "201104");
foreach (var order in orders) {
  foreach (var sub in order.Suborders) 
    // Do something with sub that is required

  foreach (var work in order.Workhours)
    // Do something with work that is required
}

Теперь этот код выглядит великолепно, но его производительность довольно низкая. Поскольку для каждого заказа он выполняет два дополнительных запроса для извлечения дочерних записей для заказа . Я переписал это по-другому:

var subs = dc.Suborders.Where(a => (a.Order.status == STATUS_WORK || a.Order.status == STATUS_READY) && a.Order.month == "201104");

var works = dc.Workhours.Where(a => (a.Order.status == STATUS_WORK || a.Order.status == STATUS_READY) && a.Order.month == "201104");

foreach (var sub in subs) 
  // Do something with sub that is required

foreach (var work in order.Workhours)
  // Do something with work that is required

Теперь этот тип кода отлично работает с точки зрения производительности, но страдает от явного синдрома «Копировать и вставить». Если имеется несколько дочерних таблиц, и мы хотим выполнить для них одинаковую фильтрацию, код будет выглядеть очень некрасиво. Интересно, можно ли это сделать так:

bool IsValid(Order order) {
  return (order.status == STATUS_WORK || order.status == STATUS_READY) && order.month == "200104";
}

// ..
var subs = dc.Suborders.Where(a => IsValid(a.order));

var works = dc.Workhours.Where(a => IsValid(a.order));

foreach (var sub in subs) 
  // Do something with sub that is required

foreach (var work in order.Workhours)
  // Do something with work that is required

Попытка выполнить этот код запускается с ошибкой, поскольку поставщик LinqToSQL не может преобразовать это в соответствующий код SQL. Так есть ли способ указать этот предикат, чтобы LinqToSQL-провайдер мог его понять? Что-то вроде:

Predicate P(Order order) {
  P <=> (order.status == STATUS_WORK || order.status == STATUS_READY) && order.month == "200104";
}
// ..
var subs = dc.Suborders.Where(a => P(a.order));

var works = dc.Workhours.Where(a => P(a.order));

foreach (var sub in subs) 
  // Do something with sub that is required

foreach (var work in order.Workhours)
  // Do something with work that is required

Теперь, идея загрузки Orders со всеми дочерними объектами мне не очень нравится, и я не предлагаю ее, поскольку мне это не нужно. Потому что на самом деле могут быть дополнительные условия, такие как:

var subs = dc.Suborders.Where(a => P(a.order) && a.enabled == true);
var works = dc.Workhours.Where(a => P(a.order) && a.total>10.0f);

UPD:

ОК, позвольте мне более четко заявить, почему я не хочу использовать LoadWithOptions. Это потому, что это загрузит ненужную информацию здесь, в моем коде. Потому что родительская таблица заказов слишком велика. Кроме того, предположим более сложную иерархию. Допустим, Подзаказы также имеют дочернюю таблицу, назовем ее Бюджеты . Мне также нужно отфильтровать его по родителю родителя следующим образом:

var budgets = dc.Budgets.Where(a => (a.Suborder.Order.status == STATUS_WORK || a.Suborder.Order.status == STATUS_READY) && a.Suborder.Order.month == "201104");

Этот запрос будет извлекать только те бюджеты, которые мне нужны, но если я использую строгую загрузку, как предложено в комментариях, это также извлечет все подзаказы и заказы - слишком много ненужной информации и серьезная проблема с памятью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...