문제

나는 nhibernate에서 초기화되지 않은 프록시에 문제가 있습니다

도메인 모델

동물, 개, 고양이 및 동물 소유자, 개 소유자, 개와 고양이가 동물과 도그로에서 물려받은 카 타운과 캐비너가 동물 소유자로부터 물려받는 두 가지 병렬 계층이 있다고 가정 해 봅시다. Animalsowner는 Ownedanimal이라는 유형의 동물을 참조합니다.

예제의 수업은 다음과 같습니다.

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 매핑이 있으며 모든 특성은 지속적이며 게으른로드 할 수있는 모든 것이 게으른로드됩니다.

응용 프로그램 비즈니스 로직을 통해 개를 개 소유자와 고양이에 고양이를 세게 할 수 있습니다.

문제

다음과 같은 코드가 있습니다.

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

이 방법은 여러 가지 다른 방법으로 호출 할 수 있습니다. 대부분의 경우 개는 이미 기억 상태에 있고 모든 것이 괜찮지 만 개는 아직 기억에 빠지지 않습니다.이 경우에는 nhibernate "Unitialized Proxy"를 얻지 만 캐스트가 던져집니다. nhibernate는 개가 아닌 동물의 대리를 장기하기 때문에 예외입니다.

나는 이것이 nhibernate의 작동 방식이라는 것을 이해하지만, 물체를로드하지 않고 유형을 알아야한다. 또는 더 정확하게는 초기화되지 않은 프록시가 고양이 나 개의 대리가되어 동물의 대리가 아닌 것이 필요하다.

제약

  • 도메인 모델을 변경할 수없고, 모델을 다른 부서에서 나에게 전달하고 모델을 변경하도록 시도하고 실패했습니다.
  • 실제 모델은 예제보다 훨씬 더 복잡하며 클래스는 쿼리에 열렬한 로딩 또는 조인을 추가하는 것을 사용하여 성능의 이유로 문제가되지 않습니다.
  • 소스 코드, HBM 매핑 및 데이터베이스 스키마를 완전히 제어하고 있으며 모델 클래스 간의 관계를 변경하지 않는 한 원하는 방식으로 변경할 수 있습니다.
  • 나는 예제에있는 것과 같은 많은 방법을 가지고 있으며 그들 모두를 수정하고 싶지 않습니다.

감사,
니르

도움이 되었습니까?

해결책

동물 수업에 게으른 짐을 끄는 것이 가장 쉽습니다. 당신은 어쨌든 그것이 주로 기억에 있다고 말합니다.

<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>
{
}

또는

당신은 매핑 할 수 있습니다 DogOwners 그리고 CatOwners 별도의 테이블에서, 매핑에서 콘크리트 동물 유형을 정의하십시오.

<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.CastEntit<Dog>();

다른 팁

우리는 최근에 비슷한 문제가 있다고 생각합니다. Afair 솔루션은 '동물'자아 -"방법/속성"을 제공하는 것이 었습니다.

public Animal Self { get { return this; } }

그런 다음 "동물"을 수정하기 위해 캐스트 될 수 있습니다. 당신의 원래 객체는 nhibernate 프록시 객체 (게으르게로드 될 때)를 참조하여 동물 클래스를 통해 노출 된 모든 방법에 대해 동물 역할을하는 것입니다 (모든 호출을로드 된 객체에 전달 함). 그러나 다른 동물로 캐스트 될 수는 없습니다. 왜냐하면 이들 중 어느 것도 아니기 때문입니다. 그러나 AnimalProxy가 캡슐화 한 클래스는 올바른 클래스의 실제 사례이기 때문에 서브 클래스 동물로 캐스트 될 수 있습니다. this 참조.

이 메소드를 기본 엔티티에 넣을 수 있습니다.

public virtual T As<T>() where T : Entity {
      return this as T;
}

프록시 된 유형 (NH 2.0+를 가정)을 볼 수 있도록 이것을 시도 할 수 있습니다.

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

그러나 어쨌든 이런 종류의 캐스팅 또는 "타입 엿보기"는 매우 나쁜 연습입니다 ...

우리가 같은 문제로 작업 한 경우 문제는 생성 된 프록시가 개가 아닌 동물의 대리라는 것입니다.

우리가 사용한 솔루션은 객체를 다시로드하는 것이 었습니다.

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

이것은 세션으로 돌아가서 올바른 유형으로 객체를 다시로드합니다.

도움이 되었기를 바랍니다

Fluent Nhibernate를 사용하는 경우 자동 매핑 재정의를 사용하여 해당 속성에 대한 게으른로드를 끄는 것입니다.

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top