使用NHibernate 3.2实现Repository(ORuM)(十一)NHibernate、LINQ

上接“A023使用NHibernate 3.2实现Repository(ORuM)(十)Linq Provider

NHibernate.Linq

在.NET Framework 3.5中提供了LINQ 支持后,Linq的呼声一度很高,各种LINQ Provider更是满天飞。他能够将数据查询语句集成到编程语言中,以一种统一的方式操作各种数据源,减少数据访问的复杂性。而LINQ本身也提供了很 好的扩展性,使开发人员可以轻松地编写属于自己的LINQ Provider。


在NHibernate 3.0.0 之前版本中并不存在Linq功能,Ayende Rahien贡献者为NHibernate2.1.0GA和NHibernate2.1.2GA版本设计第三方NHiberante.Linq.dll(对应为NHibernate.Linq-1.0.0.GA-bin.zip和NHibernate.Linq-2.1.2-GA-Bin.zip)(目前已经停止了维护),它是基于Criteria API的Linq Provider,主要功能是将简单的Linq表达式转化为Criteria API,由于Criteria API的功能有限,所以存在很多天生的不足(联接和子查询不支持)。如果使用NHibernate2.1.0GA或者NHibernate2.1.2GA版本可以下载使用NHiberante.Linq.dll,

自NHibernate 3.0.0版本开始,NHibernate Query方式新增了Linq支持查询方式。
NHibernate 3.x 的 NHibernate.Linq时基于HQL AST分析器的Linq Provider,由Steve Strong贡献者开发的,底层使用第三方Re-Linq开源框架。

NHibernate Linq Provider

实现过程:

NHibernate.Linq.LinqExtensionMethods类

    public static class LinqExtensionMethods
{
public static IQueryable<T> Query<T>(this ISession session)
{
return new NhQueryable<T>(session.GetSessionImplementation());
}

......

}

NHibernate.Linq.NhQueryable<T>类

   ///<summary>
/// Provides the main entry point to a LINQ query.
///</summary>
public class NhQueryable<T> : QueryableBase<T>
{
// This constructor is called by our users, create a new IQueryExecutor.
public NhQueryable(ISessionImplementor session)
: base(new DefaultQueryProvider(session))
{
}

// This constructor is called indirectly by LINQ's query methods, just pass to base.
public NhQueryable(IQueryProvider provider, Expression expression)
: base(provider, expression)
{
}
}

Remotion.Linq.QueryableBase<T>类

    public abstract class QueryableBase<T> : IOrderedQueryable<T>, IQueryable<T>, 
IEnumerable<T>, IOrderedQueryable, IQueryable, IEnumerable
{
protected QueryableBase(IQueryProvider provider);
protected QueryableBase(IQueryParser queryParser, IQueryExecutor executor);
protected QueryableBase(IQueryProvider provider, Expression expression);

public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }

public IEnumerator<T> GetEnumerator();
}

 NHibernate.Linq.INhQueryProvider接口

    public interface INhQueryProvider : IQueryProvider
{
object ExecuteFuture(Expression expression);
void SetResultTransformerAndAdditionalCriteria(IQuery query,
NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters);
}

