LINQ的基于通用的替代品predicate< T>?替代品、LINQ、predicate、GT

2023-09-04 00:39:42 作者:不期而遇

我有一个名为接口 ICatalog 如下图所示,其中每个 ICatalog 有一个名字和一个将恢复方法根据项 predicate<项目> 功能

 公共接口ICatalog
{
     字符串名称{; }
     IEnumerable的<项目> GetItems(predicate<项目> predicate);
}
 

目录的具体实施可能与目录的各种格式,如XML或SQL数据库。

通过一个XML目录我最终反序列化整个XML文件到内存中,所以测试每个项目的predicate功能并不会增加很多更多的开销,因为它已经在内存中。

不过,与SQL实现我宁愿不检索数据库的全部内容到内存中,然后过滤用predicate功能的项目。相反,我想找到一种方法,以某种方式通过predicate到SQL服务器,或以某种方式将其转换为SQL查询。

Linq Join Lambda Join

这似乎是可以与LINQ的解决的问题,但我pretty的新的给它。如果我的界面返回IQueryable的呢?我不担心,现在有如何真正实现我ICatalog的SQL版本。我只是想确保我的界面将允许它的未来。

解决方案

罗布表明你怎么可能做到这一点(虽然比较经典的LINQ的方法可能采取防爆pression< Func键<项目,布尔>> 和possbily返回的IQueryable< IFamily>

好消息是,如果你想使用predicate使用LINQ到对象(对于你的XML方案),则只能使用方法:

  predicate<项目> FUNC = predicate.Compile();
 

或者(对于其它签名):

  Func键<项目,布尔> FUNC = predicate.Compile();
 

和你有一个委托( FUNC )与测试对象。

这个问题虽然是这是一个的的噩梦的单元测试 - 你只能真正的集成的测试

现在的问题是,你不能可靠地模拟(使用LINQ到对象)任何涉及复杂的数据存储;例如,下面将正常工作在你的单元测试,但将无法正常工作真正的对数据库:

 变种富= GetItems(X => SomeMagicFunction(x.Name));

静态布尔SomeMagicFunction(字符串名称){返回name.Length> 3; } // 为什么不呢
 

的问题是,只有一些操作可以翻译成TSQL。你得到同样的问题与的IQueryable< T> - 例如,EF和LINQ到SQL支持在查询不同的操作;甚至只是一()的行为有所不同(EF要求你明确地先订购,LINQ到SQL不)。

因此​​,在总结:

它可以工作 但仔细一想,你是否想这样做;一个比较经典的黑盒版本库/服务接口可能会更容易测试

I have an interface called ICatalog as shown below where each ICatalog has a name and a method that will return items based on a Predicate<Item> function.

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

A specific implementation of a catalog may be linked to catalogs in various format such as XML, or a SQL database.

With an XML catalog I end up deserializing the entire XML file into memory, so testing each item with the predicate function does does not add a whole lot more overhead as it's already in memory.

Yet with the SQL implementation I'd rather not retrieve the entire contents of the database into memory, and then filter the items with the predicate function. Instead I'd want to find a way to somehow pass the predicate to the SQL server, or somehow convert it to a SQL query.

This seems like a problem that can be solved with Linq, but I'm pretty new to it. Should my interface return IQueryable instead? I'm not concerned right now with how to actually implement a SQL version of my ICatalog. I just want to make sure my interface will allow for it in the future.

解决方案

Rob has indicated how you might do this (although a more classic LINQ approach might take Expression<Func<Item,bool>>, and possbily return IQueryable<IFamily>).

The good news is that if you want to use the predicate with LINQ-to-Objects (for your xml scenario) you can then just use:

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

or (for the other signature):

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

and you have a delegate (func) to test your objects with.

The problem though, is that this is a nightmare to unit test - you can only really integration test it.

The problem is that you can't reliably mock (with LINQ-to-Objects) anything involving complex data-stores; for example, the following will work fine in your unit tests but won't work "for real" against a database:

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

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

The problem is that only some operations can be translated to TSQL. You get the same problem with IQueryable<T> - for example, EF and LINQ-to-SQL support different operations on a query; even just First() behaves differently (EF demands you explicitly order it first, LINQ-to-SQL doesn't).

So in summary:

it can work but think carefully whether you want to do this; a more classic black box repository / service interface may be more testable