1

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

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

对于这个例子,我有三个实体。 Employee, Group, 和EmployeeGroup. 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 扩展并注册它。就像在文章(上面链接)中一样,我创建了一个GroupHasEmployeeGenerator扩展类BaseHqlGeneratorForMethod。我将其.SupportedMethods属性设置为引用我的 HasEmployee 扩展方法。

我迷路的地方是覆盖到BuildHql. 要构建的表达式变得非常复杂。我想,因为我要替换该.Any子句 - 一个好的起点是内置AnyHqlGenerator类的源代码。但这并没有考虑到源是原始元素的属性,也没有考虑到我没有 lambda 表达式来表示 where 子句。我需要手动构建这些部分。

到目前为止,发布我的尝试毫无意义,因为它们都离任何可行的方法相去甚远。

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

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

4

1 回答 1

1

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

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

在您的示例中:

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);
}

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

于 2012-07-27T20:39:38.607 回答