C# 存储库和规范模式

C# 存储库和规范模式,c#,.net,repository,specification-pattern,C#,.net,Repository,Specification Pattern,我目前正在建立一个新项目,我遇到了一些事情,我需要一些投入 这就是我正在考虑的: 我想要一个通用存储库 我不想从我的存储库返回IQueryable 我想将我的查询封装在规范中 我已经实现了规范模式 它需要易于测试 现在我遇到了一个小问题,我的问题是哪种方式是使用一个或多个规范调用find方法的最优雅的方式: (流利):bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner() 或者使用我的规范将查询表示为lamb

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

这就是我正在考虑的:

  • 我想要一个通用存储库

  • 我不想从我的存储库返回IQueryable

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

  • 我已经实现了规范模式

  • 它需要易于测试

现在我遇到了一个小问题,我的问题是哪种方式是使用一个或多个规范调用find方法的最优雅的方式:

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

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

(Lambda):
bannerRepository.Find.Where(banner=>banner.IsFrontendCampaignBanner和banner.IsSmallMediaBanner)

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


我希望实现的是,在能够组合规范方面保持som的灵活性,并提供对规范进行“过滤”的体验,但不会向控制器泄漏IQueryable,但更像是ISpecifiable,它只允许使用规范修改查询,而不允许使用Linq。但是我又回到了以这种方式向控制器泄漏查询逻辑的问题上了吗?

我看到一些Fluent API使用属性作为规范,因此它们不会向客户端添加括号噪声

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()
Being Exec()针对repo执行规范的方法


但即使您不使用属性,我也会选择fluent API,因为它的噪音最小。

我个人会选择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);
}   
是的,这意味着您必须做一些工作,但以后更改数据源会更容易

希望有帮助

或者完全不同的方式

事实上,我并不完全了解您的存储库实现(例如,方法
.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;
    }
}
公共类Foo
{ 
公共Int32种子{get;set;}
}
公共接口规范
{
bool满足(T项);
}
公共接口IfoosSpecification:ISpecification
{
不接受(如果oospecificationvisitor);
}
公共类SeedGreaterThanSpecification:IFooSpecification
{
公共SeedGreaterThanSpecification(int阈值)
{
这个阈值=阈值;
}
公共Int32阈值{get;private set;}
公共业务符合要求(Foo项目)
{
返回项.Seed>this.Threshold;
}
公共不接受(IfoosSpecificationVisitor)
{
回访者。参观(本);
}
}
公共接口IfoosSpecificationVisitor
{
T访问(种子大于规格接受者);
T访问(其他类型的规格接受者);
...
}
公共接口IFooRepository
{
IEnumerable Select(iFoosSpecification规范);
}
公共接口ISqlFooSpecificationVisitor:IFooSpecificationVisitor{}
公共类SqlFooSpecificationVisitor:ISqlFooSpecificationVisitor
{
公共字符串访问(种子大于规范接受者)
{
返回“Seed>”+acceptor.Threshold.ToString();
}
...
}
公共类存储库
{   
私人ISQLFooSpecification访客;
公共FooRepository(ISqlFooSpecificationVisitor)
{
this.visitor=访客;
}
公共IEnumerable选择(IFooSpecification规范)
{
string sql=“SELECT*FROM Foo WHERE”+specification.Accept(this.visitor);
返回此.DoSelect(sql);
}
私有IEnumerable DoSelect(字符串sql)
{
//进行实际选择;
}
}
因此,我有一个实体,它的规范接口和几个访问者模式中涉及的实现者,它的存储库接口接受规范接口,它的存储库实现,接受能够将规范转换为SQL子句的访问者(当然,这只是这种情况的一个问题)。最后,我将在存储库接口之外编写规范(使用fluent接口)

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

谢谢你的回答。我已经介绍了存储库的各个部分——与您的解决方案没有什么不同。我想要的是更多关于如何使用所提到的语法进行规范模式的输入。目前,fluent方法被广泛使用,可能是许多人的首选和支持。但如前所述,我个人会选择Lambda方式,因为对我来说,它确实更可读,看起来更整洁。但这只是我的偏好:PI使用了和你们在这里提出的Shaharyar非常相似的方法。我遇到的最大问题是可测试性。我仍然没有在单元测试中找到一种方法来验证是否调用了特定的linq表达式。我使用NSubstitute,但它不能这样做。我还查看了Moq,但找不到这样做的方法。使用这种方法,您将加载整个表并在内存中对其进行过滤,因为Func无法转换为“store”(其中(Func)是IEnumerable not IQueryable的扩展方法)。您应该使用Expression,这样您就可以使用IQueryable方法。其中(Expression)可以将条件转换为存储提供程序。+1我最后做了类似的事情。这个项目是
interface IBannerRepository : IRepository<Banner>
{
}
class BannerRepository : Repository<Banner>, IBannerRepository
{
}
BannerRepository _repo = new BannerRepository();

_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);
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;
    }
}