Существует несколько методов, которые вы можете использовать, лучший из которых будет зависеть от вашего конкретного сценария.
Вместо того, чтобы просто обсуждать логику поиска с точки зрения местоположения (например, в службе или в домене), может быть более полезно провести различие между местоположением спецификации и местоположением исполнения. Под расположением спецификаций я подразумеваю, в каких слоях вы указываете, в каких полях вы хотите искать. Под местом исполнения я подразумеваю немедленное или отложенное исполнение.
Если у вас есть несколько взаимоисключающих типов поиска (т. Е. В сценарии A, который вы хотите искать по CustomerId, а в сценарии B вы хотите искать по CustomerName), это может быть достигнуто путем создания репозитория для конкретного домена с выделенными методами. для каждого типа поиска или в .Net вы можете использовать выражение LINQ. Например:
доменный метод поиска:
_customers.WithName("Willie Nelson")
LINQ-запрос к хранилищу, реализующему IQueryable:
_customers.Where(c => c.Name.Equals("Willie Nelson")
Первый допускает более выразительную область, в то время как последний обеспечивает большую гибкость использования с немного сокращенным временем разработки (возможно, за счет читабельности).
Для более сложных критериев поиска вы можете использовать описанную вами методику передачи набора критериев поиска (строго типизированных или иным образом) или использовать шаблон спецификации . Преимущество шаблона спецификации заключается в том, что он обеспечивает более выразительный, богатый доменом язык запросов. Одним из примеров использования может быть:
_customers.MeetingCriteria(
Criteria.LivingOutsideUnitedStates.And(Criteria.OlderThan(55)))
Композиция, предоставляемая через шаблон спецификации, может предоставляться также через LINQ API .Net, но с меньшим контролем над указанием кода, раскрывающего намерения.
Что касается времени выполнения, репозитории могут быть написаны для обеспечения отложенного выполнения, возвращая IQueryable или позволяя передавать выражения LINQ для оценки методом репозитория. Например:
Отложенный запрос:
var customer = (from c in _customers.Query()
where c.Name == "Willie Nelson"
select c).FirstOrDefault();
Выполняется методом Query ():
var customer =
_customers.Query(q => from c in q
where c.Name == "Willie Nelson"
select c).FirstOrDefault();
Прежний метод Query (), который возвращает IQueryable, имеет преимущество, заключающееся в том, что его немного проще тестировать, поскольку Query () может быть легко вставлен в заглушку, чтобы обеспечить работу коллекции, вызываемую кодом, тогда как последний имеет преимущество в том, что детерминированный.
===== EDIT ====
Вдохновленный подходом Гаарона, я решил изменить свой ответ с помощью аналогичной техники. Его подход является своего рода перевернутым шаблоном спецификации, где спецификация выполняет фактический запрос. По сути, это делает запрос самостоятельным, поэтому давайте просто назовем его так:
public class SomeClass
{
// Get the ICustomerQuery through DI
public SomeClass(ICustomerQuery customerQuery)
{
_customerQuery = customerQuery;
}
public void SomeServiceMethod()
{
_customerQuery()
.WhereLivingOutSideUnitedStates()
.WhereAgeGreaterThan(55)
.Select();
}
}
Итак, где вы можете спросить хранилище? Нам здесь не нужен Наш ICustomerQuery может быть просто внедрен с IQueryable, который может быть реализован как вам угодно (возможно, регистрация IoC, которая просто возвращает следующее для NHibernate:
_container.Resolve<ISession>().Linq<Customer>()