NHibernate 3.2を用いてRepository(ORuM)(十一)NHibernate、LINQを実現

18797 ワード

A 023 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貢献者はNHibernate 2であった.1.0 GAとNHibernate 2.1.2 GAバージョン設計サードパーティ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の機能が限られているため、多くの生まれつきの不足(結合とサブクエリがサポートされていない)がある.NHibernate 2を使用する場合.1.0 GAまたはNHibernate 2.1.2 GAバージョンは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クラス
   ///<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.QueryabeBaseクラス
    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.QueryabeBaseクラスは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()拡張方法を用いてNHibernateを作成する.Linqクエリー.
    var query = session.Query<User>().Where(x => x.Username == "  ").ToList();

    Query()拡張方法はNHibernate.Linq.LinqExtensionMethodsクラスではSystemに戻る.Linq.IQueryable .
     
    ソースのダウンロード:http://mvcquick.codeplex.com/