nhibernate에서 올바른 유형의 프록시를 얻습니다
-
03-07-2019 - |
문제
나는 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();
}
}