2012-05-15 13 views
5

es donde una forma de escribir algo como esto:NHibernate proveedor de LINQ datediff

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

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

ahora consigo una excepción

[NotSupportedException: System.DateTime AddHours (Doble)]

+0

¿Está utilizando NH3.3? y mapeo por código? – Rippo

+0

Sí, uso ambos –

Respuesta

7

No hay manera fácil de hacer su LINQ trabajo de consulta. El problema con su escenario es que NHibernate no sabe cómo traducir el método DateTime.AddHours(double hours). ¿Pero puede usar HQL para escribir una consulta similar? Aparentemente no. No hay una función HQL AddHours estándar. Por lo tanto, debe registrar esta nueva función. NHibernate usa dialectos para traducir entre hql y la sintaxis de SQL específica del proveedor. Para hacer esto, debes crear una nueva clase de dialecto derivada de una existente y reemplazar el método RegisterFunctions. Pero esto resuelve solo la primera mitad del problema. A continuación, debe mostrar a NHibernate cómo usar esta función en LINQ. Tiene que "mapear" entre el método DateTime.AddHours(double hours) y la función hql personalizada previamente registrada. NHibernate usa un registro para este propósito. Deberá extender el registro predeterminado linq-to-hql.

voy a mostrar un ejemplo que trabajó con NHibernate 3.3

Crear una nueva clase dialecto (mi ejemplo se utiliza la predefinido MsSql2008Dialect)

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

crear una nueva clase generador de LINQ a HQL que sabe cómo traducir método 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()); 
     } 
    } 

Extender el defecto LINQ a HQL clase de registro

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

Configurar

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

Casi la misma respuesta que la mía y contestó cinco minutos más tarde :) – Rippo

+0

no presionó "mostrar nueva respuesta" mientras editaba, dejé mi solución ya que la suya no lo hizo t tiene el dialecto personalizado antes de editar – Vasea

+0

Gracias por la respuesta y una buena descripción de la situación. Por cierto, el uso del método Merge en DefaultLinqToHqlGeneratorsRegistry se ve un poco más bonito :) –

5

Si está utilizando la configuración NH 3.3 y Locuacious, puede hacer algo como esto ...

Añadir la LinqToHqlGeneratorsRegistry a la configuración: -

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

y añadir las tres clases siguientes: -

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)" 
       ) 
      ); 
    } 
} 

He basado este en este blog post por Fabio.

Ahora usted puede utilizar su código como es: -

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

Esto también es posible en 3.2 pero los parámetros public override HqlTreeNode BuildHql(..) son ligeramente diferentes ...

+0

¡Gracias por la solución! Lamento haber aceptado otra respuesta porque tiene una buena descripción de lo que sucede y por qué. –

+0

No se preocupe, no me molesta demasiado, aunque un poco de mí siente que la otra respuesta debe eliminarse, usted gana un poco, pierde un poco. – Rippo

+0

¿Es posible hacer que esto funcione con DateTime.Subtract? – nfplee