在 NHibernate 中重写 Linq 表达式 + DDD 上下文
我有一个关于转换 Linq 表达式的棘手问题。我进行了很好的搜索,但找不到任何似乎涵盖此案的内容。我对 Linq 相当熟悉,至少在创建 lambda 并将其传递给方法方面如此,但我对表达式的东西有点弱。
首先,一些背景信息:我有一个基于 NHibernate 的通用持久性解决方案,在许多 DDD 项目中使用。对于聚合中给定的子集合本质上可能是无限的(即确实非常大)的特殊情况,我不能简单地映射集合或包,因为将整个集合加载到内存中是永远不可接受的。在此架构中,使用 API 的代码不可能直接与存储库对话以执行查询或以这种方式限制结果。我当然可以在 API 中根本没有集合,而是公开方法来检索子对象的相关子集(如果这不起作用,那就是我要做的),但我正在尝试做一些稍微不同的事情我几乎已经开始工作了...
这些项目正在使用 Fluent 进行映射(不是自动映射),因此我以以下形式向我的基本映射类添加了一个方法
HasManyQueryable<TCollection>(Expression<Func<T, IQueryable<TCollection>>> memberExpression, Expression<Func<T, TCollection, bool>> selector)
此方法派生了相关的 PropertyInfo
来自第一个表达式
(指定要映射的成员)。选择器 Expression
包含父对象和子对象之间的关系,作为普通 NHibernate 映射的替代。
因此,假设我在映射中针对域类型 User
(即上面的 T
)有一个选择器:
HasManyQueryable<Transaction>(x => x.Transactions, (u, t) => t.User == u);
这指定了所有 Transactions< 的子集之间的映射/code> 其中
。当构造实际的 Transaction.User
是提供的 User
u,User.Transactions
属性是 <代码>IQueryable<事务>User
对象时,我需要将其转换为
Expression<Func<Transaction, bool>> expression = (t => t.User == this)
this
是正在构造的 User
对象。换句话说,我想采用一条通用规则,说明如何将Users
映射到Transactions
,并将其转换为有关映射this的规则<代码>用户到交易
。然后,我可以使用此表达式通过执行 Linq 查询从存储库生成 IQueryable
,因此:
return Repository.For<Transaction>().Where(selector);
这仅在选择器为 Func
FuncFunc
)转换为 Func
。
这给了我一个 IQueryable 集合,其中所有查询操作都作为 Linq-to-NHibernate 查询完成,因此整个集合永远不会加载到内存中(是的,我知道您可以构建一个查询,该查询将实际上让它做到了这一点,但我可以在代码审查时捕获它们)。
唷。希望这是有道理的。
任何具有 leet Expression 重写技能的人都能够为我指明正确的方向吗?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
老实说,我对您从领域角度尝试做的事情感到有点迷失。但从代码的角度来看,听起来您所需要的只是一个柯里化函数——一种接受 User 并生成 Func 的方法。一个示例方法是这样的,尽管您可以内联执行相同的操作:
因此,要获取特定于用户的选择器,您会做...
我怀疑您需要直接使用
Expression
层次结构,除非您正在实现自己的查询提供程序,但在本例中并非如此。I'm honestly kind of lost about what you're trying to do from a domain perspective. But from a code perspective, it sounds like all you need is a curried function--a method that takes a User and produces a Func. An example method would be this, though you could do the same thing inline:
So to get the user-specific selector, you would do...
I doubt you need to work directly with the
Expression
hierarchy unless you're implementing your own query provider, which you aren't in this case.