4

NH Criteria を使用してエンティティを取得し、選択フィールドをカスタム クラスに投影しています (MVC ビューに表示するためにデータを ViewModel に投影するのと少し似ています)。

これは、ProjectionList を使用して簡単に行うことができます。

var emailCriteria = mSession.CreateCriteria<Email>();
emailCriteria.SetProjection(
    Projections.ProjectionList()
        .Add(Projections.Property("Subject"), "Subject")
);
emailCriteria.SetResultTransformer(Transformers.AliasToBean<EmailDataModel>());
var result = emailCriteria.List<EmailDataModel>();

ただし、私のエンティティにはコレクションが含まれており、それも元に戻し、コレクションとしてカスタム クラスに投影したいと考えています。

私のドメイン モデルは (簡略化された形式で) 次のようになります。

public class Email {
    public string Subject
    public List<EmailAttachment> Attachments
    etc...
}

public class EmailAttachment {
    public UploadedFile File
}

public class UploadedFile {
    public string Filename
    public UploadedFileData Data
}

public class UploadedFileData {
    public byte[] Data
}

投影したい「データモデル」クラスは次のとおりです。

public class EmailDataModel {
    public string Subject
    public List<EmailAttachmentDataModel> Attachments
}

public class EmailAttachmentDataModel {
    public string Filename
    public byte[] Data
}

これらのモデルが非常によく似ていることがわかったので、「何がポイントなの?」と考えても仕方ありませんが、それは単純化したからです。ドメイン オブジェクトを便利なデータ モデルにフラット化できると便利です。

私の大きな問題は、子オブジェクト (この場合は UploadedFile.Filename と UploadedFileData.Data) の深いところから必要なフィールドにアクセスし、それらを EmailAttachmentDataModel コレクションとして EmailDataModel に投影する方法を見つけることです。

EmailCriteria.CreateAlias または EmailCriteria.CreateQuery のいずれかを使用して、子コレクションへのアクセスについて説明しているオンラインの記事をたくさん読みましたが、子コレクションをコレクションとして投影する方法を説明するものは見つかりませんでした。

これが、NH Criteria クエリをいじることに興味のある人にとって、役に立つ演習になることを願っています。

4

1 に答える 1

6

OK、NHibernate3へのアップグレードとQueryOverの使用は解決したと思います。これが私のコードが今どのように見えるかです:

//Declare entities
Email email = null;
EmailAttachment attachment = null;
UploadedFile file = null;
Byte[] fileData = null;

//Select data from parent and child objects
var results = mSession.QueryOver<QueuedEmail>(() => email)
   .JoinAlias(() => email.Attachments, () => attachment, JoinType.LeftOuterJoin)
   .JoinAlias(() => attachment.File, () => file, JoinType.LeftOuterJoin)
   .JoinAlias(() => file.Data, () => fileData, JoinType.LeftOuterJoin)
   .TransformUsing(Transformers.DistinctRootEntity)
   .List<Email>()

   //Loop through results projecting fields onto POCO
   .Select(x => new EmailDataModel()
   {
       Id = x.Id,
       Body = x.Body,
       AttachmentCount = x.Attachments.Count(),
       FromAddress = x.FromAddress,
       //Loop through child collection projecting fields onto POCO
       Attachments = x.Attachments.Select(attach => new EmailAttachmentDataModel()
       {
           Data = attach.File.Data.Data,
           Filename = attach.File.Filename,
           Id = attach.Id
       }).ToArray() //NB Now projecting this collection as an array, not a list
   }).ToArray();

ですからあります。結果は、必要なデータを含むフラット化されたクラスと、添付ファイルのコレクション(それぞれ、データ構造からの2つのフィールドのみが含まれています-適切にフラット化されています)です。

なぜあなたはこれをする必要がありますか?

  1. 本当に必要なフィールドのみにフラット化することで、結果を単純化します。

  2. 私のデータは、誤ってデータを更新することを恐れずに渡すことができるクラスに安全にカプセル化されるようになりました(これは、NHデータエンティティを返すだけの場合に発生する可能性があります)。

  3. 最後に(そして最も重要なことですが)、上記のコードはSELECTステートメントを1つしか生成しないためです。元のCriteriaクエリに固執した場合、行ごとに1つのSELECTが生成され、さらにチェーンのさらに下の子に対してさらに多くのSELECTが生成されます。少数を扱っている場合は問題ありませんが、数千行を返す可能性がある場合は問題ありません(この場合のように、これは電子メールエンジンのWebサービスです)。

これがNHibernateクエリにもう少しプッシュしたい人にとって役立つことを願っています。個人的には、今は自分の人生をやり遂げることができてうれしいです!

于 2011-02-14T14:40:28.580 に答える