NHibernate.Linq.DefaultQueryProvider类

    public class DefaultQueryProvider : INhQueryProvider
{
public DefaultQueryProvider(ISessionImplementor session)
{
Session = session;
}

protected virtual ISessionImplementor Session { get; private set; }

#region IQueryProvider Members

public virtual object Execute(Expression expression)
{
IQuery query;
NhLinqExpression nhQuery;
NhLinqExpression nhLinqExpression = PrepareQuery(expression, out query, out nhQuery);

return ExecuteQuery(nhLinqExpression, query, nhQuery);
}

public TResult Execute<TResult>(Expression expression)
{
return (TResult)Execute(expression);
}

public virtual IQueryable CreateQuery(Expression expression)
{
MethodInfo m = ReflectionHelper.GetMethodDefinition(
(DefaultQueryProvider p) => p.CreateQuery<object>(null))
.MakeGenericMethod(expression.Type.GetGenericArguments()[0]);

return (IQueryable)m.Invoke(this, new[] { expression });
}

public virtual IQueryable<T> CreateQuery<T>(Expression expression)
{
return new NhQueryable<T>(this, expression);
}

#endregion

public virtual object ExecuteFuture(Expression expression)
{
IQuery query;
NhLinqExpression nhQuery;
NhLinqExpression nhLinqExpression = PrepareQuery(expression, out query, out nhQuery);
return ExecuteFutureQuery(nhLinqExpression, query, nhQuery);
}

protected NhLinqExpression PrepareQuery(Expression expression, out IQuery query,
out NhLinqExpression nhQuery)
{
var nhLinqExpression = new NhLinqExpression(expression);

query = Session.CreateQuery(nhLinqExpression);

nhQuery = query.As<ExpressionQueryImpl>().QueryExpression.As<NhLinqExpression>();

SetParameters(query, nhLinqExpression.ParameterValuesByName);
SetResultTransformerAndAdditionalCriteria(query, nhQuery, nhLinqExpression.ParameterValuesByName);
return nhLinqExpression;
}

protected virtual object ExecuteFutureQuery(NhLinqExpression nhLinqExpression,
IQuery query, NhLinqExpression nhQuery)
{
MethodInfo method;
if (nhLinqExpression.ReturnType == NhLinqExpressionReturnType.Sequence)
{
method = typeof(IQuery).GetMethod("Future").MakeGenericMethod(nhQuery.Type);
}
else
{
method = typeof(IQuery).GetMethod("FutureValue").MakeGenericMethod(nhQuery.Type);
}

object result = method.Invoke(query, new object[0]);


if (nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer != null)
{
((IDelayedValue)result).ExecuteOnEval =
nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer;
}

return result;
}

protected virtual object ExecuteQuery(NhLinqExpression nhLinqExpression,
IQuery query, NhLinqExpression nhQuery)
{
IList results = query.List();

if (nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer != null)
{
try
{
return nhQuery.ExpressionToHqlTranslationResults.PostExecuteTransformer
.DynamicInvoke(results.AsQueryable());
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
}

if (nhLinqExpression.ReturnType == NhLinqExpressionReturnType.Sequence)
{
return results.AsQueryable();
}

return results[0];
}

private static void SetParameters(IQuery query, IDictionary<string, Tuple<object, IType>> parameters)
{
foreach (string parameterName in query.NamedParameters)
{
Tuple<object, IType> param = parameters[parameterName];

if (param.First == null)
{
if (typeof(ICollection).IsAssignableFrom(param.Second.ReturnedClass))
{
query.SetParameterList(parameterName, null, param.Second);
}
else
{
query.SetParameter(parameterName, null, param.Second);
}
}
else
{
if (param.First is ICollection)
{
query.SetParameterList(parameterName, (ICollection)param.First);
}
else if (param.Second != null)
{
query.SetParameter(parameterName, param.First, param.Second);
}
else
{
query.SetParameter(parameterName, param.First);
}
}
}
}

public void SetResultTransformerAndAdditionalCriteria(IQuery query,
NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
query.SetResultTransformer(nhExpression.ExpressionToHqlTranslationResults.ResultTransformer);

foreach (var criteria in nhExpression.ExpressionToHqlTranslationResults.AdditionalCriteria)
{
criteria(query, parameters);
}
}
}

 

Remotion.Linq.QueryableBase<T>类是由re-Linq提供。

re-Linq

出自:http://relinq.codeplex.com/

 

With re-linq, it's now easier than ever to create full-featured LINQ providers.

Yes, you've heard that before. But re-linq is the real thing:

  • Instead of the IQueryable expression tree, re-linq gives you an abstract syntax tree that resembles the original LINQ query expression (the one using the from, whereetc. keywords).
    • This works even when the original query was not a query expression, but a sequence of method invocations of Where(), Select() etc.
    • You can easily extend or modify quieries using this representation in your application, directly or via a Specification framework.
  • In the process, re-linq gets rid of everything that IQueryable puts between you and your desired target query language:
    • The structure is simplified (e.g., SelectMany is transformed back to multiple from-sources).
    • Transparent identifiers are removed.
    • Sub-queries are identified and can be handled as such.
    • Expressions are evaluated up-front wherever possible.
    • Special method calls inserted by Visual Basic are "normalized" so that they are handled implicitly, except if you choose to handle them differently.
  • re-linq handles even the most complex LINQ query expression, including joins, groups and nested queries.
  • re-linq gives you the tools to easily create your own provider, including:
    • A set of visitors that you just implement for your transformation.
    • A registry for translating methods to specific expressions in the target query language (like string methods, or ICollection.Contains).
    • A mechanism to specify eager fetching for related objects or collections that will automatically create the required queries.
  • re-linq is completely agnostic of the target query language. You can use it to create any dialect of SQL as well as other query languages such as XQL or Entity SQL. Currently, the NHibernate project uses re-linq to create HQL ASTs.


 

使用NHibernate.Linq

1、

using NHibernate.Linq;

2、使用ISession接口的Query<T>()扩展方法创建一个NHibernate.Linq查询。

var query = session.Query<User>().Where(x => x.Username == "张三").ToList();

 Query<T>()扩展方法在NHibernate.Linq.LinqExtensionMethods类中,返回System.Linq.IQueryable<T> 。

 

源代码下载:http://mvcquick.codeplex.com/  

 

posted @ 2011-10-22 20:59  GuYoung  阅读(2063)  评论(7编辑  收藏  举报