首页 > 解决方案 > 为内存集合实现规范模式时塑造数据的优雅方式

问题描述

是否有任何健壮、优雅和/或标准的方式来实现内存中集合(IEnumerable<T>而不是IQueryable<T>)的规范模式,包括塑造/投影结果?

使用Func<T, bool>for 标准显然只涵盖Where子句/方法,而不是Select.

到目前为止,我想出的想法是我可以在我的规范中包含另一个委托(不是双关语)专门涵盖该Select操作。实现可能如下所示。正如您在底部看到的那样,存储库只是简单地执行Whereand Select,并传入规范的委托成员。

这个解决方案似乎工作得很好,但我在很多场合发现我正在解决的问题存在现有的更好的解决方案,所以问这个问题似乎是合理的。

(我计划使用规范模式的原因是,我的应用程序可能需要从复杂对象的内存集合中以各种形状显示大量结果,并且将查询内容保持在一个单一的,易于查找/管理的地方。但请随意提出完全不同的建议。)

internal interface IMySpecification<TEntity, TResult>
{
    Func<TEntity, bool> Where { get; }
    Func<TEntity, TResult> Select { get; }
    bool IsSatisfiedBy(TEntity t);
    // ...  
}

internal interface IMyRepository<TEntity>
{
    // ...
    TResult GetSingle<TResult>(IMySpecification<TEntity, TResult> spec);
    IEnumerable<TResult> List<TResult>(IMySpecification<TEntity, TResult> spec);
}

internal class MyGenericRepository<T> : IMyRepository<T>
{
    protected IEnumerable<T> _collection;

    public MyGenericRepository(IEnumerable<T> list)
        => _collection = list;

    // ...

    public TResult GetSingle<TResult>(IMySpecification<T, TResult> spec)
        => List(spec).Single();

    public IEnumerable<TResult> List<TResult>(IMySpecification<T, TResult> spec)
        => (IEnumerable<TResult>)_collection.Where(spec.Where).Select(spec.Select);
}

标签: c#.netienumerablespecification-pattern

解决方案


在评论中进行了简短讨论后,我认为回答我自己的问题很合适:

您通常不应该这样做。

如果您因为想要实现相同的东西而最终来到这里,那么您可能需要退后一步,并考虑您实际上要解决的问题是什么。

通过将规范与数据的塑造相结合,您在很大程度上违背了规范的目的:多个消费者不能再以灵活的方式使用规范,因为不同的消费者可能非常希望使用不同形状的数据,链接/组合规格等。

  • 如果您不需要这种灵活性,那么您可能可以从设计中删除规范层(例如,只需在存储库中公开方法)。
  • 如果您确实需要这种灵活性,请让消费者塑造/映射数据。

(如果您有任何要添加的内容,请随时编辑此答案。)


推荐阅读