Репозиторий и шаблон спецификации - PullRequest
6 голосов
/ 08 февраля 2010

Я сейчас настраиваю новый проект, и я столкнулся с несколькими вещами, где мне нужно немного информации.

Это то, что я рассматриваю:

  • Я бы хотел общий репозиторий

  • Я не хочу возвращать IQueryable из моего хранилища.

  • Я бы хотел заключить свои запросы в спецификации.

  • Я реализовал шаблон спецификации

  • Это должно быть легко проверяемым

Теперь я немного застрял, и мой вопрос - какой способ был бы наиболее элегантным способом вызова метода find с одной или несколькими спецификациями:

(Свободно): bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()

или выражайте запросы как лямбды с моими характеристиками

(лямбда): bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

или, может быть, каким-то другим способом? Самое главное, чтобы парень, реализующий фронт MVC, имел хороший интуитивный опыт работы с хранилищем.

Чего я надеюсь достичь, так это сохранить некоторую гибкость в том, что касается возможности сочетания спецификаций и предоставления опыта «фильтрации» со спецификациями, но без утечки IQueryable на контроллер, а скорее как ISpecifiable, это позволяет изменять запрос только со спецификациями, а не с Linq. Но я только вернулся к утечке логики запросов к контроллеру таким образом?

Ответы [ 3 ]

2 голосов
/ 04 сентября 2013

или, может быть, каким-то другим способом?

Ну, на самом деле, я не совсем понимаю вашу реализацию репозитория (например, что вернет метод .Find()?), Но я бы выбрал другое направление:

public class Foo 
{ 
    public Int32 Seed { get; set; }
}

public interface ISpecification<T> 
{
    bool IsSatisfiedBy(T item);
}

public interface IFooSpecification : ISpecification<Foo> 
{
    T Accept<T>(IFooSpecificationVisitor<T> visitor);
}

public class SeedGreaterThanSpecification : IFooSpecification
{
    public SeedGreaterThanSpecification(int threshold)
    {
        this.Threshold = threshold;
    }
    public Int32 Threshold { get; private set; }
    public bool IsSatisfiedBy(Foo item) 
    {
        return item.Seed > this.Threshold ;
    }
    public T Accept<T>(IFooSpecificationVisitor<T> visitor)
    {
        return visitor.Visit(this);
    }
}
public interface IFooSpecificationVisitor<T>
{
    T Visit(SeedGreaterThanSpecification acceptor);
    T Visit(SomeOtherKindOfSpecification acceptor);
    ...
}
public interface IFooRepository 
{
    IEnumerable<Foo> Select(IFooSpecification specification);
}
public interface ISqlFooSpecificationVisitor : IFooSpecificationVisitor<String> { }
public class SqlFooSpecificationVisitor : ISqlFooSpecificationVisitor
{
    public string Visit(SeedGreaterThanSpecification acceptor)
    {
        return "Seed > " + acceptor.Threshold.ToString();
    }
    ...
}
public class FooRepository
{   
    private ISqlFooSpecificationVisitor visitor;

    public FooRepository(ISqlFooSpecificationVisitor visitor)
    {
        this.visitor = visitor;
    }

    public IEnumerable<Foo> Select(IFooSpecification specification)
    {
        string sql = "SELECT * FROM Foo WHERE " + specification.Accept(this.visitor);
        return this.DoSelect(sql);
    }

    private IEnumerable<Foo> DoSelect(string sql)
    {
        //perform the actual selection;
    }
}

Итак, у меня есть объект, его интерфейс спецификации и несколько разработчиков, участвующих в шаблоне посетителя, его интерфейс хранилища, принимающий интерфейс спецификации, и его реализация хранилища, принимающая посетителя, способного переводить спецификации в предложения SQL (но это всего лишь вопрос это случай, конечно). Наконец, я бы составил спецификацию «вне» интерфейса репозитория (используя свободный интерфейс).

Может быть, это просто наивная идея, но я нахожу ее довольно простой. Надеюсь, это поможет.

2 голосов
/ 08 февраля 2010

Я видел некоторые Fluent API, которые используют свойства для спецификаций, поэтому они не добавляют шум круглых скобок для клиентов.

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()

Будучи Exec (), метод для выполнения спецификаций в отношении репо.

но даже если вы не используете свойства, я бы выбрал свободный API, поскольку он имеет минимальный шум.

1 голос
/ 08 февраля 2010

Лично я бы пошел по пути лямбды. Это может быть из-за моей любви к лямбде, но она предоставляет много места для общей настройки репозитория.

С учетом следующего:

bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

Я не знаю, как выглядит ваш паттерн, но вы могли бы изменить некоторые вещи здесь:

Создайте универсальный интерфейс с именем «IRepository» типа, содержащий все методы для доступа к данным.

Это может выглядеть так:

interface IRepository<T> where T : class
{
    IEnumerable<T> FindAll(Func<T, bool> exp);

    T FindSingle(Func<T, bool> exp);
}   

Создайте абстрактный класс 'Repository', реализующий этот интерфейс:

class Repository<T> : IRepository<T> where T : class
{
    TestDataContext _dataContext = TestDataContext();

    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Where<T>(exp);
    }

    public T FindSingle(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Single(exp);
    }
}

Теперь мы можем создать интерфейс для таблицы / объектов баннеров, который реализует наш «IRepository» и конкретный класс, расширяющий абстрактный класс «Repository» и реализующий «IBannerInterface»:

interface IBannerRepository : IRepository<Banner>
{
}

И соответствующий репозиторий для его реализации:

class BannerRepository : Repository<Banner>, IBannerRepository
{
}

Я бы предложил использовать этот подход, поскольку он дает вам большую гибкость и достаточную мощность для управления всеми крошечными объектами, которые у вас есть.

Вызывать эти методы будет очень просто:

BannerRepository _repo = new BannerRepository();

_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);

Да, это означает, что вам нужно выполнить какую-то работу, но вам будет гораздо проще изменить источник данных позже.

Надеюсь, это поможет!

...