Entity Framework 4.1 和 NHibernate 的获取策略封装
我创建了一个项目来测试 NHibernate 3+ 与 Entity Framework 4.1,将其包装在存储库中,使其可以使用接口等进行测试。
我不想在存储库之外公开任何 ORM(我什至不公开 IQueryables) 。一切都应该在该层处理,直到我尝试以抽象方式处理获取,一切都很好。
Microsoft 添加急切加载的实现在 Include 函数上使用魔术字符串 (yuck) 或 Linq 表达式 (yay)。他们的语法如下:
IQueryableThing.Include(o => o.Person);
IQueryableThing.Include(o => o.Company.Contact);
IQueryableThing.Include(o => o.Orders.Select(p => p.LineItem.Cost);
第一个将只加载关联的人。 (家长) 第二个将加载关联公司和每个公司的联系人。 (父母和祖父母)。 第三个将加载所有关联的订单、行项目和每个订单的成本。
这是一个非常巧妙的实现。
NHibernate 使用稍微不同的方法。他们仍然使用 Linq 表达式,但更多地使用扩展方法(流畅的方法)。
IQueryableThing.Fetch(o => o.Person);
IQueryableThing.Fetch(o => o.Company).ThenFetch(o => o.Contact);
IQueryableThing.FetchMany(o => o.Orders).ThenFetch(p => p.LineItem).ThenFetch(q => q.Cost);
(我不确定第三行是否是正确的语法)
我可以将表达式列表封装在单独的类中,然后将这些表达式应用于该类中的 IQueryable。所以我需要做的是标准化 Microsoft 表达式语法,然后通过遍历表达式树并重建每个表达式将其转换为 NHibernate 语法。
这是真正棘手的部分。我必须维护特定的操作顺序,以便为 IQueryable 调用正确的函数(必须以 Fetch 或 FetchMany 开头,每个后续都是“ThenFetch”或“ThenFetchMany”),这阻止我使用内置的在 ExpressionVisitor 类中。
编辑: 我最终创建了一个表达式解析器,它将采用任何级别的属性、集合嵌套,并在集合上进行选择并生成表达式数组。不幸的是,内置的 Fetch 扩展方法不将 LambdaExpression 作为参数。
我目前遇到的问题是无法使用 nHibernate 中内置的 Fetch 定义。看来我可能必须直接使用 Remotion 库的函数或注册我自己的扩展方法来满足他们的解析器的要求。
时髦。
I created a project to test out NHibernate 3+ vs. Entity Framework 4.1, wrapping it in a repository, making it very testable using interfaces etc.
I do not want to expose either ORM outside of the repositories (I do not even expose IQueryables). Everything should be handled in that layer and until I tried to handle fetching in an abstract way, everything was good.
Microsoft's implementation of adding eager loading uses either magic strings (yuck) or Linq expressions (yay) on the Include function. Their syntax follows something like this:
IQueryableThing.Include(o => o.Person);
IQueryableThing.Include(o => o.Company.Contact);
IQueryableThing.Include(o => o.Orders.Select(p => p.LineItem.Cost);
The first will just load the associated person. (parent)
The second will load the associated company and each company's contact. (parent and grandparent).
The third will load all associated orders, line items and costs for each order.
It's a pretty slick implementation.
NHibernate uses a slightly different approach. They still use Linq expressions, but they make heavier use of extension methods (fluent approach).
IQueryableThing.Fetch(o => o.Person);
IQueryableThing.Fetch(o => o.Company).ThenFetch(o => o.Contact);
IQueryableThing.FetchMany(o => o.Orders).ThenFetch(p => p.LineItem).ThenFetch(q => q.Cost);
(I'm not sure I if the third line is the correct syntax)
I can encapsulate a list of expressions in a separate class and then apply those expression to the IQueryable within that class. So what I would need to do is standardize on the Microsoft expression syntax and then translate that into NHibernate's syntax by walking the expression tree and rebuilding each expression.
This is the part that's really tricky. I have to maintain a particular order of operations in order to call the correct function for the IQueryable (must start with either Fetch or FetchMany, with each subsequent being a "ThenFetch" or "ThenFetchMany"), which stops me from using the built-in ExpressionVisitor class.
Edit:
I finally created an expression parser that will take any level of nesting of properties, collections, and selects on collections and produce an array of expressions. Unfortunately, the built in Fetch extensions methods do not take LambdaExpression as a parameter.
The part I am stuck on currently is not being able to use the built in Fetch definitions from nHibernate. It looks like I may have to hit the Remotion library's functions directly or register my own extension methods that will satisfy their parser.
Funky.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您是否尝试过使用
NHiberanteUtil.Initialize()
?我没有尝试做你正在做的事情,但我认为 Initialize 的工作方式类似于Include()
。Have you tried using
NHiberanteUtil.Initialize()
? I haven't attempted to do what you are doing, but I think Initialize will work akin toInclude()
.