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