文章目錄
- IRuntimeMethodHqlGenerator
- IHqlGeneratorForMethod
- IHqlGeneratorForProperty
- BaseHqlGeneratorForMethod
- BaseHqlGeneratorForProperty
public class AddDaysGenerator : BaseHqlGeneratorForMethod
{
public AddDaysGenerator()
{
SupportedMethods = new[] {ReflectionHelper.GetMethodDefinition<Date>(d => d.AddDays(0))};
}
#region Overrides of BaseHqlGeneratorForMethod
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.MethodCall("AddDays", new[]
{
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(targetObject).AsExpression()
});
}
#endregion
}
public class AddDaysGeneratorRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public AddDaysGeneratorRegistry()
{
RegisterGenerator(ReflectionHelper.GetMethodDefinition<Date>(d => d.AddDays(0)), new AddDaysGenerator());
}
}
public class CustomDialect : MsSql2008Dialect
{
public CustomDialect()
{
RegisterFunction("AddDays", new SQLFunctionTemplate(NHibernateUtil.DateTime, "DATEADD(DD,?1,?2)"));
}
}
as you can see, this picks up our method AddDays defined on our custom Date class. This means that we can do something like:
query = query.Where(dc => (dc.EndDateReminderPeriod != null && (((dc.EndDate).AddDays(0-dc.EndDateReminderPeriod.Value)) == _reminderDate)) ||
(dc.RealEndDateReminderPeriod != null && (((dc.RealEndDate).AddDays(0-dc.RealEndDateReminderPeriod.Value)) == _reminderDate)));
Where in this example XXReminderPeriod is an int and XXDate is a Date property.
I "think" that this fails because the return type of the function is DateTime and so when it gets to the equality part of the method, its trying to put the _reminderDate into a DateTime. But as I am sure you have guessed, _reminderDate is a Date.
I was trying to change the function to use my custom transaltor for the return type, but it seems that SQLFunction only accepts a IType and not an IUserType.
Any thoughts?
本節內容
- 系列引入
- 概述
- Linq provider自訂擴充機制
- Linq provider自訂擴充實現
- 結語
- 參考資料
系列引入
NHibernate3.0剖析系列分別從Configuration篇、Mapping篇、Query篇、Session策略篇、應用篇等方面全面揭示NHibernate3.0新特性和應用及其各種應用程式的整合,基於NHibernte3.0版本。如果你還不熟悉NHibernate,可以快速閱讀NHibernate之旅系列文章導航系列入門,如果你已經在用NHibernate了,那麼請跟上NHibernate3.0剖析系列吧。
- NHibernate專題:http://kb.cnblogs.com/zt/nhibernate/
- NHibernate官方網站:http://nhforge.org/
- NHibernate參考文檔:http://nhforge.org/doc/nh/en/
- 擷取NHibernate地址:http://sourceforge.net/projects/nhibernate/
概述
NHibernate.Linq除了本身提供了標準查詢運算子和NHibernate特有的兩個強查詢立即抓取(EagerFetching)和查詢快取(QueryCacheable),我們也可以自己定義Linq provider擴充。
Linq provider自訂擴充機制
在NHibernate中,幾乎所有的物件導向查詢語言(HQL、Criteria、QueryOver)都是可擴充的,Linq也不例外。我們可以擴充自訂LINQ-provider並將LINQ擴充方法轉換為SQL。下面看看NHibernate對外提供的Linq provider擴充機制。
ILinqToHqlGeneratorsRegistry介面
為Hql-Generators提供統一註冊介面,在Build SessionFactory的時候,NHibernate註冊提供的Hql-Generators。
LinqToHqlGeneratorsRegistryFactory註冊工廠
提供Hql-Generators註冊工廠,預設註冊NHibernate內建支援的NHibernate.Linq查詢,譬如DateTime類型提供的屬性和方法、String類型提供的屬性和方法、Queryable和Enumerable提供的方法。
可以通過Configuration的"linqtohql.generatorsregistry"配置節或者Configuration類提供的LinqToHqlGeneratorsRegistry擴充方法註冊實現ILinqToHqlGeneratorsRegistry介面自訂Linq provider擴充。
DefaultLinqToHqlGeneratorsRegistry註冊類
預設NHibernate內建支援的NHibernate.Linq查詢註冊類,繼承ILinqToHqlGeneratorsRegistry介面。
三種Hql-Generators介面:IRuntimeMethodHqlGenerator
對運行時方法註冊,ICollection<T>集合的Contains方法,帶LinqExtensionMethodAttribute的擴充方法。
IHqlGeneratorForMethod
對方法Hql產生,譬如Queryable和Enumerable類的Any、All、Min、Max、Contains方法;string類型的StartsWith、EndsWith、Contains、Equals、ToLower、ToLowerInvariant、ToUpper、ToUpperInvariant、Substring、IndexOf、Replace方法和帶LinqExtensionMethodAttribute的擴充方法,NHibernate內部用於識別和轉換Visitors類的方法。
IHqlGeneratorForProperty
對屬性Hql產生,譬如DateTime類型的Year、Month、Day、Hour、Minute、Second、Date屬性;string類型的Length屬性。NHibernate內部用於識別和轉換Visitors類的屬性。
兩種Hql-Generators抽象類別:BaseHqlGeneratorForMethod
BaseHqlGeneratorForMethod抽象類別實現IHqlGeneratorForMethod介面。用於定義方法的Hql-Generators。
例如NHibernate內建提供string類型StartWith()方法的Hql-Generators實現:
BaseHqlGeneratorForProperty
BaseHqlGeneratorForProperty抽象類別實現IHqlGeneratorForProperty介面。用於定義屬性的Hql-Generators。
例如NHibernate內建提供string類型Length屬性的Hql-Generators實現:
知道了上面的內容,相信你可以自訂一個Linq provider擴充了。
Linq provider自訂擴充實現
我們以String類型為例,使用IsLike擴充方法對String類型擴充,模仿SQL中的LIKE從句。
1.Linq擴充方法
使用IsLike擴充方法對String類型擴充,代碼如下:
//Code Snippets Copyright http://lyj.cnblogs.com/public static class MyLinqExtensions{ public static bool IsLike(this string source, string pattern) { pattern = Regex.Escape(pattern); pattern = pattern.Replace("%", ".*?").Replace("_", "."); pattern = pattern.Replace(@"\[", "[").Replace(@"\]","]").Replace(@"\^", "^"); return Regex.IsMatch(source, pattern); }}2.IsLike擴充方法的Hql-Generators實現
建立完擴充方法之後,就可以在記憶體中使用這個擴充了。但是我們需要NHibernate把他翻譯成持久化查詢(persistence-queries),即需要轉換為SQL。像NHibernate內建的實作類別似,我們需要建立一個Generators:
//Code Snippets Copyright http://lyj.cnblogs.com/public class IsLikeGenerator : BaseHqlGeneratorForMethod{ public IsLikeGenerator() { SupportedMethods = new[] {ReflectionHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null))}; } public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(), visitor.Visit(arguments[1]).AsExpression()); }}3.註冊IsLike擴充方法Hql-Generators
我們繼承預設NHibernate內建支援的NHibernate.Linq查詢註冊類,這樣可以把我們自訂的Hql-Generators附加進去。
//Code Snippets Copyright http://lyj.cnblogs.com/public class MyLinqToHqlGeneratorsRegistry: DefaultLinqToHqlGeneratorsRegistry{ public MyLinqToHqlGeneratorsRegistry() { RegisterGenerator(ReflectionHelper.GetMethodDefinition( () => MyLinqExtensions.IsLike(null, null)),new IsLikeGenerator()); }}4.配置自訂Linq provider擴充
使用IsLike擴充方法去查詢DB資料,我們需要配置我們自訂的LinqToHQLGeneratorsRegistry,如果使用設定檔配置,則需要使用linqtohql.generatorsregistry:
如果使用Loquacious-configuration就是這樣:
//Code Snippets Copyright http://lyj.cnblogs.com/configuration.LinqToHqlGeneratorsRegistry<MyLinqToHqlGeneratorsRegistry>();
5.使用IsLike擴充方法
//Code Snippets Copyright http://lyj.cnblogs.com/var users = session.Query<User>().Where(o => o.Name.IsLike("%永京%")).ToList();6.執行結果
結語
通過這篇文章學習了Linq provider自訂擴充機制和實現。
參考資料
Fabio Maulo:NHibernate LINQ provider extension
NHibernate Jira: Add support for user-provided extensions to the Linq provider
希望本文對你有所協助。