0

I have a thorny question about transforming Linq Expressions. I had a good search about, but I couldn't find anything that seems to cover this case. I'm reasonably familiar with Linq, at least in terms of creating and passing lambdas to methods, but I'm somewhat weaker on the Expression stuff.

First, some context: I have a generic persistence solution based on NHibernate used inside a number of DDD-ish projects. For special cases where a given collection of children within an aggregate could be essentially infinite (ie very large indeed), I cannot simply map a set or bag as it would never be acceptable for the entire collection to be loaded into memory. In this architecture it's not possible for code consuming the API to talk to the Repository directly to do a query or limit the results that way. I could of course not have a collection in the API at all and expose methods to retrieve relevant subsets of child objects instead (and if this doesn't work that's what I'll do), but I'm trying to do something slightly different and I've almost got it working...

These projects are being mapped with Fluent (not auto-mapping), and so I added a method to my base map class in the form

HasManyQueryable<TCollection>(Expression<Func<T, IQueryable<TCollection>>> memberExpression, Expression<Func<T, TCollection, bool>> selector)

This method derives the relevant PropertyInfo from the first Expression (which specifies the member to map). The selector Expression contains the relationship between the parent and child objects as a substitute for a normal NHibernate mapping.

So, suppose I have a selector in the map for a domain type User (which is T above):

HasManyQueryable<Transaction>(x => x.Transactions, (u, t) => t.User == u);

This specifies a mapping between the subset of all Transactions where Transaction.User is the supplied User u, and the User.Transactions property which is IQueryable<Transaction>. When an actual User object is constructed, I need to turn this into

Expression<Func<Transaction, bool>> expression = (t => t.User == this)

where this is the User object being constructed. In other words, I want to take a general rule that says how to map Users to Transactions and turn it into a rule about mapping this User to Transactions. I can then use this expression to generate an IQueryable<Transaction> from the Repository by doing a Linq query, thus:

return Repository.For<Transaction>().Where(selector);

This can only work when the selector is Func<Transaction, bool>, hence my ultimate need is to turn the original expression, which would generate a Func<User, Transaction, bool> into Func<Transaction, bool>.

This gives me an IQueryable collection where all the query operations are being done as Linq-to-NHibernate queries and thus the entire collection never gets loaded into memory (yes, I know you could frame a query that would actually make it do that, but I can catch those at code review time).

Phew. Hope that makes sense.

Anyone with leet Expression re-writing skills able to point me in the right direction?

4

1 回答 1

1

老实说,从领域的角度来看,我对您正在尝试做的事情感到有些迷茫。但从代码的角度来看,听起来你所需要的只是一个柯里化函数——一个接受用户并产生一个 Func 的方法。一个示例方法是这样的,尽管您可以内联执行相同的操作:

Func<Transaction,bool> UserSpecificSelector(User user,
                                            Func<User,Transaction,bool> selector)
{
    return t => selector(user, t);
}

因此,要获得特定于用户的选择器,您可以...

Func<User, Transaction, bool> selector = // whatever;
User user = //whatever;
Repository.For<Transaction>().Where(t => selector(user, t));

我怀疑您是否需要直接使用Expression层次结构,除非您正在实现自己的查询提供程序,在这种情况下您不是。

于 2011-08-24T20:22:27.983 回答