#c# #.net #linq #nhibernate #linq-to-nhibernate
#c# #.net #linq #nhibernate #linq-to-nhibernate
Вопрос:
Я использую NHibernate 3.1.0 и пытаюсь расширить поставщика LINQ, используя BaseHqlGeneratorForMethod
и расширяя DefaultLinqToHqlGeneratorsRegistry
, как описано в сообщении Fabio.
Например, для поддержки ToString()
я создал ToStringGenerator
, как показано ниже.
internal class ToStringGenerator : BaseHqlGeneratorForMethod
{
public ToStringGenerator()
{
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<object>(x => x.ToString())
};
}
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.Cast(visitor.Visit(targetObject).AsExpression(), typeof(string));
}
}
и я зарегистрировался с помощью
internal class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public CustomLinqToHqlGeneratorsRegistry()
{
this.Merge(new ToStringGenerator());
}
}
и т.д. Пока это работает для «статических» запросов, я могу использовать это следующим образом:
var results = mSession.Query<Project>();
string pId = "1";
results = results.Where(p => p.Id.ToString().Contains(pId));
Это правильно переводится на его аналог SQL (с использованием SQL Server 2008)
where cast(project0_.Id as NVARCHAR(255)) like (''%'' @p0 ''%'')
Проблема возникает, когда я пытаюсь использовать его в сочетании с библиотекой Microsoft Dynamic LINQ (обсуждаемой в этом сообщении Скотта Гатри) следующим образом:
var results = mSession.Query<Project>();
string pId = "1";
results = results.Where("Id.ToString().Contains(@0)", pId);
Это приводит к исключению NotSupportedException с сообщением «System.String toString()» (это были точно такие же сообщения, которые я получал со статическими запросами перед внедрением упомянутых выше классов). Это исключение генерируется с источником «NHibernate» и с отслеживанием стека в «в NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.visitmethod CallExpression(выражение MethodCallExpression)«.
Итак, чего мне здесь не хватает? Что я сделал не так или что нужно сделать для поддержки этого сценария?
Ответ №1:
У меня была такая же проблема, и я ее исправил.
Сначала я хочу поблагодарить murki за предоставленную информацию, которая помогла мне в этом!
Ответ частично содержится в сообщении Fabio. Чтобы решить эту проблему, вы должны использовать RegisterGenerator
вместо Merge
метода в CustomLinqToHqlGeneratorsRegistry
конструкторе. Моя реализация CustomLinqToHqlGeneratorsRegistry
класса заключается в следующем:
public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public CustomLinqToHqlGeneratorsRegistry()
: base()
{
MethodInfo toStringMethod = ReflectionHelper.GetMethodDefinition<int>(x => x.ToString());
RegisterGenerator(toStringMethod, new ToStringGenerator());
}
}
Комментарии:
1. Тогда это звучит как ошибка в реализации слияния.
Ответ №2:
Здесь есть два четко определенных отдельных этапа:
- Преобразование динамического (строкового) запроса в статическое выражение (выполняется динамической библиотекой Linq)
- Разбор этого в HqlTree, затем выполнение (выполняется NHibernate)
Поскольку вы определили, что статическое выражение работает хорошо, проблема заключается в 1.
Что произойдет, если вы сделаете следующее?
var results = Enumerable.Empty<Project>().AsQueryable();
string pId = "1";
results = results.Where("Id.ToString().Contains(@0)", pId);
Если это не удается, вы подтвердите, что проблема связана только с Dynamic Linq (т. Е. он не поддерживает выражение, которое вы ему передаете), поэтому вам придется разобраться в этом и исправить.
Частично связанный: ToStringGenerator
выглядит полезным; не могли бы вы отправить исправление для NHibernate? http://jira.nhforge.org
Комментарии:
1. Спасибо за ваш ответ. Я сделал то, что вы предложили, и запрос работает нормально (я имею в виду, что он не дает результатов, но выражение внутри запроса сгенерировано правильно). Что-то, что я заметил сейчас, сравнивая два запроса, заключается в том, что выражение для статического использует выражение. Константа со значением переменной (pId), тогда как динамический использует строковое значение напрямую. Как вы думаете, это может иметь отношение к проблеме?
2. @murki: да, это может быть связано. Высокоимпедансные деревья выражений чрезвычайно хрупки в плане поддержки небольших вариаций. Теперь, учитывая это, у вас есть два пути: исправление динамического Linq для генерации выражения. Константа или исправление NHibernate для поддержки использования, создаваемого Dynamic Linq.
Ответ №3:
Предположим, что свойство Id
класса Project
является Int32
попробуйте зарегистрировать соответствующий Int32.ToString()
метод в вашем ToStringGenerator
классе.
...
public ToStringGenerator()
{
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<object>(x => x.ToString()),
ReflectionHelper.GetMethodDefinition<int>(x => x.ToString()),
};
}
...
Комментарии:
1. Да, это Int32, но я действительно пробовал это раньше — и это, вероятно, тема для другого вопроса, но: 1) если я добавлю только object и int, в моей ошибке ничего не изменится, 2) если я начну вводить больше типов данных (например, DateTime), это вызовет исключение NhibernateException с ошибкой «Элемент с тем же ключом уже добавлен» при создании SessionFactory.
2. Хорошо, спасибо. После некоторого исследования NHibernate 3.2, все еще находящийся в бета-версии, будет иметь встроенную поддержку toString в Linq.