Здравствуйте, zelenprog, Вы писали:
Z>Концептуально на этом уровне вроде все просто.
Z>Но как реализовать это — не совсем понятно. Проблема в следующем.
Z>Чтобы получить список аналогов для указанного списка товаров, Репозиторий аналогов должен записать список товаров во временную таблицу БД. А затем выполнить запрос, который фильтрует аналоги по товарам во временной таблице (SELECT * FROM analogs WHERE analog.tovar_id IN (SELECT tovar_id FROM temptable_tovar)).
Ошибка уже здесь. Временная таблица — это очень плохое решение:
1. Если несколько пользователей выполняют запросы одновременно, то их результаты перемешаются во временной таблице. Это можно решить, но потребуется набор костылей.
2. Даже если решить предыдущую проблему, производительность связанных запросов будет ограничена. Выполнение поиска аналогов как единого запроса позволяет СУБД построить оптимальный план в зависимости от селективности конкретных поисковых предикатов на конкретных данных. Принудительное сохранение во временную таблицу ограничивает возможный выбор планов, да ещё и создаёт нагрузку на IO там, где без неё можно было обойтись.
Z>Чтобы получить список упаковок для указанного списка товаров, Репозиторий упаковок должен сделать то же самое.
Z>Если каждый Репозиторий будет использовать "свою" временную таблицу — это будет неэффективно.
Z>Значит, чтобы дважды не формировать временную таблицу, оба Репозитория (и Репозиторий аналогов и Репозиторий упаковок) должны "знать" про одну и ту же временную таблицу, и понимать что указанный в методе GetListByTovar список товаров уже "лежит" во временной таблице.
Z>Как это сделать не нарушая ответственности слоев?
Лучше всего — работать с системой, которая позволяет манипулировать с запросами как с запросами.
Например, на linq:
var tovarList = TovarRepository.GetList(filterCritery); // никакого обращения в базу ещё нет, просто построен запрос.
/// запрос устроен так, что при его исполнении в базу поедет что-то вроде select * from tovar where ...
var analogList = AnalogRepository.GetListByTovar(tovarList); // никакого обращения в базу ещё нет, просто построен запрос
/// запрос устроен так, что при его исполнении в базу поедет что-то вроде select * from tovar where SELECT * FROM analogs WHERE analog.tovar_id IN (select * from tovar where ...)
Z>Конечно можно сделать что-то типа такого:
Z>Z>TovarList = TovarRepository.GetList(FilterCritery);
Z>TovarTempTable = TovarRepository.SaveTempTable(TovarList);
Z>AnalogList = AnalogRepository.GetListByTovar(TovarTempTable);
Z>PackList = PackRepository.GetListByTovar(TovarTempTable);
Z>
Если не получается прикрутить ленивые запросы с возможностями подстановки друг в друга, то можно обойтись простым вариантом, в котором AnalogRepository при построении GetListByTovars(IEnumerable<Tovar>) просто берёт и строит список ID переданных ему товаров:
SELECT * FROM analogs WHERE analog.tovar_id IN (4, 8, 15, 16, 23, 42)
Здесь нет никакой временной таблицы и связанных с ней проблем. Производительность всё равно будет хуже, чем в предыдущем варианте, но лучше любых других вариантов.