19

nhibernate の初期化されていないプロキシに問題があります

ドメインモデル

2 つの並列クラス階層があるとします。Animal、Dog、Cat、および AnimalOwner、DogOwner、CatOwner で、Dog と Cat は両方とも Animal から継承され、DogOwner と CatOwner は両方とも AnimalOwner から継承されます。AnimalOwner には、OwnedAnimal というタイプの Animal の参照があります。

例のクラスは次のとおりです。

public abstract class Animal
{
   // some properties
}

public class Dog : Animal
{
   // some more properties
}

public class Cat : Animal
{
   // some more properties
}

public class AnimalOwner 
{
   public virtual Animal OwnedAnimal {get;set;}
   // more properties...
}

public class DogOwner : AnimalOwner
{
   // even more properties
}

public class CatOwner : AnimalOwner
{
   // even more properties
}

クラスには適切な nhibernate マッピングがあり、すべてのプロパティは永続的であり、遅延ロードできるものはすべて遅延ロードされます。

アプリケーションのビジネス ロジックでは、DogOwner に Dog を設定し、CatOwner に Cat を設定することしかできません。

問題

次のようなコードがあります。

public void ProcessDogOwner(DogOwner owner)
{
   Dog dog = (Dog)owner.OwnedAnimal;
   ....
}

このメソッドは、多くの異なる方法で呼び出すことができます。ほとんどの場合、犬は既にメモリ内にあり、すべて問題ありませんが、犬がまだメモリ内にないことはめったにありません。この場合、nhibernate の「初期化されていないプロキシ」を取得しますが、キャストがスローしますnhibernate は Dog ではなく Animal のプロキシを生成するため、例外です。

これが nhibernate の仕組みであることは理解していますが、オブジェクトをロードせずに型を知る必要があります。または、より正確には、初期化されていないプロキシが Animal のプロキシではなく、Cat または Dog のプロキシである必要があります。

制約

  • ドメインモデルを変更できません。モデルは別の部門から渡されました。モデルを変更してもらいましたが失敗しました。
  • 実際のモデルは例よりもはるかに複雑であり、クラス間で多くの参照が行われます。パフォーマンス上の理由から、熱心な読み込みを使用したり、クエリに結合を追加したりすることは問題外です。
  • 私はソース コード、hbm マッピング、およびデータベース スキーマを完全に制御しており、(モデル クラス間の関係を変更しない限り) 好きなように変更できます。
  • 例のようなメソッドがたくさんありますが、それらすべてを変更したくありません。

ありがとう、
ニル

4

6 に答える 6

24

動物クラスの遅延読み込みをオフにするのが最も簡単です。とにかく、それはほとんどメモリにあるとあなたは言います。

<class name="Animal" lazy="false">
<!-- ... -->
</class>

その変形として、使用することもできます。この投稿no-proxyを参照してください:

<property name="OwnedAnimal" lazy="no-proxy"/>

私が見る限り、それはAnimalOwner実際にプロキシである場合にのみ機能します。

また

動物の所有者でジェネリックスを使用して、参照を具体的なクラスにすることができます。

class AnimalOwner<TAnimal>
{
  virtual TAnimal OwnedAnimal {get;set;}
}

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

また

DogOwnersCatOwnersを別々のテーブルにマッピングし、マッピングで具体的な動物の種類を定義できます。

<class name="CatOwner">
  <!-- ... -->
  <property name="OwnedAninal" class="Cat"/>
</class>
<class name="DogOwner">
  <!-- ... -->
  <property name="OwnedAninal" class="Dog"/>
</class>

また

このブログで提案されているように、NHibernateを少しいじります。NHは、実際にはプロキシの背後にある実際のオブジェクトを返すことができます。ここで提案されているように、少し単純な実装があります。

    public static T CastEntity<T>(this object entity) where T: class
    {
        var proxy = entity as INHibernateProxy;
        if (proxy != null)
        {
            return proxy.HibernateLazyInitializer.GetImplementation() as T;
        }
        else
        {
            return entity as T;
        }
    }

これは次のように使用できます:

Dog dog = dogOwner.OwnedAnimal.CastEntity<Dog>();
于 2009-04-16T12:28:54.097 に答える
12

最近、同様の問題が発生したと思います.AFAIRの解決策は、「動物」に自己「メソッド/プロパティ」を与えることでした:

public Animal Self { get { return this; } }

これは、「動物」を修正するためにキャストできます。元のオブジェクトには nhibernate プロキシ オブジェクトへの参照があり (遅延ロードされている場合)、Animal クラスを介して公開されているすべてのメソッドの Animal として機能します (ロードされたオブジェクトへのすべての呼び出しを渡します)。ただし、これらのいずれでもないため、他の動物としてキャストすることはできません。Animal クラスをエミュレートするだけです。ただし、AnimalProxy によってカプセル化されたクラスは、正しいクラスの実際のインスタンスであるため、サブクラス化された animal としてキャストできます。その参照にアクセスするだけで済みますthis

于 2009-04-20T18:20:14.130 に答える
1

このメソッドを基本エンティティに配置してみてください。

public virtual T As<T>() where T : Entity {
      return this as T;
}
于 2015-08-31T18:51:28.413 に答える
0

Fluent NHibernate を使用する場合は、自動マッピング オーバーライドを使用して、そのプロパティのみの遅延読み込みをオフにすることができます。

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
于 2014-06-16T18:32:07.473 に答える
0

これを試して、プロキシされたタイプを確認することをお勧めします (NH 2.0+ を想定):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

しかし、この種のキャストまたは「型の覗き見」はとにかく非常に悪い習慣です...

于 2009-02-18T00:52:40.973 に答える
0

同じ問題に取り組んできた場合、問題は、生成されたプロキシが Dog ではなく Animal のプロキシであることです。

私たちが使用した解決策は、オブジェクトをリロードすることでした:

Dog dog = this.CurrentSession.Load<Dog>(owner.OwnedAnimal.AnimalID);

これにより、セッションに戻り、オブジェクトが正しいタイプで再ロードされます。

お役に立てれば

于 2010-07-21T14:46:16.503 に答える