Вопрос

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

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

  • Мне бы хотелось иметь общий репозиторий

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

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

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

  • Это должно быть легко тестируемо

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

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

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

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

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

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

Это было полезно?

Решение

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

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()

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

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

Другие советы

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

Ну, на самом деле я не совсем понимаю реализацию вашего репозитория (напримеркаким будет этот метод .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;
    }
}

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

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

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

Принимая во внимание следующее:

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);
    }
}

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

interface IBannerRepository : IRepository<Banner>
{
}

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

class BannerRepository : Repository<Banner>, IBannerRepository
{
}

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

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

BannerRepository _repo = new BannerRepository();

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top