2012-05-15 23 views
5

mi:NHibernate linq sağlayıcı 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); 

Şimdi bir istisna olsun

[NotSupportedException: (Çift) System.DateTime AddHours]

+0

Eğer NH3.3 kullanıyor musunuz? ve kod ile eşleme? – Rippo

+0

Evet, ikisini de kullanıyorum –

cevap

7

sizin LINQ yapmak için kolay bir yolu yoktur sorgulama çalışması. Senaryosundaki sorun, NHibernate'in DateTime.AddHours(double hours) yöntemini nasıl çevireceğini bilmemesidir. Ama benzer bir sorgu yazmak için HQL'yi kullanabilir misiniz? Görünüşe göre öyle değil. Standart HQL AddHours işlevi yoktur. Bu nedenle bu yeni fonksiyonu kaydetmelisiniz. NHibernate, hql ve satıcıya özgü SQL sözdizimi arasında çeviri yapmak için lehçeleri kullanır. Bunu yapabilmek için, yeni bir dilden türeyen yeni bir konuşma sınıfı oluşturmalı ve RegisterFunctions yöntemini geçersiz kılmalısınız. Fakat bu problemin sadece ilk yarısını çözüyor. Daha sonra, NHibernate'i bu işlevi LINQ'de nasıl kullanacağınızı göstermelisiniz. DateTime.AddHours(double hours) yöntemi ve önceden kayıtlı özel hql işlevi arasında "map" yapmalısınız. NHibernate bu amaç için bir kayıt defteri kullanır. Varsayılan linq-to-hql kayıt defterini genişletmeniz gerekecek. NHibernate ile çalıştı bir örnek gösterecektir

3,3

(benim örnek önceden tanımlanmış MsSql2008Dialect kullanır)

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

bilmediği yeni LINQ HQL jeneratör sınıf oluşturma Yeni bir lehçesi sınıf oluşturun AddHours yöntemini

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

çevirmek için nasıl varsayılan uzatın LINQ HQL kayıt sınıf

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

yapılandır

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

kullanın hemen hemen aynı cevabı benimki ve beş dakika sonra cevapladı :) – Rippo

+0

düzenleme yaparken "yeni cevap göster" tuşuna basmadı, seninkiden beri çözümümü bıraktım ' t – Vasea

+0

'u düzenlemeden önce özel lehçeye sahip olunuz. Yanıtın ve durumun iyi tanımlanması için teşekkür ederiz. DefaultLinqToHqlGeneratorsRegistry içinde Birleştirme yöntemini kullanarak BTW biraz daha güzel görünüyor :) –

5

Eğer NH 3.3 ve Loquacious konfigürasyonunu kullanıyorsanız, o zaman aşağıdaki gibi bir şey yapabilirsiniz ..

yapılandırmasına LinqToHqlGeneratorsRegistry ekleyin: -

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

ve aşağıdaki üç sınıfları ekleyin: -

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

Ben fabio tarafından bu blog post dayandırdı var. olduğu gibi

Artık kodu kullanabilirsiniz: -

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

Bu 3.2 de mümkündür ancak public override HqlTreeNode BuildHql(..) parametreleri biraz farklıdır ...

+0

Çözüm için teşekkür ederiz! Üzgünüm, başka bir cevabı kabul ettim, çünkü neler olup bittiğine dair iyi bir bilgi birikimine sahip. –

+0

Endişelenme Beni biraz rahatsız etmeme rağmen rahatsız etmiyorum, diğer cevaplar kaldırılmalı, bazılarını kaybedersiniz. – Rippo

+0

Bunu DateTime.Subtract ile çalışmak mümkün mü? – nfplee