复杂搜索条件的好模式?

6 c# design-patterns

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

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)

谢谢。

Stu*_*tLC 4

长话短说

我相信一般来说,这个问题最好在 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表示形式 - 这解决了如何执行谓词/条件的序列化/反序列化循环的问题。