Pergunta

Eu tenho um problema com não inicializado proxies em nhibernate

O modelo de domínio

Vamos dizer que tenho duas hierarquias de classe paralelas: animal, cão, gato e AnimalOwner, dogowner, catowner onde o cão e gato ambos herdam Animal e dogowner e catowner ambos herdam AnimalOwner. AnimalOwner tem uma referência do tipo animal chamado OwnedAnimal.

Aqui estão as classes no exemplo:

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
}

As classes têm mapeamento nhibernate adequada, todas as propriedades são persistentes e tudo o que pode ser preguiçoso carregado é preguiçoso carregado.

A lógica de negócios do aplicativo só permitem que você defina um cão em um dogowner e um gato em um catowner.

O Problema

Eu tenho um código como este:

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

Este método pode ser chamado por muitos métodos de diffrent, na maioria dos casos, o cão já está na memória e tudo está ok, mas raramente o cão não é já na memória - neste caso, eu recebo um nhibernate "procuração não inicializado", mas o elenco lança uma exceção porque genrates nhibernate um proxy para animal e não para cão.

Eu entendo que isso é como funciona o nhibernate, mas eu preciso saber o tipo sem carregar o objeto -. Ou, mais corretamente Eu preciso o proxy não inicializada para ser um proxy de gato ou cachorro e não um proxy de animal

Restrições

  • Eu não posso mudar o modelo de domínio, o modelo é entregue a mim por um outro departamento, eu tentei levá-los a mudar o modelo e falhou.
  • O modelo actual é muito mais complicado, em seguida, o exemplo e as classes têm muitas referências entre eles, usando o carregamento ansioso ou adicionar se junta às consultas está fora de questão por razões de desempenho.
  • Eu tenho total controle do código-fonte, o mapeamento hbm e o esquema de banco de dados e eu posso mudá-los de qualquer maneira que eu quero (contanto que eu não mudar as relações entre as classes do modelo).
  • Eu tenho muitos métodos como a do exemplo e eu não quero modificar todos eles.

Obrigado,
Nir

Foi útil?

Solução

É mais fácil para desligar o carregamento lento para a classe animal. Você diz que é principalmente na memória de qualquer maneira.

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

Como uma variante disso, você também pode usar no-proxy, consulte este post :

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

Tanto quanto eu posso ver, ele só funciona quando o AnimalOwner é realmente um proxy.

ou

Você pode usar os genéricos sobre o proprietário animal para fazer a referência uma classe concreta.

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

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

ou

Você pode mapear o DogOwners e CatOwners em mesas separadas, e definir o tipo concreto de animais no mapeamento.

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

ou

Você confusão um pouco por aí com NHibernate, como proposto na este blogue . NH é realmente capaz de retornar o objeto real por trás do proxy. Aqui um pouco implementação mais simples, como proposto lá:

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

que pode ser usado como este:

Dog dog = dogOwner.OwnedAnimal.CastEntit<Dog>();

Outras dicas

Eu acho que recentemente teve um problema semelhante, solução AFAIR era dar 'Animal' a auto - "método / propriedade":

public Animal Self { get { return this; } }

Este poderia então ser escalado para "animal" correta. O que acontece é que o seu objeto original tem uma referência ao objeto proxy nhibernate (quando é preguiçosamente carregado), que atua como animal para todos os métodos expostos via classe Animal (ele passa todas as chamadas para o objeto carregado). No entanto, não pode ser escalado como qualquer um de seus outros animais porque não é nada disso, só emula a classe Animal. No entanto, a classe que é encapsulado por AnimalProxy pode ser escalado como animais subclasse porque é um exemplo real da classe correta, você só precisa chegar a ele de referência this.

Você pode tentar colocar este método em sua entidade de base:

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

Você pode querer tentar isso para ver o tipo de proxy (assumindo NH 2.0 +):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

Mas este tipo de vazamento ou "Tipo de espreitar" é muito má prática de qualquer maneira ...

Se temos vindo a trabalhar com o mesmo problema, a questão é que o proxy gerado é o proxy do animal e não de cão.

A solução que usamos foi para recarregar o objeto:

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

Isso remonta à sua sessão e recarrega o objeto com o tipo correto.

Espero que isso ajude

Se você usar Fluent NHibernate, você pode usar uma substituição de mapeamento automático para desligar o carregamento lento por apenas que a propriedade:

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top