NHibernateの代理警告を狭める

私たちは、データアクセスのためにNHを利用するASP.NET MVCアプリケーションを構築しています。 NH Profilerを使用すると、「WARN:Domain.CaseTaskへのプロキシの絞り込み - この操作は==」のような警告が表示されます。 NH Linqプロバイダを使用して、サブクラスごとにテーブルにマップされたクラスのクエリを実行するときに、これらを非常に頻繁に取得します。

Query().Where(c => c.Assignee == Of || c.Operator == Of)

CaseTaskクラスがTaskから継承するクラスでは、警告がトリガーされます。

インターネット上の警告に関する情報は不十分で、ほとんど無視されるべきことを示唆しています...この警告は正確に何について警告していますか?これが正しいものでなければならないのですか?

6

2 答え

現実はより複雑です。 session.Load を使用してエンティティをロードするか、遅延ロードされたプロパティにアクセスすると、Hibernateはプロキシオブジェクトを返します。そのプロキシオブジェクトは、そのプロパティのいずれかに初めてアクセスしたときに、水和(データはDBからロードされます)されます。これを実現するために、NHibernateは、エンティティクラスを拡張するプロキシクラスを生成し、すべてのプロパティゲッターとセッターをオーバーライドします。プロキシとエンティティクラス(プロキシベースクラス)を区別する方法がないため、継承が使用されていない場合は完全に機能します。単純なテストプロキシはMyEntity が常に動作します。

Personエンティティがあるとしましょう:

class Person {
 //lazy-loaded
  public Animal Pet { get; set; }
}

また、 Animal クラス階層もあります。

public abstract class Animal { ... }
public class Cat { ... }
public class Dog { ... }

ここで、 Pet プロパティが遅延ロードされていると仮定し、NHibernateに人のペットを頼むときにプロキシオブジェクトを取得します:

var pet = somePerson.Pet;//pet will be a proxy

しかし、 Pet は遅延ロードされたプロパティなので、NHは Cat Dog のインスタンスになるかどうかを知りません。 Animal を拡張するプロキシを作成します。プロキシはペットはAnimal のテストに合格しますが、ペットはCat またはペットis Dog のいずれかのテストに失敗します。

Now assume that you will access some property of pet object, forcing NH to load data from DB. Now NH will know that your pet is e.g. a Cat but proxy is already generated and cannot be changed. This will force NHibernate to issue a warning that original proxy for pet that extends type Animal will be narrowed to type Cat. This means that from now on proxy object for animal with pet.Id that you create using session.Load(pet.Id) will extend Cat from now. This also means that since Cat is now stored as a part of session, if we load a second person that shares cat with the first, NH will use already available Cat proxy instance to populate lazy-loaded property.

One of the consequences will be that object reference to pet will be different that reference obtained by session.Load(pet.Id) (in object.ReferencesEqual sense).

// example - say parent and child share *the same* pet
var pet = child.Pet;//NH will return proxy that extends Animal
pet.DoStuff();//NH loads data from DB

var parent = child.Parent;//lazy-loaded property
var pet2 = parent.Pet;//NH will return proxy that extends Cat

Assert.NotSame(pet, pet2);

今これがあなたに害を与えるかもしれないとき:

  1. エンティティをコード内に Set または Dictionary に配置したり、 Equals/GetHashCode ペアを使用します。これは、カスタム Equals/GetHashCode 実装を提供することで簡単に修正できます( http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let- hibernate-steal-your-identity.html?page = 1

  2. プロキシオブジェクトをターゲットタイプにキャストしようとすると、 (Cat)pet -nhibernate "> NHibernateで正しいタイプのプロキシを取得する)

だから道徳的なのは、あなたのドメインモデルでできるだけ多くの継承を避けることです。

3
追加された

この警告は、サブクラスであるプロパティまたはフィールドを持つクラスに関するものです。 IE:

public class Animal
{
    public int Id {get;set;}
}

public class Cat : Animal
{
    public int Weight {get;set;}
}

public class Person
{
    public Cat Pet {get;set;}
}

NHibernateは、動作が予測不可能になるため、あなたのためにキャストしたくないので、人物エンティティを読み込むと動揺します。あなたがNHibernateに(他のロジックの中でも)Equalsを扱う方法を伝えない限り、それ自身でその比較を行う方法はわかりません。

これを修正するための基本的な考え方は、NHibernateが基本クラスオブジェクトをグラフに入れさせてからキャストを処理することです(この設定ではコードを単純化するためにこれを行っていますが、完全なgetter/setterとしてプロパティを保持することによって):

public class Animal
    {
        public int Id {get;set;}
    }

public class Cat : Animal
{
    public int Weight {get;set;}
}

public class Person
{
    private Animal _pet;
    public Cat Pet {
        get{return _pet as Cat;}
    }
}
2
追加された
これは大きな問題ですか?このWARNを無視するとどうなりますか?
追加された 著者 Beatles1692,
大きな問題であるかどうかは、受け入れるリスクのレベルに依存します。あなたのコードとデータベースとの間には常に切り離しがあるので、いつでも確実にキャスティングが動作するとは限りません。これにより、診断が難しく、データベースやコードを変更することなく解決できないバグが発生します。
追加された 著者 Fourth,