Расширение LINQ для провайдера Nhibernate в сочетании с проблемой динамического LINQ - PullRequest
6 голосов
/ 07 июня 2011

Я использую NHibernate 3.1.0 и пытаюсь расширить поставщика LINQ, используя BaseHqlGeneratorForMethod и расширяя DefaultLinqToHqlGeneratorsRegistry, как объяснено в пост Фабио .

Например, для поддержки 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 » и с StackTrace в « в NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression (выражение MethodCallExpression) ».

".

Так чего мне здесь не хватает? Что я сделал не так или что нужно сделать, чтобы поддержать этот сценарий?

Ответы [ 3 ]

3 голосов
/ 04 января 2012

У меня была такая же проблема и я ее исправил.
Сначала я хочу поблагодарить Мурки за предоставленную мне информацию!

Ответ частично находится в посте Фабио. Чтобы решить эту проблему, вы должны использовать метод RegisterGenerator вместо Merge в конструкторе CustomLinqToHqlGeneratorsRegistry. Моя реализация класса CustomLinqToHqlGeneratorsRegistry выглядит следующим образом:

public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public CustomLinqToHqlGeneratorsRegistry()
        : base()
    {
        MethodInfo toStringMethod = ReflectionHelper.GetMethodDefinition<int>(x => x.ToString());
        RegisterGenerator(toStringMethod, new ToStringGenerator());
    }
}
1 голос
/ 07 июня 2011

Здесь есть два четко определенных отдельных этапа:

  1. Преобразование динамического (строкового) запроса в статическое выражение (выполняется библиотекой Dynamic Linq)
  2. Анализ этогов 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

0 голосов
/ 08 июня 2011

Предположим, свойство Id класса Project это Int32, попробуйте зарегистрировать соответствующий метод Int32.ToString() в вашем ToStringGenerator классе.

...
public ToStringGenerator()
{
    SupportedMethods = new[]
        {
            ReflectionHelper.GetMethodDefinition<object>(x => x.ToString()),
            ReflectionHelper.GetMethodDefinition<int>(x => x.ToString()),
        };
}
...
...