помогите с выражением для расширения nHibernate linq провайдера - PullRequest
1 голос
/ 01 сентября 2011

Я работаю над пользовательским расширением linq для nHibernate, расширяя BaseHqlGeneratorForMethod.Техника описана здесь: 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.Но это не учитывает, что источник является свойством исходного элемента, и это также не учитывает, что у меня нет лямбда-выражения для представления предложения where.Мне нужно собрать эти детали вручную.

Пока нет смысла публиковать мои попытки, поскольку все они довольно далеки от всего, что будет работать.

Кто-нибудь, пожалуйстапомогите мне преобразовать это простое выражение в подходящий набор методов для переопределения метода BuildHql?

Если есть какая-либо лучшая документация для этого, пожалуйста, дайте мне знать.Спасибо.

1 Ответ

1 голос
/ 28 июля 2012

Я знаю, что этому вопросу год, но я столкнулся с очень похожей проблемой при внедрении BaseHqlGeneratorForMethod сегодня.

Входные данные для BuildHql содержат коллекцию System.Linq.Expressions.Expression аргументов, которые передаются в ваш метод расширения. Используя эти аргументы, вы можете построить дерево выражений, которое представляет реализацию вашего метода расширения. Если полученное выражение является чем-то, что поддерживает NHibernate.Linq, то вы можете преобразовать это выражение в поддерево Hql, используя предоставленную 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);
}

Я не могу по-настоящему протестировать этот конкретный пример, но тот же подход работал для меня при предоставлении поддержки для моего собственного метода расширения.

...