NHibernate3剖析:Query篇之NHibernate.Linq自定义扩展

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?

 

 

 

本节内容

系列引入

NHibernate3.0剖析系列分别从Configuration篇、Mapping篇、Query篇、Session策略篇、应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本。如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧。

概述

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

Linq-BaseHqlGeneratorForMethod

BaseHqlGeneratorForMethod抽象类实现IHqlGeneratorForMethod接口。用于定义方法的Hql-Generators。

例如NHibernate内置提供string类型StartWith()方法的Hql-Generators实现:

Linq-StartsWithGenerator

BaseHqlGeneratorForProperty

Linq-BaseHqlGeneratorForProperty

BaseHqlGeneratorForProperty抽象类实现IHqlGeneratorForProperty接口。用于定义属性的Hql-Generators。

例如NHibernate内置提供string类型Length属性的Hql-Generators实现:

Linq-LengthGenerator

知道了上面的内容,相信你可以自定义一个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-IsLikeExtensions

结语

通过这篇文章学习了Linq provider自定义扩展机制和实现。

参考资料

Fabio Maulo:NHibernate LINQ provider extension

NHibernate Jira: Add support for user-provided extensions to the Linq provider

希望本文对你有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值