Здравствуйте, Sinclair, Вы писали:
P_K>>Во-первых, то что бизнес-сущности не всегда один-к-одному соответствуют таблицам я думаю вы согласитесь. В простейших случаях это следствие преднамеренной денормализации (храним список Id строкой, не охота городить таблицу; слабоструктурированные динамические данные — храним JSON в строке; ...).
S>Откуда в бизнес-логике взялся JSON? Это же всего лишь представление — оно появляется только при взаимодействии с другими системами.
Но ведь linq оперирует объектами, напрямую отражающими структуру таблиц?
Предположим что у нас прайс-лист для какого-то одного случая хранится в базе в JSON-строке. Когда я пишу логику на сущностях мне пофиг — DAL достанет этот JSON и переведёт его в объекты, которыми я уже и буду оперировать. А вот когда я пишу логику на linq? Как я смогу достать из этого прайс-листа несколько нужных мне позиций запросом? Вот я о чём.
P_K>>Во-вторых, поведение часто бывает динамическим. Давайте разовьём пример с заказами. Пусть всего в системе у заказа может быть пять способов его оплаты. Кроме того, заказ может входить в некие
S>Давайте напишем для начала всё это на чистом С#.
S>Вот у нас, допустим, класс Order. У него есть операция CalculateCost(). Как она устроена?
S>Мы будем делать 15 наследников класса Order c перегрузками?
Нет, всё не так. Заказ не считает свою цену
S>Или у нас будут свойства PaymentMethod и SupplyProgram, у которых методы GetPrice() и GetDiscount() — виртуальные?
Опять мимо. Я же написал, будут использоваться стратегии. Как-то так:
public interface IPricingStrategy { // Будет реализация, которая смотрит на заказ и по его свойствам определяет откуда брать цену, плюс простая реализация для тестов
decimal GetPrice(Order order);
}
public interfact IDiscountingStrategy { // Будет реализация, которая смотрит на заказ и по его свойствам определяет откуда брать скидку, плюс простая реализация для тестов
decimal GetDiscountPercents(Order oreder);
}
// Пересчёт стоимости заказа после изменений
private void RecalcOrderCost(Order order) {
var price = _pricingStrategy.GetPrice(order);
var discount = _discountingStrategy.GetDiscountPercents(order) / 100M;
order.Cost = order.Quantity * price * (1M - discount);
}
Нетестовые реализации стратегий могут быть разные — где-то в виде swith или набора if, где-то более изощрённо-динамически, но не суть.
S>Давайте детализировать.
S>S>var totalCost = (from pl in GetPriceList(PaymentMethod) join i in Items on pl.itemId equals i.itemId select i.quantity * pl.price).Sum();
S>
S>И его вычисление вместо нудных N+1 запросов превратится в нормальный join c агрегатом на стороне СУБД. Писать и отлаживать это ещё проще, чем пошаговую логику в традиционном ERP-приложении с Rich ORM и Lazy Load.
Но опять же, это будет отдельный запрос, который вернёт сразу сумму. А стоимость нужно будет потом проапдейтить в заказе — другим запросом.
Точно так же будет и в моём случае — реализация стратегии сбегает в базу, посчитает стоимость и отдаст сразу в виде decimal. А не в виде IQueriable<что-то>, что нужно ещё потом правильно сджинить. Кроме того, стратегия может бросить исключение, например, "для товара нет цены в прайс-листе", что является предсказуемой бизнес-ситуацией и обрабатывается. А вот как это выяснить для IQueriable?
S>При этом производительность будет как минимум на порядок выше.
Будет выше — скорее всего. На порядок или нет — спорить не готов.
Вернёмся к исходной проблеме. Мы хотим обеспечить ситуацию, чтобы на время обновления заказа использованный прайс-лист не мог быть изменён.
Что в вашем случае, что в моём поведение с т.з. базы одинаково — будет последовательность запросов:
1) найти заказ
2) найти цены
3) проапдейтить заказ
Ни тот ни другой подход не обеспечат того, что между 2 и 3 использованные в шаге 2 сущности не поменяются. Поэтому нет смысла продолжать спорить в рамках этой темы о том как лучше выбирать данные — через linq или нет.