我正在通过扩展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
的{{1}}类。我将其BaseHqlGeneratorForMethod
属性设置为引用我的HasEmployee扩展方法。
我迷失的地方是.SupportedMethods
。构建的表达式变得非常复杂。我想,因为我正在替换BuildHql
子句 - 一个好的起点是使用内置.Any
类的源代码。但是这并没有考虑到源是原始元素的属性,并且它也没有考虑到我没有lambda表达式来表示where子句。我需要手动构建这些部件。
到目前为止,发布我的尝试毫无意义,因为它们距离任何可行的东西都很远。
请有人帮我将这个简单的表达式转换为AnyHqlGenerator
方法覆盖的适当方法集吗?
如果有更好的文档,请告诉我。感谢。
答案 0 :(得分:1)
我知道这个问题已经有一年了,但我今天在实施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);
}
我无法真正测试这个具体的例子,但在为我自己的扩展方法提供支持时,同样的方法对我有用。