NHibernate linq提供者dateiff

时间:2012-05-15 16:21:47

标签: linq nhibernate

这是写这样的东西的方法:

public class Item
{
    public DateTime Start { get; set; }
    public DateTime Finish{ get; set; }
}

Sessin.Query<Item>.Where( x => x.Start.AddHours( 3 ) > x.Finish );

现在我得到了一个例外

  

[NotSupportedException:System.DateTime AddHours(Double)]

2 个答案:

答案 0 :(得分:7)

没有简单的方法可以让你的LINQ查询工作。您的方案存在的问题是NHibernate不知道如何翻译DateTime.AddHours(double hours)方法。但是你可以使用HQL编写类似的查询吗?显然不是。没有标准的HQL AddHours函数。因此,您必须注册此新功能。 NHibernate使用方言在hql和特定于供应商的SQL语法之间进行转换。为了做到这一点,你必须创建一个新的方言类,派生自一个expend类并覆盖RegisterFunctions方法。 但这只解决了问题的前半部分。接下来,你必须向NHibernate展示如何在LINQ中使用这个函数。您必须在DateTime.AddHours(double hours)方法和先前注册的自定义hql函数之间“映射”。 NHibernate为此目的使用注册表。您将不得不扩展默认的linq-to-hql注册表。

我将展示一个适用于NHibernate 3.3的示例

创建一个新的方言类(我的示例使用预定义的MsSql2008Dialect)

    public class EnhancedMsSql2008Dialect : MsSql2008Dialect
    {
        protected override void RegisterFunctions() {
            base.RegisterFunctions();
            RegisterFunction("add_hours", new SQLFunctionTemplate(NHibernateUtil.DateTime, "dateadd(hour, ?1, ?2)"));
        }
    }

创建一个新的LINQ-to-HQL生成器类,它知道如何翻译AddHours方法

    using NHibernate.Linq.Functions;
    using NHibernate.Linq;
    using NHibernate.Hql.Ast;

    public class DateTimeMethodsHqlGenerator : BaseHqlGeneratorForMethod
    {
        public DateTimeMethodsHqlGenerator() {
            SupportedMethods = new[] {
                ReflectionHelper.GetMethodDefinition((DateTime x) => x.AddHours(1))
            };
        }

        public override HqlTreeNode BuildHql(System.Reflection.MethodInfo method, System.Linq.Expressions.Expression targetObject, System.Collections.ObjectModel.ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor) {
            return treeBuilder.MethodCall("add_hours", visitor.Visit(arguments[0]).AsExpression(), visitor.Visit(targetObject).AsExpression());
        }
    }

扩展默认的LINQ-to-HQL注册表类

    public class EnhancedLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
    {
        public EnhancedLinqToHqlGeneratorsRegistry() : base() {
            //
            RegisterGenerator(ReflectionHelper.GetMethodDefinition((DateTime x) => x.AddHours(1)), new DateTimeMethodsHqlGenerator());
        }
    }

配置

    cfg.DataBaseIntegration(c => {
        c.Dialect<EnhancedMsSql2008Dialect>();
    });
    cfg.LinqToHqlGeneratorsRegistry<EnhancedLinqToHqlGeneratorsRegistry>();

答案 1 :(得分:5)

如果你正在使用NH 3.3和Loquacious配置,那么你可以做这样的事情......

LinqToHqlGeneratorsRegistry添加到配置中: -

        var configure = new Configuration()
            .DataBaseIntegration(x => {
                x.Dialect<CustomDialect>();
                x.ConnectionStringName = "db";
             })
            .LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry()
            .CurrentSessionContext<WebSessionContext>();

并添加以下三个类: -

public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public MyLinqtoHqlGeneratorsRegistry()
    {
        this.Merge(new AddHoursGenerator());
    }
}

public class AddHoursGenerator : BaseHqlGeneratorForMethod
{
    public AddHoursGenerator()
    {
        SupportedMethods = new[] {
        ReflectionHelper.GetMethodDefinition<DateTime?>(d =>      
                d.Value.AddHours((double)0))
          };
    }

    public override HqlTreeNode BuildHql(MethodInfo method,
        System.Linq.Expressions.Expression targetObject,
        ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
        HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.MethodCall("AddHours",
                visitor.Visit(targetObject).AsExpression(),
                visitor.Visit(arguments[0]).AsExpression()
            );
    }
}

public class CustomDialect : MsSql2008Dialect
{
    public CustomDialect()
    {
        RegisterFunction(
             "AddHours",
             new SQLFunctionTemplate(
                  NHibernateUtil.DateTime,
                  "dateadd(hh,?2,?1)"
                  )
             );
    }
}

我的基础是blog post由fabio。

您现在可以按原样使用您的代码: -

Session.Query<Item>.Where( x => x.Start.AddHours( 3 ) > x.Finish );

这在3.2中也是可能的,但public override HqlTreeNode BuildHql(..)参数略有不同......