2011-06-07 68 views
6

我正在使用NHibernate 3.1.0,並試圖通過使用BaseHqlGeneratorForMethod和擴展DefaultLinqToHqlGeneratorsRegistry來擴展LINQ提供程序,如Fabio's post中所述。將LINQ擴展爲Nhibernate提供程序,並結合動態LINQ問題

例如,要支持ToString()我創建瞭如下的ToStringGenerator

internal class ToStringGenerator : BaseHqlGeneratorForMethod 
{ 
    public ToStringGenerator() 
    { 
     SupportedMethods = new[] 
      { 
       ReflectionHelper.GetMethodDefinition<object>(x => x.ToString()) 
      }; 
    } 

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) 
    { 
     return treeBuilder.Cast(visitor.Visit(targetObject).AsExpression(), typeof(string)); 
    } 
} 

和我一直在使用

internal class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry 
{ 
    public CustomLinqToHqlGeneratorsRegistry() 
    { 
     this.Merge(new ToStringGenerator()); 
    } 
} 

等。到目前爲止,這個工程的「靜態」查詢,我可以用它像這樣註冊:

var results = mSession.Query<Project>(); 
string pId = "1"; 
results = results.Where(p => p.Id.ToString().Contains(pId)); 

這正確地轉換到其SQL對應(使用SQL Server 2008)

where cast(project0_.Id as NVARCHAR(255)) like (''%''[email protected]+''%'') 

問題出現時,我嘗試與微軟動態的LINQ庫(在this Scott Guthrie's post討論)組合使用這樣的:

var results = mSession.Query<Project>(); 
string pId = "1"; 
results = results.Where("Id.ToString().Contains(@0)", pId); 

這導致NotSupportedException異常與「System.String的ToString消息()「(這與我在實現上述類之前獲得的靜態查詢完全相同)。此異常正在拋出,其源代碼爲「NHibernate」,而StackTrace的「」位於NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression(MethodCallExpression expression)「。

那麼我在這裏錯過了什麼?我做錯了什麼,或者需要做些什麼來支持這種情況?

回答

3

我有同樣的問題,並修復它。
起初我想感謝murki提供的信息讓我在途中!

答案部分在法比奧的帖子中。要解決此問題,您必須使用RegisterGenerator而不是CustomLinqToHqlGeneratorsRegistry構造函數中的Merge方法。我執行的CustomLinqToHqlGeneratorsRegistry類如下:

public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry 
{ 
    public CustomLinqToHqlGeneratorsRegistry() 
     : base() 
    { 
     MethodInfo toStringMethod = ReflectionHelper.GetMethodDefinition<int>(x => x.ToString()); 
     RegisterGenerator(toStringMethod, new ToStringGenerator()); 
    } 
} 
+0

這聽起來像是合併實現中的一個錯誤。 – murki 2014-12-09 02:28:04

1

有兩種,明確定義的,獨立這裏階段:

  1. 轉換的動態(串)的查詢到靜態表達(通過動態LINQ的庫進行)
  2. 解析該成HqlTree,然後執行已(由NHibernate的完成)

既然你已經確定了一個靜態的表達效果很好,問題就出在

如果您執行以下操作,會發生什麼情況?

var results = Enumerable.Empty<Project>().AsQueryable(); 
string pId = "1"; 
results = results.Where("Id.ToString().Contains(@0)", pId); 

如果失敗的話,你就會有證實它與單純的動態的LINQ(即它不支持你餵養它表達的),所以你必須要深入到它的一個問題並修補它。

半相關:ToStringGenerator看起來有用;你可以提交NHibernate的補丁嗎?http://jira.nhforge.org

+0

感謝您的回答。我已經完成了你的建議,並且查詢工作正常(我的意思是它不會產生任何結果,但查詢內的表達式是正確生成的)。 現在我已經注意到,比較2個查詢的東西是,靜態表達式使用Expression.Constant與變量(pId)值,而動態表達式則直接使用字符串值。你認爲這可能與問題有關嗎? – murki 2011-06-07 17:45:35

+1

@murki:是的,它可能是相關的。高阻抗表達式樹在支持小的變化方面非常脆弱。現在,鑑於此,您有兩條路徑:修補Dynamic Linq以發出Expression.Constant,或修補NHibernate以支持Dynamic Linq發出的用法。 – 2011-06-07 18:35:01

0

假設類Project的財產Id這是一個Int32嘗試在ToStringGenerator類註冊相應Int32.ToString()方法。

... 
public ToStringGenerator() 
{ 
    SupportedMethods = new[] 
     { 
      ReflectionHelper.GetMethodDefinition<object>(x => x.ToString()), 
      ReflectionHelper.GetMethodDefinition<int>(x => x.ToString()), 
     }; 
} 
... 
+0

是的,這是一個Int32,但我實際上已經嘗試過 - 這可能是另一個問題的主題,但是:1)如果我只將對象和int沒有更改我的錯誤,2)如果我開始放更多的數據類型(例如DateTime)時,它會在構建SessionFactory時拋出一個NhibernateException,並帶有錯誤「具有相同鍵的項目已被添加」。 – murki 2011-06-08 16:45:53

+0

好的謝謝。經過一番調查,NHibernate 3.2仍然處於測試版,它將在Linq中有內置的ToString支持。 – 2011-06-08 18:53:00