帮助 nHibernate linq 提供程序扩展的表达式

发布于 2024-12-02 12:14:18 字数 1634 浏览 1 评论 0原文

我正在通过扩展 BaseHqlGeneratorForMethod 为 nHibernate 开发自定义 linq 扩展。该技术记录如下: http://fabiomaulo.blogspot.com/2010/07/nhibernate -linq-provider-extension.html

我已经成功地为各种类型的操作实现了这些,但我必须说 - 将简单的 linq 表达式转换为它的完整表达树并不容易!我现在被困在一个了。

对于此示例,我有三个实体。 员工员工组。 EmployeeGroup 类在 Employee 和 Group 之间建立多对多关系。我必须专门创建中间类,因为还有其他属性可以跟踪每个员工在每个组中拥有的特定权限。所以有两个一对多的关系,而不是一个nHibernate的多对多关系。

现在假设我想要获取包含特定员工的所有组。我可以编写这个查询:

var groups = session.Query<Group>()
  .Where(g => g.EmployeeGroups.Any(eg => eg.Employee == employee));

这工作正常,但需要输入很多内容。我更愿意能够做到这一点:

var groups = session.Query<Group>().Where(g => g.HasEmployee(employee));

我首先创建一个扩展方法,如下所示:

public static bool HasEmployee(this Group group, Employee employee)
{
  return group.EmployeeGroups.Any(eg => eg.Employee == employee);
}

这在查询本地组列表时有效,但不适用于 nHibernate 会话。为此,我还必须创建一个 linq 扩展并注册它。就像本文(上面链接)中一样,我创建了一个扩展 BaseHqlGeneratorForMethodGroupHasEmployeeGenerator 类。我设置其 .SupportedMethods 属性来引用我的 HasEmployee 扩展方法。

我迷失的地方是对 BuildHql 的覆盖。要构建的表达式很快就会变得复杂。我认为,由于我要替换 .Any 子句 - 一个好的起点是内置 AnyHqlGenerator 类的源代码。但这没有考虑到源是原始元素的属性,也没有考虑到我没有 lambda 表达式来表示 where 子句。我需要手动构建这些部件。

到目前为止,发布我的尝试是没有意义的,因为它们离任何有效的方法都还很远。

有人可以帮我将这个简单的表达式转换为 BuildHql 方法覆盖的适当方法集吗?

如果有任何更好的文档,请告诉我。谢谢。

I'm working on a custom linq extension for nHibernate by extending the BaseHqlGeneratorForMethod. The technique is documented here:
http://fabiomaulo.blogspot.com/2010/07/nhibernate-linq-provider-extension.html

I've had success with implementing these for various types of operations, but I must say - converting a simple linq expression to its full expression tree is not easy! I'm stuck on one now.

For this example, I have three entities. Employee, Group, and EmployeeGroup. The EmployeeGroup class sets up a many-to-many relationship between Employee and Group. I must specifically create the intermediate class because there are additional properties to track like specific permissions each employee has in each group. So there are two one-to-many relationships, rather than an nHibernate many-to-many relationship.

Now say I want to get all groups that contain a specific employee. I can write this query:

var groups = session.Query<Group>()
  .Where(g => g.EmployeeGroups.Any(eg => eg.Employee == employee));

This works fine, but it's a lot to type. I'd much rather be able to do this:

var groups = session.Query<Group>().Where(g => g.HasEmployee(employee));

I start by creating an extension method like so:

public static bool HasEmployee(this Group group, Employee employee)
{
  return group.EmployeeGroups.Any(eg => eg.Employee == employee);
}

This works when querying a local list of groups, but not against the nHibernate session. For that, I have to also create a linq extension and register it. Just like in the article (linked above), I create a GroupHasEmployeeGenerator class that extends BaseHqlGeneratorForMethod. I set its .SupportedMethods property to reference my HasEmployee extension method.

