基于Linq的谓词替代谓词<t>?</t>

时间:2010-05-06 19:13:26

标签: c# .net linq iqueryable

我有一个名为ICatalog的界面,如下所示,其中每个ICatalog都有一个名称和一个方法,它将根据Predicate<Item>函数返回项目。

public interface ICatalog
{
     string Name { get; }
     IEnumerable<Item> GetItems(Predicate<Item> predicate);
}

目录的特定实现可以链接到各种格式的目录,例如XML或SQL数据库。

使用XML目录,我最终将整个XML文件反序列化为内存,因此使用谓词函数测试每个项目并不会增加更多的开销,因为它已经存在于内存中。

然而,对于SQL实现,我宁愿不将数据库的全部内容检索到内存中,然后使用谓词函数过滤项目。相反,我想找到一种方法以某种方式将谓词传递给SQL服务器,或以某种方式将其转换为SQL查询。

这似乎是一个可以用Linq解决的问题,但我对它很陌生。我的界面应该返回IQueryable吗?我现在不关心如何实际实现我的ICatalog的SQL版本。我只是想确保我的界面将来允许它。

2 个答案:

答案 0 :(得分:7)

Rob已经指出了如何做到这一点(尽管更经典的LINQ方法可能需要Expression<Func<Item,bool>>,并且可能会返回IQueryable<IFamily>)。

好消息是,如果您想将谓词与LINQ-to-Objects一起使用(对于您的xml场景),那么您可以使用:

Predicate<Item> func = predicate.Compile();

或(对于其他签名):

Func<Item,bool> func = predicate.Compile();

并且您有一个委托(func)来测试您的对象。

问题是,这是单元测试的噩梦 - 你只能集成测试它。

问题是你不能可靠地模拟(使用LINQ-to-Objects)任何涉及复杂数据存储的东西;例如,以下内容在单元测试中可以正常工作,但不能对数据库“真实”工作:

var foo = GetItems(x => SomeMagicFunction(x.Name));

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not

问题是只有一些操作可以转换为TSQL。您遇到与IQueryable<T>相同的问题 - 例如,EF和LINQ-to-SQL支持对查询的不同操作;即使只是First()行为也不同(EF要求你先显式排序,但LINQ-to-SQL却没有。)

总结如下:

  • 它可以工作
  • 但仔细想想你是否愿意这样做;一个更经典的黑匣子存储库/服务界面可能更容易测试

答案 1 :(得分:5)

您无需一直走,并创建IQueryable implementation

如果您将GetItems方法声明为:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate);

然后,您的实现类可以检查Expression以确定要求的内容。

请阅读IQueryable文章,因为它解释了如何构建表达式树访问者,您需要构建一个简单版本的。