NHibernate3剖析:Query篇之NHibernate.Linq自訂擴充

來源:互聯網
上載者:User
文章目錄
  • 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

希望本文對你有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.