Советы с шаблоном проектирования репозитория / сервисного уровня - PullRequest
2 голосов
/ 25 августа 2010

Попытка сделать действительно простой репозиторий и шаблон уровня сервиса здесь.(.NET 4, C #, LINQ, хотя этот вопрос частично не зависит от языка).Примечание: это всего лишь R & D.

Моя цель - минимизировать количество определений методов в моем слое обслуживания.

Вот мой контракт с репозиторием:

interface IFooRepository
{
   IEnumerable<Foo> Find();
   void Insert(Foo foo);
   void Update(Foo foo);
   void Delete(Foo foo);
}

Ничего новоготам.

Теперь вот что я (пытаюсь) иметь в своем контракте на обслуживание:

interface IFooDataService
{
   public IEnumerable<Foo> Find(FooSearchArgs searchArgs);
}

По сути, любой конкретный «Foo» имеет много свойств (id, name и т. д.),который я хотел бы иметь возможность искать.

Итак, я не хочу иметь 1x метод Find для каждого отдельного свойства, я просто хочу один - таким образом, когда я создаю дополнительные свойства, мне не нужно изменятьконтракты.

"FooSearchArgs" - это просто простое POCO со всеми различными свойствами "Foo".

Итак, вот что я пытаюсь сделать, вот мои вопросы:

  • Это плохой дизайн?Если да, каковы альтернативы?
  • Как я могу реализовать эту фильтрацию на уровне сервиса?Должен ли я проверить, какие свойства "FooSearchArgs" установлены, а затем продолжить фильтрацию?(если это, то query.where, если this, query.where и т. д.) У кого-нибудь есть идея умного метода расширения LINQ IEnumerable, чтобы сделать это?(т.е. repository.WhereMeetsSearchCriteria(fooSearchArgs))

Цените помощь.

Ответы [ 2 ]

3 голосов
/ 25 августа 2010

Мы используем нечто очень похожее.Вам нужно определиться с тем, хотите ли вы выставить IQueryable вне репозитория.Ваш метод find возвращает IEnumerable, который может быть IQueryable, возвращаемым из условия when.

Преимущество возврата IQueryable состоит в том, что вы можете еще больше уточнить свои критерии вне уровня хранилища.1005 * Выражение будет скомпилировано только тогда, когда вы начнете использовать возвращенные данные, и здесь есть недостаток.Поскольку вы обращаетесь к базе данных только тогда, когда действительно используете результаты, вы можете попытаться вызвать базу данных после того, как ваш сеанс (nhibernate) или соединения были закрыты.

Мое личное предпочтение - использовать шаблон спецификациигде вы передаете свой метод find, для выполнения запроса используется объект ISpecification.

public interface ISpecification<TCandidate>
{
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source);
}

public class TestSpecification : ISpecification<TestEntity>
{
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source)
    {
        return source.Where(x => x.SomeValue == 2);
    }
}

public class ActiveRecordFooRepository: IFooRepository
{
    ...

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        ...

        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray();

        ...
    }

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First();
    }
}

После выполнения запроса репозиторий вызывает ToArray или ToList для результирующего IQueryable, возвращенного из спецификации, так что запрос оцениваетсятам и тогда.Хотя это может показаться менее гибким, чем предоставление IQueryable, оно имеет несколько преимуществ.

  1. Запросы выполняются сразу и предотвращают вызов в базу данных после закрытия сеансов.
  2. ПосколькуВаши запросы теперь объединены в спецификации, которые можно тестировать на единицу.
  3. Спецификации можно использовать повторно, что означает, что у вас нет дублирования кода при попытке выполнить похожие запросы, и любые ошибки в запросах нужно исправить только в одном месте.
  4. При правильной реализации вы также можете объединить свои спецификации.

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));
0 голосов
/ 25 августа 2010

Передает ли Func в качестве параметра методу Find вашего сервисного уровня вместо FooSearchArgs, вариант?В перечислимых объектах есть метод Where (linq), который принимает Func в качестве параметра, поэтому вы можете использовать его для фильтрации результатов.

...