Привет. Здесь вопрос. Предположим, у меня есть таблица 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");
Этот запрос будет извлекать только те бюджеты, которые мне нужны, но если я использую строгую загрузку, как предложено в комментариях, это также извлечет все подзаказы и заказы - слишком много ненужной информации и серьезная проблема с памятью.