2

1 対多の子コレクションを持つエンティティ オブジェクトがあり、特定の子オブジェクトを照会する必要がある場合、NHibernate が全体をフェッチすることを回避するために、まだ思い付いていない機能または巧妙なパターンがありますか?子コレクション?

例:

class Parent 
{
    public virtual int Id { get; proteced set; } // generated PK
    public virtual IEnumerable<Child> Children { get; proteced set; }
}

class Child
{
    public virtual int Id { get; protected set; } // generated PK
    public virtual string Name { get; protected set; }
    public virtual Parent Parent { get; protected set; }
}

// mapped with Fluent

class Service
{

    private readonly ISessionFactory sessionFactory;

    public Service(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    void DoSomethingWithChildrenNamedBob(int parentId)
    {
        using(var session = sessionFactory.OpenSession())
        {
            var parent = session.Get<Parent>(parentId);
            // Will cause lazy fetch of all children!
            var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob"); 
            // do something with the children
        }
    }
}

この場合、おそらく子エンティティを直接クエリするだけなので、これが最良の例ではないことはわかっていますが、すでに親オブジェクトがあり、それを介して特定のサブツリーをトラバースする必要がある状況に遭遇しました。

4

3 に答える 3

4

短い答え:いいえ。より長い答え: 手先の早業で、これを行うことができます。

上記のRippoの回答は、「適切な」NHibernateの方法でそれを行う方法を示しています(Linq、QueryOver、またはHQLのいずれを使用するかは問題ではありません-ポイントは、クエリを実行するために親->子関係の外に出なければならないことです)。これをさらに一歩進めて、ファサードの背後に隠すことができます。ただし、そのためには、マップされたリレーションシップを完全に削除し、常にクエリに置き換える必要があります。親 -> 子のマッピングを削除しますが、子 -> 親のマッピングはそのまま残します。次に、Parent のプロパティを次のように書き直します。

public virtual IQueryable<Child> Children
{
    get
    {
        // somehow get a reference to the ISession (I use ambient context), then
        return session.Query<Child>().Where(c => c.Parent == this);
    }
}

さて、使用Parent.Childrenするとクエリ可能なコレクションが返されるので、次のように書くことができます

IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");

これを実行してマッピングを保持できる唯一の方法は、NHibernate のコレクション オブジェクトを修正する (または独自のオブジェクトを注入する) ことです。Diego Mijelshon (これらの部分の周りにいる) はまさにそのスパイクを書き、NHibernate コレクションに IQueryable サポートを追加して、あなたができるようにしました

IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");

しかし、私が見る限り、これはそれ以上進んでおらず、この機能を NH に追加する明確な計画はありません。私はディエゴのコードを実行しましたが、動作しますが、明らかに製品品質ではなく、テストされていません。また、プライベート パッチとしても公式に「リリース」されたことはないと思います。

NH イシュー トラッカーに関するディスカッションへのリンクは次のとおりです: https://nhibernate.jira.com/browse/NH-2319

ほとんどの .NET 開発者が列挙可能なものとやり取りしたいのは自然な方法であるため、NH はすぐにこれをサポートする必要があると思います。無制限のコレクションを RAM にロードするのは最悪です。しかし、従来の NH モデルはセッション -> クエリであり、99% の人がそれを使用しています。

于 2013-01-15T11:25:16.050 に答える
3

数週間前に NHusers で同じ質問をしましたが、回答が得られなかったので、答えはyou will always get all the parents children and then perform a in-memory filter. 多くの場合、これが正しい見方かもしれません。

あなたの場合、クエリを次のように書き直します:-

var childrenNamedBob = session.Query<Children>()
  .Where(w => w.Parent.Id == parentId && w.Name == "Bob");

次に、単に親を取得するために (childrenNamedBob が結果を持っている場合)、次のように呼び出すことができます:-

   var parent = childrenNamedBob.First().Parent;

またはあなたが正しく指摘したように:-

var parent = session.Get<Parent>(parentId);
于 2013-01-15T10:06:07.380 に答える