Where I get lost is in the override to BuildHql. The expression to build gets complicated pretty fast. I figure since I'm replacing the .Any clause - a good place to start is with the source for the built-in AnyHqlGenerator class. But that doesn't take into account that the source is a property of the original element, and it also doesn't take into account that I don't have a lambda expression to represent the where clause. I need to build these parts manually.

There's no point in posting my attempts so far, as they've all be quite far from anything that would work.

Will someone please help me convert this simple expression into the approprate set of methods for the BuildHql method override?

If there is any better documentation out there for this, please let me know. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清醇 2024-12-09 12:14:18

我知道这个问题已经有一年了,但是我今天在实现 BaseHqlGeneratorForMethod 时遇到了一个非常类似的问题。

BuildHql 的输入包含传递给扩展方法的 System.Linq.Expressions.Expression 参数的集合。使用这些参数,您可以构建表示扩展方法的实现的表达式树。如果 NHibernate.Linq 支持生成的表达式,那么您可以使用提供的 IHqlExpressionVisitor 将该表达式转换为 Hql 的子树。

在您的示例中:

public static bool HasEmployee(this Group group, Employee employee)
{
  return group.EmployeeGroups.Any(eg => eg.Employee == employee);
}

这将变得与此类似:

public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
    var AnyMethod = EnumerableHelper.GetMethod("Any", new[] {typeof(IEnumerable<EmployeeGroup>), typeof(Func<EmployeeGroup, bool>)}, new[] {typeof(EmployeeGroup)});
    var EmployeeGroupsProperty = ReflectionHelper.GetProperty<Group>(g => g.EmployeeGroups);
    var EmployeeProperty = ReflectionHelper.GetProperty<EmployeeGroup>(eg => eg.Employee);

    var EmployeeGroupParameter = Expression.Parameter(typeof(EmployeeGroup));
    var EmployeeGroupPredicate = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(EmployeeGroupParameter, EmployeeProperty), arguments[1]), EmployeeGroupParameter);

    var CallExpression = Expression.Call(AnyMethod, Expression.MakeMemberAccess(arguments[0], EmployeeGroupsProperty), EmployeeGroupPredicate);

    return visitor.Visit(CallExpression);
}

我无法真正测试这个特定示例,但在为我自己的扩展方法提供支持时,相同的方法对我有用。

I know this question is a year old, but I ran into a very similar issue when implementing BaseHqlGeneratorForMethod today.

The input to BuildHql contains a collection of System.Linq.Expressions.Expression arguments that are passed to your extension method. Using these arguments, you can build an expression tree that represents the implementation of your extension method. If the resulting expression is something NHibernate.Linq supports, then you can transform that expression to a subtree of Hql using the provided IHqlExpressionVisitor.

In your example:

public static bool HasEmployee(this Group group, Employee employee)
{
  return group.EmployeeGroups.Any(eg => eg.Employee == employee);
}

This would become something similar to this:

public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
    var AnyMethod = EnumerableHelper.GetMethod("Any", new[] {typeof(IEnumerable<EmployeeGroup>), typeof(Func<EmployeeGroup, bool>)}, new[] {typeof(EmployeeGroup)});
    var EmployeeGroupsProperty = ReflectionHelper.GetProperty<Group>(g => g.EmployeeGroups);
    var EmployeeProperty = ReflectionHelper.GetProperty<EmployeeGroup>(eg => eg.Employee);

    var EmployeeGroupParameter = Expression.Parameter(typeof(EmployeeGroup));
    var EmployeeGroupPredicate = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(EmployeeGroupParameter, EmployeeProperty), arguments[1]), EmployeeGroupParameter);

    var CallExpression = Expression.Call(AnyMethod, Expression.MakeMemberAccess(arguments[0], EmployeeGroupsProperty), EmployeeGroupPredicate);

    return visitor.Visit(CallExpression);
}

I can't really test this specific example, but the same approach worked for me when providing support for my own extension method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文