我有一个名为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版本。我只是想确保我的界面将来允许它。
答案 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文章,因为它解释了如何构建表达式树访问者,您需要构建一个简单版本的。