C# 复杂搜索条件的良好模式?

C# 复杂搜索条件的良好模式?,c#,design-patterns,C#,Design Patterns,我目前正在开发一个用户搜索功能。 搜索条件相当复杂,例如: Footage >= 50 AND (SizeCode == SizeType.Large OR MobileEnd == "ABC") 我认为标准/过滤器模式可能适合此功能。 我的问题是: 如何将参数添加到MeetCriteria()中? 我需要一些功能,如: public List<Store> MeetCriteria(List<Store> entities, int footage) pu

我目前正在开发一个用户搜索功能。 搜索条件相当复杂,例如:

Footage >= 50 AND (SizeCode == SizeType.Large OR MobileEnd == "ABC") 
我认为标准/过滤器模式可能适合此功能。

我的问题是: 如何将参数添加到MeetCriteria()中? 我需要一些功能,如:

public List<Store> MeetCriteria(List<Store> entities, int footage)

public List<Store> MeetCriteria(List<Store> entities, string mobileEnd)
谢谢。

TL;DR

我相信一般来说,这个问题最好在C#中使用LINQ解决。具体来说,如果要将这些条件转换为SQL并应用于数据库,则可以将这些条件表示为表达式树,因为它们可以由实体框架之类的查询提供程序进行处理

理由:

IMO如果您尝试构建自己的标准或从头开始,正如您所建议的,您将需要构建一个用于定义标准的模型或语言,能够解析此模型(包括处理优先顺序等),并可能将其转换为Sql或其他数据查询语言,以高效地执行标准。这将是一个漫长而痛苦的旅程

下面是一个简单的示例,使用
Func
谓词来建立条件并针对静态内存集合执行它们。这一点也不完整-所有这一切都与
完全匹配,但允许进行可选筛选:

private static readonly IEnumerable<Store> _myStores = new[]
{
    new Store {Footage = 100, MobileEnd = "XYZ", SizeCode = SizeCode.Small},
    new Store {Footage = 200, MobileEnd = "XYZ", SizeCode = SizeCode.Medium},
    new Store {Footage = 300, MobileEnd = "XYZ", SizeCode = SizeCode.Large},
    new Store {Footage = 150, MobileEnd = "ABC", SizeCode = SizeCode.Small},
    new Store {Footage = 250, MobileEnd = "ABC", SizeCode = SizeCode.Medium},
    new Store {Footage = 350, MobileEnd = "ABC", SizeCode = SizeCode.Large},
};

private static IEnumerable<Store> ApplyAndPredicates(IEnumerable<Func<Store, bool>> predicates)
{
    var filteredStores = _myStores;
    foreach (var predicate in predicates)
    {
        filteredStores = filteredStores.Where(predicate);
    }
    return filteredStores;
}

public static List<Store> MeetCriteria(List<Store> entities, int? footage = null, string mobileEnd = null, SizeCode? sizeCode = null)
{
    var predicates = new List<Func<Store, bool>>();
    if (footage.HasValue)
    {
        predicates.Add(s => s.Footage == footage.Value);
    }
    if (mobileEnd != null)
    {
        predicates.Add(s => s.MobileEnd == mobileEnd);
    }
    if (sizeCode != null)
    {
        predicates.Add(s => s.SizeCode == sizeCode);
    }
    return ApplyAndPredicates(predicates).ToList();
}
private static readonly IEnumerable\u myStores=new[]
{
新店{taines=100,MobileEnd=“XYZ”,SizeCode=SizeCode.Small},
新店{taines=200,MobileEnd=“XYZ”,SizeCode=SizeCode.Medium},
新店{taines=300,MobileEnd=“XYZ”,SizeCode=SizeCode.Large},
新店{taines=150,MobileEnd=“ABC”,SizeCode=SizeCode.Small},
新店{taines=250,MobileEnd=“ABC”,SizeCode=SizeCode.Medium},
新店{taines=350,MobileEnd=“ABC”,SizeCode=SizeCode.Large},
};
私有静态IEnumerable ApplyAndPredicates(IEnumerable谓词)
{
var filteredStores=_myStores;
foreach(谓词中的var谓词)
{
filteredStores=filteredStores.Where(谓词);
}
返回过滤器存储;
}
公共静态列表MeetCriteria(列表实体,int?taines=null,string mobileEnd=null,SizeCode?SizeCode=null)
{
var谓词=新列表();
如果(进尺值)
{
Add(s=>s.taines==taines.Value);
}
if(mobileEnd!=null)
{
Add(s=>s.MobileEnd==MobileEnd);
}
if(sizeCode!=null)
{
Add(s=>s.SizeCode==SizeCode);
}
返回ApplyAndPredicates(谓词).ToList();
}
编辑
我想说的一点是,LINQ表达式树已经为表达任何标准/规范模式提供了一种类型安全的、有表现力的、支持Sql注入的DSL。我建议您尽快将表示层查询转换为强类型的
IQueryable
,从而避免使用任何自定义标准语言/DSL

仍然需要对查询进行一些验证,例如防止用户执行任意查询,以及返回整个表


