代码之家  ›  专栏  ›  技术社区  ›  Luhmann

存储库和规范模式

  •  6
  • Luhmann  · 技术社区  · 14 年前

    我目前正在建立一个新的项目,我遇到了一些事情,我需要一些输入。

    这就是我正在考虑的:

    • 我想要一个通用存储库

    • 我不想从我的存储库返回iQueryable。

    • 我想将我的查询封装在规范中。

    • 我已经实现了规范模式

    • 它需要易于测试

    现在,我遇到了一些困难,我的问题是,哪种方法是调用具有一个或多个规范的find方法的最优雅的方法:

    (流利): bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()

    或者用我的规范将查询表示为lambda

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

    或者完全不同的方式?最重要的是,实现MVC前端的人应该对存储库有良好的直观体验。

    我希望实现的是保持SOM在结合规范方面的灵活性,并为规范提供“过滤”的经验,但不向控制器泄漏可查询,更像是可识别的,只允许使用规范而不使用LINQ修改查询。但是我是不是又回到了用这种方式向控制器泄漏查询逻辑的问题上了?

    3 回复  |  直到 11 年前
        1
  •  2
  •   David Lay    14 年前

    我见过一些使用属性作为规范的流畅的API,因此它们不会向客户机添加括号噪声。

    bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()
    

    being exec()针对repo执行规范的方法。

    但即使您不使用这些属性,我也会选择Fluent API,因为它的噪声最小。

        2
  •  2
  •   petrux    11 年前

    或者完全不同的方式?

    好吧,实际上我没有得到您的存储库实现(例如,方法 .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子句的访问者(但这只是一个问题 当然是这样)。最后,我将在存储库接口的“外部”编写规范(使用Fluent接口)。

    也许这只是一个幼稚的想法,但我觉得很简单。 希望这有帮助。

        3
  •  1
  •   Faizan S.    14 年前

    就我个人而言,我会选择lambda方式。这可能是因为我喜欢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);
    }   
    

    创建实现此接口的抽象“存储库”类:

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

    是的,这意味着你必须做一些工作,但是你以后更容易更改数据源。

    希望它有帮助!