2012-05-15 15 views
5

gdzie sposób napisać coś takiego:NHibernate dostawcy 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); 

Teraz otrzymuję wyjątek

[NotSupportedException: System.DateTime AddHours (dwukrotnie)]

+0

Używasz NH3.3? i mapowanie według kodu? – Rippo

+0

Tak, używam obu –

Odpowiedz

7

Nie ma łatwy sposób dokonać LINQ praca z zapytaniami. Problem ze scenariuszem polega na tym, że NHibernate nie wie, jak przetłumaczyć metodę DateTime.AddHours(double hours). Ale czy możesz użyć HQL do napisania podobnego zapytania? Najwyraźniej nie. Nie ma standardowej funkcji HQL AddHours. Dlatego musisz zarejestrować tę nową funkcję. NHibernate używa dialektów do tłumaczenia między składnią SQL a specyficzną dla dostawcy wersją SQL. Aby to zrobić, musisz stworzyć nową klasę dialektów wywodzącą się z istniejącej i zastępującą metodę RegisterFunctions. Ale to rozwiązuje tylko pierwszą połowę problemu. Następnie musisz pokazać NHibernate, jak korzystać z tej funkcji w LINQ. Musisz "mapować" pomiędzy metodą DateTime.AddHours(double hours) a wcześniej zarejestrowaną niestandardową funkcją hql. NHibernate używa w tym celu rejestru. Będziesz musiał przedłużyć domyślny rejestr linq-to-hql.

pokażę przykład, który pracował z NHibernate 3.3

Utwórz nową klasę dialekt (mój przykład używa predefiniowanego MsSql2008Dialect)

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

Utwórz nowy LINQ-HQL klasy generator, który wie jak przetłumaczyć metodę 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()); 
     } 
    } 

Extend domyślnego LINQ-HQL klasa rejestru

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

Konfiguracja

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

Prawie ta sama odpowiedź co moja i odpowiedział pięć minut później :) – Rippo

+0

nie nacisnąłem "pokaż nową odpowiedź" podczas edycji, zostawiłem moje rozwiązanie, ponieważ nie zrobiłeś tego " • przed edycją wybierz niestandardowy dialekt – Vasea

+0

Dziękujemy za odpowiedź i dobry opis sytuacji. BTW przy użyciu metody Merge w DefaultLinqToHqlGeneratorsRegistry wygląda nieco ładniej :) –

5

Jeśli używasz konfiguracji NH 3.3 i Loquacious, możesz zrobić coś takiego ..

Dodaj LinqToHqlGeneratorsRegistry do konfiguracji: -

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

i dodać trzy następujące klasy: -

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

mam to oparte na tej blog post Fabio.

Teraz można użyć kodu jak: -

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

Jest to również możliwe w 3.2 ale parametry public override HqlTreeNode BuildHql(..) są nieco inne ...

+0

Dziękujemy za rozwiązanie! Przykro mi, że przyjąłem inną odpowiedź, ponieważ ma ona dobry opis tego, co się dzieje i dlaczego. –

+0

Nie martw się Nie martwię się zbytnio, chociaż część mnie uważa, że ​​druga odpowiedź powinna zostać usunięta, wygrywasz, tracisz. – Rippo

+0

Czy to możliwe, aby to działało z funkcją DateTime.Subtract? – nfplee

Powiązane problemy