基于Linq的谓词替代谓词<T>?

Eri*_*tas 9 .net c# linq iqueryable

我有一个名为ICatalog如下所示的接口,其中每个接口ICatalog都有一个名称和一个基于Predicate<Item>函数返回项目的方法.

public interface ICatalog
{
     string Name { get; }
     IEnumerable<Item> GetItems(Predicate<Item> predicate);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Mar*_*ell 7

Rob已经表明你将如何做到这一点(虽然更经典的LINQ方法可能会采取Expression<Func<Item,bool>>,并可能返回IQueryable<IFamily>).

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

Predicate<Item> func = predicate.Compile();
Run Code Online (Sandbox Code Playgroud)

或(对于其他签名):

Func<Item,bool> func = predicate.Compile();
Run Code Online (Sandbox Code Playgroud)

你有一个委托(func)来测试你的对象.

但问题是,这是单元测试的噩梦 - 你只能真正集成测试它.

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

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

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not
Run Code Online (Sandbox Code Playgroud)

问题是只有一些操作可以转换为TSQL.您遇到同样的问题IQueryable<T>- 例如,EF和LINQ-to-SQL支持对查询的不同操作; 甚至只是First()表现不同(EF要求你先明确订购它,LINQ-to-SQL不要).

总结如下:

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


Rob*_*sor 5

您无需一直进行创建IQueryable实现

如果将GetItems方法声明为:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate);
Run Code Online (Sandbox Code Playgroud)

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

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