C# 面向对象和单元测试友好查询系统的设计

C# 面向对象和单元测试友好查询系统的设计,c#,unit-testing,design-patterns,complexity-theory,C#,Unit Testing,Design Patterns,Complexity Theory,我正在开发一个应用程序,允许牙医获取有关某些临床活动的信息。虽然应用程序不是高度可定制的(没有定制的工作流或表单),但它确实提供了一些基本的定制功能;客户机可以选择使用自己的自定义表单字段扩充预定义表单字段。管理员可以创建大约六种不同的字段类型(即文本、日期、数字、下拉列表等)。我们在持久性方面使用实体属性值(Entity Attribute Value,EAV)来建模此功能 应用程序的另一个关键特性是能够针对这些自定义字段创建自定义查询。这是通过一个UI来实现的,在这个UI中可以有任意数量的规

我正在开发一个应用程序,允许牙医获取有关某些临床活动的信息。虽然应用程序不是高度可定制的(没有定制的工作流或表单),但它确实提供了一些基本的定制功能;客户机可以选择使用自己的自定义表单字段扩充预定义表单字段。管理员可以创建大约六种不同的字段类型(即文本、日期、数字、下拉列表等)。我们在持久性方面使用实体属性值(Entity Attribute Value,EAV)来建模此功能


应用程序的另一个关键特性是能够针对这些自定义字段创建自定义查询。这是通过一个UI来实现的,在这个UI中可以有任意数量的规则(Date我认为LINQ到SQL将是一个理想的解决方案,可能与VS2008示例中的动态LINQ相结合。使用LINQ,特别是IEnumerable/IQueryable上的扩展方法,您可以根据得到的输入使用标准和自定义逻辑构建查询。我大量使用这种技术来实现fil我的许多MVC操作都非常有效。因为它实际上构建了一个表达式树,然后在需要具体化查询的地方使用它生成SQL,所以我认为它非常适合您的场景,因为大部分繁重的工作仍然由SQL server完成。在LINQ被证明生成非最优查询的情况下s您可以始终使用添加到LINQ数据上下文中的表值函数或存储过程作为方法来利用优化的查询

更新:简而言之,您也可以尝试使用C#3.0

示例:查找标题包含一组搜索词之一且出版商为O'Reilly的所有书籍

 var predicate = PredicateBuilder.True<Book>();
 predicate = predicate.And( b => b.Publisher == "O'Reilly" );
 var titlePredicate = PredicateBuilder.False<Book>();
 foreach (var term in searchTerms)
 {
     titlePredicate = titlePredicate.Or( b => b.Title.Contains( term ) );
 }
 predicate = predicate.And( titlePredicate );

 var books = dc.Book.Where( predicate );
var predicate=PredicateBuilder.True();
谓词=谓词和(b=>b.Publisher==“O'Reilly”);
var titlePredicate=PredicateBuilder.False();
foreach(搜索术语中的var术语)
{
titlePredicate=titlePredicate.Or(b=>b.Title.Contains(term));
}
谓词=谓词和(titlePredicate);
var books=dc.Book.Where(谓词);

我所看到的方法是创建对象,对您希望用户从中构建查询的每个条件进行建模,并使用这些条件构建对象树

从对象树中,您应该能够递归地构建满足查询的SQL语句

您需要的基本对象将是AND和OR对象,以及用于模型比较的对象,如EQUALS、LESSTHAN等。您可能希望为这些对象使用一个接口,以便以不同的方式将它们链接在一起

一个简单的例子:

public interface IQueryItem
{
    public String GenerateSQL();
}


public class AndQueryItem : IQueryItem
{
    private IQueryItem _FirstItem;
    private IQueryItem _SecondItem;

    // Properties and the like

    public String GenerateSQL()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(_FirstItem.GenerateSQL());
        builder.Append(" AND ");
        builder.Append(_SecondItem.GenerateSQL());

        return builder.ToString();
    }
}
以这种方式实现它应该允许您非常容易地对规则进行单元测试


消极的一面是,这个解决方案仍然让数据库做很多工作,这听起来好像你并不真的想做。

Tim,谢谢你的回复。我想你没有任何我可以研究的示例代码(除了你提到的动态LINQ示例)?这绝对是一个可行的解决方案,但我希望设计一个解决方案,避免与假脱机动态SQL(单元可测试性、隔离测试规则等)相关的陷阱。