如果您的物理体系结构具有跨层的序列化接口,请查看使用查询表示法-这解决了如何执行谓词/条件的序列化/反序列化循环的问题。

规范模式是另一个候选模式:。它允许您在现有标准的基础上构建更多标准

以下是您将如何使用它:

var OverDue = new OverDueSpecification();
var NoticeSent = new NoticeSentSpecification();
var InCollection = new InCollectionSpecification();

// example of specification pattern logic chaining
var SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());

var InvoiceCollection = Service.GetInvoices();

foreach (var currentInvoice in InvoiceCollection) {
    if (SendToCollection.IsSatisfiedBy(currentInvoice))  {
        currentInvoice.SendToCollection();
    }
}
以及C#6.0中的通用代码:

公共接口规范
{
布尔是满意的(T候选人);
i规范和(i规范其他);
i指定与否(i指定其他);
i规范或(i规范其他);
ISpecification或Not(ISpecification其他);
isspecificationnot();
}
公共抽象类LINQ规范:复合规范
{
公共抽象表达式AsExpression();
public override bool issatifiedby(T candidate)=>AsExpression().Compile()(candidate);
}
公共抽象类复合规范:ISpecification
{
公开摘要bool满足(T候选人);
公共i规范和(i规范其他)=>新的和规范(本,其他);
公共isspecification and not(isspecification other)=>new and notspecification(this,other);
公共规范或(其他规范)=>新规范或规范(本规范或其他规范);
公共ISpecification OrNot(ISpecification other)=>新的ISpecification OrNot规范(this,other);
public isspecification Not()=>new NotSpecification(本);
}
公共类和规范:复合规范
{
左边是种;
具体化权;
公共和规范(i规范左侧,i规范右侧)
{
this.left=左;
这个。右=右;
}
公共覆盖bool issatifiedby(T候选者)=>left.issatifiedby(候选者)和&right.issatifiedby(候选者);
}
公共类和非规范:复合规范
{
左边是种;
具体化权;
公共和非规范(左侧为规范,右侧为规范)
{
this.left=左;
这个。右=右;
}
public override bool issatifiedby(T candidate)=>left.issatifiedby(candidate)和&right.issatifiedby(candidate)!=true;
}
公共类或规范:复合规范
{
左边是种;
具体化权;
公共或规范(i规范左侧,i规范右侧)
{
this.left=左;
这个。右=右;
}
public override bool issatifiedby(T candidate)=>left.issatifiedby(candidate)| | right.IsSatisf
var OverDue = new OverDueSpecification();
var NoticeSent = new NoticeSentSpecification();
var InCollection = new InCollectionSpecification();

// example of specification pattern logic chaining
var SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());

var InvoiceCollection = Service.GetInvoices();

foreach (var currentInvoice in InvoiceCollection) {
    if (SendToCollection.IsSatisfiedBy(currentInvoice))  {
        currentInvoice.SendToCollection();
    }
}
  public interface ISpecification<T>
    {
        bool IsSatisfiedBy(T candidate);
        ISpecification<T> And(ISpecification<T> other);
        ISpecification<T> AndNot(ISpecification<T> other);
        ISpecification<T> Or(ISpecification<T> other);
        ISpecification<T> OrNot(ISpecification<T> other);
        ISpecification<T> Not();
    }

    public abstract class LinqSpecification<T> : CompositeSpecification<T>
    {
        public abstract Expression<Func<T, bool>> AsExpression();
        public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
    }

    public abstract class CompositeSpecification<T> : ISpecification<T>
    {
        public abstract bool IsSatisfiedBy(T candidate);
        public ISpecification<T> And(ISpecification<T> other) => new AndSpecification<T>(this, other);
        public ISpecification<T> AndNot(ISpecification<T> other) => new AndNotSpecification<T>(this, other);
        public ISpecification<T> Or(ISpecification<T> other) => new OrSpecification<T>(this, other);
        public ISpecification<T> OrNot(ISpecification<T> other) => new OrNotSpecification<T>(this, other);
        public ISpecification<T> Not() => new NotSpecification<T>(this);
    }

    public class AndSpecification<T> : CompositeSpecification<T>
    {
        ISpecification<T> left;
        ISpecification<T> right;

        public AndSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
    }

    public class AndNotSpecification<T> : CompositeSpecification<T>
    {
        ISpecification<T> left;
        ISpecification<T> right;

        public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate) != true;
    }

    public class OrSpecification<T> : CompositeSpecification<T>
    {
        ISpecification<T> left;
        ISpecification<T> right;

        public OrSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
    }
    public class OrNotSpecification<T> : CompositeSpecification<T>
    {
        ISpecification<T> left;
        ISpecification<T> right;

        public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate) != true;
    }

    public class NotSpecification<T> : CompositeSpecification<T>
    {
        ISpecification<T> other;
        public NotSpecification(ISpecification<T> other) => this.other = other;
        public override bool IsSatisfiedBy(T candidate) => !other.IsSatisfiedBy(candidate);
    }