我目前正在开发用户搜索功能。搜索条件相当复杂,例如:
Footage >= 50 AND (SizeCode == SizeType.Large OR MobileEnd == "ABC")
Run Code Online (Sandbox Code Playgroud)
我认为标准/过滤模式可能适合此功能。 http://en.wikipedia.org/wiki/Criteria_Pattern
我的问题是:如何将参数添加到 MeetCriteria() 中?我需要一些功能,例如:
public List<Store> MeetCriteria(List<Store> entities, int footage)
public List<Store> MeetCriteria(List<Store> entities, string mobileEnd)
Run Code Online (Sandbox Code Playgroud)
括号解析起来很麻烦。将界面设计为“标准形式”(仅在顶层使用 OR)是否是一个好主意,以便上述搜索条件可以被接受为:
(Footage >= 50 AND SizeCode == SizeType.Large) OR
(Footage >= 50 AND MobileEnd == "ABC")
Run Code Online (Sandbox Code Playgroud)
谢谢。
长话短说
我相信一般来说,这个问题最好在 C# 中使用 LINQ 解决。具体来说,如果要将条件转换为 SQL 并应用于数据库,则可以将条件表示为表达式树,因为这些可以由实体框架等查询提供程序提供处理。
理由:
IMO,如果您尝试从头开始构建自己的 Criteria 或Evans 规范模式,正如您所建议的,您将需要构建一个模型或语言来定义您的标准,能够解析该模型(包括处理优先顺序等) ,并且还可能将其翻译成 Sql 或其他数据查询语言以有效地执行条件。在我看来,这将是一个漫长而痛苦的旅程。
下面是一个简单的示例,使用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();
}
Run Code Online (Sandbox Code Playgroud)
编辑
我想争论的一点是,LINQ 表达式树已经提供了类型安全、富有表现力、防 Sql 注入的 DSL,用于表达任何条件/规范模式。我建议您IQueryable<T>
尽快将表示层查询转换为强类型查询,从而避免对任何自定义标准语言/DSL 的需要。
仍然需要对查询进行一些验证,例如防止用户执行任意查询以及返回整个表。
如果您的物理架构具有跨层的序列化接口,请考虑使用查询的OData表示形式 - 这解决了如何执行谓词/条件的序列化/反序列化循环的问题。