标签: specification-pattern

规格模式,Func <T,bool>谓词和管道和过滤器的比较

我正在做一些研发工作,因此正在研究设计模式.我最近一直在阅读规范模式,并参考了这篇伟大的文章.

我对代码的简洁性和清洁度很感兴趣,但我开始比较使用其他技术实现相同的清洁度.

考虑以下服务层的接口合同:

public interface IFooDataService
{
   ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
   ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
   ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}
Run Code Online (Sandbox Code Playgroud)

所以,一些初步要点:

  • 这三个都返回了Foo对象的集合
  • 这三个人只有一个论点
  • 规范方法限制对特定要求的访问
  • 谓词方法基本没有限制
  • 搜索args方法限制对特定要求的访问

现在,进入实施:

public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
    return fooDataRepository
            .Find()
            .Where(f => specification.IsSatisfiedBy(f))
            .ToList();
}

public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
    return fooDataRepository
            .Find()
            .Where(predicate)
            .ToList();
}

public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
    return fooDataRepository
            .Find()
            .WhereMeetsSearchCriteria(searchArgs)
            .ToList();
}
Run Code Online (Sandbox Code Playgroud)

实施要点:

  • 这三个都非常简单的实现(一行链式代码)
  • 规范和搜索Args过滤在外部实施.
  • 搜索args方法只是使用IEnumerable扩展方法来检查args

那么,话虽如此,在什么条件下你会使用上述3种技术之一?

我对规范模式的看法:

  • 很好,它将业务/域要求隔离成可重用的组件
  • 非常容易阅读,使代码说英语
  • 涉及一些代码(接口,抽象类).如果我要使用它,我会将抽象放在一个通用的程序集中(所以我的解决方案中没有一堆静态文件).
  • 仅通过更改规范而非服务层来轻松更改要求.
  • 域逻辑的最高可测试性(规范)

我对扩展方法(管道和过滤器)的看法:

  • 逻辑上"重量级",但仍然导致相同的简单性.
  • 将查询逻辑从服务层隔离到静态方法
  • 仍然需要排序的"反射"(检查提供的搜索参数和构建查询)
  • 允许您首先编写体系结构(存储库,服务层),而不考虑特定的业务需求(在某些情况下很方便)

我对谓词方法的看法:

  • 可以在需要对查询进行粗粒度控制的地方使用. …

architecture extension-methods design-patterns specification-pattern pipes-filters

7
推荐指数
1
解决办法
1042
查看次数

存储库和规范模式

我目前正在建立一个新项目,我遇到了一些问题,我需要一点点输入.

这就是我在考虑的问题:

  • 我想要一个通用的存储库

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

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

  • 我已经实现了规范模式

  • 它需要易于测试

现在这是我陷入困境的地方,我的问题是哪种方式是使用一个或多个规范调用find方法的最优雅方式:

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

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

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

或者可能是其他一些方式?最重要的是,实现MVC前端的人应该具有良好的存储库直观体验.

我希望实现的是保持som灵活性,以便能够结合规范,并提供"过滤"的经验与规范,但不会泄漏IQueryable到控制器,但更像是一个ISpecifiable,只允许使用规范而不是Linq修改查询.但我是否只是以这种方式将查询逻辑泄漏给控制器?

.net c# repository specification-pattern

6
推荐指数
1
解决办法
4373
查看次数

规格模式与扩展方法?

我试图掌握规范模式,我对它有点困惑.我真的觉得它对我的具体要求没有帮助.我想知道如果我更喜欢扩展方法来解决我的复杂问题,那会是什么问题?例如

