复杂搜索条件的良好模式
本文关键字:模式 搜索 条件 复杂 | 更新日期: 2023-09-27 18:34:43
我目前正在研究用户搜索功能。搜索条件非常复杂,例如:
Footage >= 50 AND (SizeCode == SizeType.Large OR MobileEnd == "ABC")
我认为标准/过滤器模式可能适合此功能。http://en.wikipedia.org/wiki/Criteria_Pattern
我的问题是:如何将参数添加到满足标准((中?我需要一些功能,例如:
public List<Store> MeetCriteria(List<Store> entities, int footage)
public List<Store> MeetCriteria(List<Store> entities, string mobileEnd)
括号解析起来很麻烦。将界面设计为"标准形式"(仅在顶层使用 OR(是否是一个好主意,以便上述搜索条件可以接受为:
(Footage >= 50 AND SizeCode == SizeType.Large) OR
(Footage >= 50 AND MobileEnd == "ABC")
谢谢。
TL;DR
我相信一般来说,这个问题最好使用 LINQ 在 C# 中解决。具体而言,如果要将条件转换为 SQL 并应用于数据库,则条件可以表示为 Expression 树,因为这些树可以由实体框架等查询提供程序处理。
理由:
IMO 如果您尝试从头开始构建自己的标准或 Evans 规范模式,正如您所建议的那样,您将需要构建一个模型或语言来定义您的标准,能够解析此模型(包括处理优先级顺序等(,并且可能还将其转换为 Sql 或其他数据查询语言以有效地执行标准。这将是IMO漫长而痛苦的旅程。
下面是一个简单的示例,使用 Func<Store, bool>
谓词来构建条件并针对静态内存中集合执行它们。这绝不是完整的 - 所有这些都与And
完全匹配,尽管确实允许可选的过滤:
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();
}
编辑
我想论证的一点是,LINQ 表达式树已经提供了一个类型安全的、富有表现力的、 SQL 注入证明的 DSL,用于表达任何条件/规范模式。我建议您尽快将表示层查询转换为强类型IQueryable<T>
,从而避免对任何自定义条件语言/DSL的需求。
仍然需要对查询进行一些验证,例如,防止用户执行任意查询,例如返回整个表。
如果您的物理体系结构具有跨层的序列化接口,请查看使用查询的 OData 表示形式 - 这解决了如何执行谓词/条件的序列化/反序列化循环的问题。
规范模式是另一个候选者:https://en.wikipedia.org/wiki/Specification_pattern 。它允许您在现有标准的基础上构建更多标准。
以下是您将如何使用它:
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 中的泛型代码:
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);
}
引用:
- 规格设计模式:http://www.martinfowler.com/apsupp/spec.pdf