2012-05-15 42 views
5

是一個地方的方式來寫是這樣的:NHibernate的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); 

現在,我得到一個異常

[NotSupportedException異常:System.DateTime的AddHours(雙人間)]

+0

是否使用NH3.3?並通過代碼映射? – Rippo

+0

是的,我同時使用 –

回答

7

有沒有簡單的方法,使您LINQ查詢工作。你的情況的問題是,NHibernate不知道如何翻譯DateTime.AddHours(double hours)方法。但是,您可以使用HQL來編寫類似的查詢嗎?顯然不是。沒有標準的HQL AddHours函數。因此你必須註冊這個新功能。 NHibernate使用方言在hql和供應商特定的SQL語法之間進行轉換。爲了做到這一點,你必須創建一個新的方言類從派生出來並覆蓋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到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到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>(); 
+0

與我的答案非常相似,並在五分鐘後回答:) – Rippo

+0

在編輯時沒有按下「顯示新答案」,因爲你的答案沒有解決,在編輯之前t沒有自定義方言 – Vasea

+0

謝謝你的回答和對情況的很好的描述。順便說一句,在DefaultLinqToHqlGeneratorsRegistry中使用合併方法看起來有點漂亮:) –

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法比奧通過基於此。

您現在可以使用你的代碼是: -

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

這也有可能在3.2,但public override HqlTreeNode BuildHql(..)參數略有不同...

+0

謝謝你的解決方案!對不起,我接受了另一個答案,因爲它對發生的事情和原因有很好的判斷。 –

+0

不要擔心我不會太困擾,雖然有些人認爲應該刪除其他答案,但是你贏了一些你會失去一些答案。 – Rippo

+0

是否有可能讓這個與DateTime.Subtract一起使用? – nfplee