public static class ProductExtensions
{
    public static IQueryable<Product> InStocks(this IQueryable<Product> query)
    {
        return query.Where(p => p.InStock && !p.IsDeleted /*others goes here*/);
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现用扩展方法包装我的长规格而不是使用spesification模式很有帮助.这有什么问题?

.net c# specification-pattern

6
推荐指数
2
解决办法
1129
查看次数

规范模式异步

我正在尝试将规范模式应用于我的验证逻辑.但我在异步验证方面遇到了一些问题.

假设我有一个AddRequest需要验证的实体(有2个字符串属性FileName和Content).

我需要创建3个验证器:

  1. 验证FileName是否包含无效字符

  2. 验证内容是否正确

  3. 异步验证数据库中是否存在具有FileName的文件.在这种情况下,我应该有类似的东西Task<bool> IsSatisfiedByAsync

但我怎么能同时实现IsSatisfiedByIsSatisfiedByAsync?我应该创建2个接口ISpecification,IAsyncSpecification或者我可以在一个接口中创建吗?

我的版本ISpecification(我只需和)

    public interface ISpecification
    {
        bool IsSatisfiedBy(object candidate);
        ISpecification And(ISpecification other);
    }
Run Code Online (Sandbox Code Playgroud)

AndSpecification

public class AndSpecification : CompositeSpecification 
{
    private ISpecification leftCondition;
    private ISpecification rightCondition;

    public AndSpecification(ISpecification left, ISpecification right) 
    {
        leftCondition = left;
        rightCondition = right;
    }

    public override bool IsSatisfiedBy(object o) 
    {
        return leftCondition.IsSatisfiedBy(o) && rightCondition.IsSatisfiedBy(o);
    }
}
Run Code Online (Sandbox Code Playgroud)

要验证文件是否存在,我应该使用:

 await _fileStorage.FileExistsAsync(addRequest.FileName);
Run Code Online (Sandbox Code Playgroud)

IsSatisfiedBy如果我确实需要那个异步,我怎么能写那个支票呢?

例如,这里是FileName的验证器(1)

public …
Run Code Online (Sandbox Code Playgroud)

c# design-patterns domain-driven-design specification-pattern async-await

6
推荐指数
1
解决办法
326
查看次数

使用带有规范模式的预先加载

我已经使用Linq实现了规范模式,如下所述https://www.packtpub.com/article/nhibernate-3-using-linq-specifications-data-access-layer

我现在想要增加急切负载的能力,并且不确定最好的方法.

链接示例中的通用存储库类:

public IEnumerable<T> FindAll(Specification<T> specification)
{
  var query = GetQuery(specification);
  return Transact(() => query.ToList());
}

public T FindOne(Specification<T> specification)
{
  var query = GetQuery(specification);
  return Transact(() => query.SingleOrDefault());
}

private IQueryable<T> GetQuery(
  Specification<T> specification)
{
  return session.Query<T>()
    .Where(specification.IsSatisfiedBy());
}
Run Code Online (Sandbox Code Playgroud)

并且规范实现:

public class MoviesDirectedBy : Specification<Movie>
{

 private readonly string _director;

 public MoviesDirectedBy(string director)
 {
   _director = director;
 }

 public override
    Expression<Func<Movie, bool>> IsSatisfiedBy()
 {
   return m => m.Director == _director;
 }
}
Run Code Online (Sandbox Code Playgroud)

这很好用,我现在想要添加能够加载的能力.我理解NHibernate的热切加载可以通过在查询上使用Fetch来完成.

我正在寻找的是是否在规范中封装急切的加载逻辑或将其传递到存储库,以及实现此目的所需的Linq /表达式树语法(即如何完成它的示例).

linq nhibernate specification-pattern repository-pattern eager-loading

5
推荐指数
1
解决办法
999
查看次数

在规范中组合 C# 代码和数据库代码

有时您需要定义一些业务规则,规范模式是一个有用的工具。例如:

public class CanBorrowBooksSpec : ISpecification<Customer>
{
    public bool Satisfies(Customer customer)
    {
         return customer.HasLibraryCard
              && !customer.UnpaidFines.Any();
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,我经常发现我需要将这些规则“推送”到 SQL 中以提高性能或满足记录分页列表之类的要求。

然后,我必须为规则编写代码两次,一次使用 CLR 代码,一次使用 SQL(或 ORM 语言)。

您如何组织这样的代码?

最好将代码放在同一个类中。这样,如果开发人员正在更新业务规则,他们忘记更新两组代码的可能性就较小。例如:

public class CanBorrowBooksSpec : ISpecification<Customer>
{
    public bool Satisfies(Customer customer)
    {
         return customer.HasLibraryCard
              && !customer.UnpaidFines.Any();
    }

    public void AddSql(StringBuilder sql)
    {
        sql.Append(@"customer.HasLibraryCard 
                     AND NOT EXISTS (SELECT Id FROM CustomerUnpaidFines WHERE CustomerId = customer.Id)");
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这对我来说似乎很难看,因为我们现在将担忧混合在一起。

另一种替代方案是使用 Linq-To-YourORM 解决方案,因为 LINQ 代码可以针对集合运行,也可以转换为 SQL。但我发现,除了最琐碎的场景之外,这种解决方案几乎不可能实现。

你做什么工作?

c# specification-pattern

5
推荐指数
1
解决办法
4508
查看次数

Linq:如何针对关联对象使用规范

我正在使用这种形式的规格:

public static Expression<Func<User, bool>> IsSuperhero
{
  get
  {
    return x => x.CanFly && x.CanShootLasersFromEyes;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以在表单中使用此规范:

var superHeroes = workspace.GetDataSource<User>().Where(UserSpecifications.IsSuperhero);
Run Code Online (Sandbox Code Playgroud)

但是我不确定如何对这样的关联对象使用规范:

var loginsBySuperheroes = workspace.GetDataSource<Login>().Where(x => x.User [ ??? ]);
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点,还是我需要重新考虑我的规范实施?

c# linq expression-trees specification-pattern

5
推荐指数
1
解决办法
531
查看次数

如何将规范模式与原始 SQL 结合起来

我尝试使用 CQRS 以 DDD 方式实现一些应用程序。我使用 asp.net core 应用程序,以及 Microsoft(在微服务书中)我使用 Mediatr。我想实现文章的过滤。为此,人们建议使用规范模式并将所有规范存储在域中(什么层应包含 DDD 中的查询)。

但是如果我使用dapper,我该如何处理解析表达式呢?对于 EF,这不是问题,但对于原始 SQL,却是问题。我认为不值得将表达式树解析器和转换器实现为 SQL。更简单的方法是在 Domain 中存储一些 FilterModel,然后通过在基础设施模型中遍历该模型来构建 SQL 或 SphinxQl。该解决方案符合 DDD 原则吗?我将不胜感激的建议)

sql orm domain-driven-design specification-pattern

5
推荐指数
0
解决办法
3094
查看次数

EF Core 规范模式添加所有列,用于使用自定义规范对数据进行排序

我为我的 .net core 项目应用了规范模式。我还为包含、排序、分页等创建了自定义规范。

sort使用 queryString 从 api url 获取值并将其传递给自定义规范类。在本课程中,我添加了一些switch case用于确定哪一列应该orderByorderByDescending

但该表中的列太多。那么有什么方法可以将该sort变量同时应用于所有列吗?或者我必须将所有列写入switch

这是我的自定义规范类。

public class PersonsWithGroupsAndPrivileges : BaseSpecification<Person>
{
public PersonsWithGroupsAndPrivileges(string sort) : base()
{
    AddInclude(x => x.Group);
    AddInclude(x => x.Privilege);
    
    if(!string.IsNullOrEmpty(sort))
    {
        switch (sort)
        {
            case "gender": ApplyOrderBy(x => x.Gender); break;
            case "genderDesc": ApplyOrderByDescending(x => x.Gender); break;
            case "roomNo": ApplyOrderBy(x => x.RoomNo); break;
            case "roomNoDesc": ApplyOrderByDescending(x => x.RoomNo); break;
            default: ApplyOrderBy(x => x.Name); break;
        }
    } …
Run Code Online (Sandbox Code Playgroud)

c# specification-pattern entity-framework-core .net-core

5
推荐指数
1
解决办法
4685
查看次数

如何在 ardalis.Specification 库中定义选择器?

我正在尝试利用Ardalis.Specification库在我的 asp.net 6 项目中应用规范模式。

安装库后,我创建了以下规范

public class ProductByIdsSpec : Specification<Product, ProductMenuItem>
{
    public ClientRecordByIdsSpec(IEnumerable<int> ids)
    {
        if (ids == null || !ids.Any())
        {
            return;
        }

        Query.Where(x => ids.Contains(x.Id));


        // some how I need to map Product to ProductMenuItem so only the needed columns are pulled from the database.
    }

}
Run Code Online (Sandbox Code Playgroud)

我不想Product从数据库中提取每个值,而是只想通过将数据投影到ProductMenuItem. 上述规范返回以下错误

SelectorNotFoundException Ardalis.Specification.SelectorNotFoundException:规范必须定义选择器

如何定义实体(即Product)和结果对象(即ProductMenuItem)之间的映射?

我尝试添加Select()定义但给了我同样的错误

public class ProductByIdsSpec : Specification<Product, ProductMenuItem>
{
    public ClientRecordByIdsSpec(IEnumerable<int> ids)
    {
        if …
Run Code Online (Sandbox Code Playgroud)

c# specifications specification-pattern asp.net-core ardalis-specification

5
推荐指数
1
解决办法
3006
查看次数