3

手元の私のタスクは、DBから直接DTOを抽出することです。ORMとして、クエリの手段としてLINQでNHibernateを使用します。以下は、私のドメインクラスと、クライアント側にデータを返すために使用するDTOクラスです(クラスは、要点を示すためにいくつかのプロパティのみを含むように縮小されています)。

public class DocLanguage
{
  public Guid Id { get; set; }

  public string Name { get; set; }

  public string PublicCode { get; set; }
}

public class Document
{
  public Guid Id { get; set; }
}

public class OutgoingDocument: Document
{
  public DocLanguage DocLanguage { get; set; }
}

public class OutgoingDocumentDto
{
  public Guid Id { get; set; }

  public Guid DocLanguageId { get; set; }

  public string DocLanguageName { get; set; }
}

これは、DBからDTOをロードするために使用するクエリです。

IQueryable<OutgoingDocument> documents = GetQueryable();

var query = from doc in documents
            select new OutgoingDocumentDto
                       {
                         Id = doc.Id,
                         DocLanguageId = doc.DocLanguage.Id,
                         DocLanguageName = doc.DocLanguage.Name
                       }

var documentList = query.ToList();

次のSQLを生成します。

exec sp_executesql N'select
  outgoingdo0_.documentId as col_0_0_,
  doclanguag1_.Id as col_1_0_,
  doclanguag1_.name as col_2_0_
from OutgoingDocuments outgoingdo0_
  inner join Documents outgoingdo0_1_
    on outgoingdo0_.documentId=outgoingdo0_1_.Id
  left outer join DicDocLanguages doclanguag1_
    on outgoingdo0_1_.docLanguageId=doclanguag1_.Id'

NULLフィールドに値がなくなるまでdocLanguageId(必須ではありません)、うまく機能します。別のケースでは、例外がスローされます。

NHibernate.Exceptions.GenericADOException was unhandled by user code
  Message=Could not execute query[SQL: SQL not available]
  Source=NHibernate
  SqlString=SQL not available
  StackTrace:
   at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 653
   at NHibernate.Impl.AbstractSessionImpl.List(IQueryExpression queryExpression, QueryParameters parameters) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\AbstractSessionImpl.cs:line 92
   at NHibernate.Impl.ExpressionQueryImpl.List() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionQueryImpl.cs:line 61
   at NHibernate.Linq.DefaultQueryProvider.ExecuteQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 103
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 35
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\DefaultQueryProvider.cs:line 40
   at Remotion.Linq.QueryableBase`1.GetEnumerator() in :line 0
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
  InnerException: System.Reflection.TargetInvocationException
   HResult=-2146232828
   Message=Exception has been thrown by the target of an invocation.
   Source=mscorlib
   StackTrace:
        at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
        at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
        at System.Delegate.DynamicInvokeImpl(Object[] args)
        at System.Delegate.DynamicInvoke(Object[] args)
        at NHibernate.Linq.ResultTransformer.TransformTuple(Object[] tuple, String[] aliases) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Linq\ResultTransformer.cs:line 25
        at NHibernate.Hql.HolderInstantiator.Instantiate(Object[] row) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Hql\HolderInstantiator.cs:line 80
        at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.GetResultList(IList results, IResultTransformer resultTransformer) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Hql\Ast\ANTLR\Loader\QueryLoader.cs:line 302
        at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Loader\Loader.cs:line 1497
        at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Loader\Loader.cs:line 1491
        at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.List(ISessionImplementor session, QueryParameters queryParameters) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Hql\Ast\ANTLR\Loader\QueryLoader.cs:line 288
        at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Hql\Ast\ANTLR\QueryTranslatorImpl.cs:line 112
        at NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Query\HQLQueryPlan.cs:line 105
        at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 643
   InnerException: System.NullReferenceException
        HResult=-2147467261
        Message=Object reference not set to an instance of an object.
        Source=Anonymously Hosted DynamicMethods Assembly
        StackTrace:
             at lambda_method(Closure , Object[] )
        InnerException: 

明示的にnullをチェックし、次のコードを使用してDTOプロパティにデフォルト値を提供しようとしました。

var query = from doc in documents
            select new OutgoingDocumentDto
                       {
                         Id = doc.Id,
                         DocLanguageId = doc.DocLanguage == null ?
                                             Guid.Empty :
                                             doc.DocLanguage.Id,
                         DocLanguageName = doc.DocLanguage == null ?
                                               Guid.Empty :
                                               doc.DocLanguage.Name
                       }

その結果、このSQLが生成されます。

exec sp_executesql N'select
  outgoingdo0_.documentId as col_0_0_,
  doclanguag1_.Id as col_1_0_,
  doclanguag1_.Id as col_2_0_,
  doclanguag1_.Id as col_3_0_,
  doclanguag1_.name as col_4_0_,
  doclanguag1_.Id as Id40_0_,
  doclanguag1_.Id as Id40_1_,
  doclanguag1_.name as nameRU40_0_,
  doclanguag1_.publicCode as publicCode40_0_,
  doclanguag1_.name as nameRU40_1_,
  doclanguag1_.publicCode as publicCode40_1_
from OutgoingDocuments
  outgoingdo0_ inner join Documents outgoingdo0_1_
    on outgoingdo0_.documentId=outgoingdo0_1_.Id
  left outer join DicDocLanguages doclanguag1_
    on outgoingdo0_1_.docLanguageId=doclanguag1_.Id'

NHibernate LINQプロバイダーは、LINQクエリのすべてのメンバーアクセス式をSQLクエリに率直に変換して、重複するフィールド選択を生成しているようです。実際、それは機能し、おそらくオプティマイザーはこのクエリプロセスを前のクエリプロセスと同じくらい速くします。しかし、それは醜く、より多くのプロパティが含まれるにつれて醜くなります。

このタスクは一般的なタスクだと思います。select句にデフォルト値を使用してプロパティを提供する正しい方法が存在するかどうかを知りたいと思います。

4

2 に答える 2

2

これはおそらく最善の解決策ではありませんが、 NHibernateにLinqクエリで使用されるカスタム拡張メソッドを認識して変換させる方法を示すブログ投稿を見つけました。この例では、特に「合体」操作を扱います。解決策は、Coalesce()拡張メソッドを作成し、メソッドにカスタムHQLジェネレーターを指定してNHibernateに「登録」し、デフォルト構成で指定されたカスタムレジストリジェネレーターを使用してNHibernateの「ナレッジベース」に追加することです。 HQLの生成。

理論的には、ターゲットスキーマの有効なSQLに変換する拡張メソッドに対してこれを行うことができます。これを使用して、データベースに配置したカスタムSQL関数をNHibernateで呼び出すこともできます(ただし、私は逸脱します)。

于 2012-09-10T17:21:17.503 に答える
1

この問題はDTOレベルで解決できます。DBオブジェクトを内部のnull許容フィールドにバインドし、それをパブリックプロパティのバッキングフィールドとして使用できます。ここで、デフォルト値のロジックを配置します。そのようです:

internal virtual DocLanguage docLanguage {get;set;}
public int DocLanguageName 
{
    get{ return docLanguage == null ? Guid.Empty : docLanguage.Name; }
}
于 2012-09-10T18:21:22.573 に答える