Frage

Ich habe ein Problem mit nicht initialisierten Proxies in nhibernate

Die Domain Model

Lassen Sie uns sagen, ich habe zwei parallelen Klassenhierarchien: Tier, Hund, Katze und AnimalOwner, dogowner, CatOwner wo Hund und Katze erben sowohl von Tier und dogowner und CatOwner beide erben von AnimalOwner. AnimalOwner hat eine Referenz vom Typ Tier genannt OwnedAnimal.

Hier sind die Klassen im Beispiel:

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
}

Die Klassen richtig nhibernate Mapping haben, sind alle Eigenschaften persistent und alles, was faul sein kann loaded faul geladen ist.

Die Anwendung Geschäftslogik nur lassen Sie sich in einem CatOwner einen Hund in einem dogowner und eine Katze setzen.

Das Problem

Ich habe Code wie folgt:

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

Diese Methode kann durch viele verschiedene Methoden aufgerufen werden, in den meisten Fällen ist der Hund bereits im Speicher und alles ist in Ordnung, aber selten ist der Hund nicht bereits im Speicher - in diesem Fall erhalte ich eine nhibernate „nicht initialisierte Proxy“, aber die Besetzung löst eine Ausnahme, weil nhibernate genrates ein Proxy für Tier und nicht für Hunde.

Ich verstehe, dass dies ist, wie nhibernate funktioniert, aber ich brauche, um den Typen zu kennen, ohne das Objekt geladen - oder richtiger brauche ich die nicht initialisierten Proxy ein Proxy von Katze oder Hund zu sein und nicht ein Proxy für Tier

Constraints

  • Ich kann das Domänenmodell ändern, wird das Modell durch eine andere Abteilung zu mir reicht, habe ich versucht, sie zu erhalten, das Modell zu ändern und fehlgeschlagen.
  • Das eigentliche Modell ist viel komplizierter, dann das Beispiel und die Klassen haben viele Verweise zwischen ihnen, eifrig Laden mit oder dem Hinzufügen von Verknüpfungen zu den Abfragen aus der Frage aus Performance-Gründen sind.
  • Ich habe die volle Kontrolle über den Quellcode, der hbm Mapping und das Datenbankschema und ich kann sie jede mögliche Weise I (solange ich nicht die Beziehungen zwischen den Modellklassen ändern) wollen ändern.
  • Ich habe viele Methoden, wie die in dem Beispiel und ich will nicht alle von ihnen ändern.

Danke,
Nir

War es hilfreich?

Lösung

Es ist am einfachsten verzögertes Laden für das Tier Klasse auszuschalten. Sie sagen, es ist vor allem im Speicher sowieso.

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

Als eine Variante davon, können Sie auch no-proxy nutzen könnten, finden Sie unter dieser Beitrag :

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

Soweit ich sehen kann, es funktioniert nur, wenn der AnimalOwner eigentlich ein Proxy ist.

oder

Sie können Generika auf dem Tierbesitzer verwenden die Referenz eine konkrete Klasse zu machen.

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

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

oder

Sie können die DogOwners und CatOwners in separaten Tabellen abzubilden, und definieren die konkreten Tierart in der Abbildung.

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

oder

Sie verwirren ein wenig um mit NHibernate, wie rel="noreferrer"> dies in . NH ist tatsächlich in der Lage das reale Objekt hinter dem Proxy zurückzukehren. Hier etwas einfachere Implementierung, da es vorgeschlagen:

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

, die wie folgt verwendet werden kann:

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

Andere Tipps

Ich denke, wir haben vor kurzem ein ähnliches Problem, war AFAIR Lösung zu geben, ‚Animal‘ eine selbst - „Methode / Eigenschaft“:

public Animal Self { get { return this; } }

Dies könnte dann gegossen wird „Tier“ zu korrigieren. Was passiert, ist, dass Ihr Original-Objekt einen Verweis auf nhibernate Proxy-Objekt hat (wenn es träge geladen), die für alle Methoden über Tier Klasse ausgesetzt als Tier handelt (es leitet alle Anrufe an das geladene Objekt). Allerdings kann es nicht als eines Ihrer anderen Tieren gegossen werden, weil es keine von diesen ist es emuliert nur die Tierklasse. Allerdings ist die Klasse, die von AnimalProxy eingekapselt ist, kann als subclassed Tier gegossen werden, weil es eine echte Instanz richtige Klasse ist, müssen Sie nur erhalten, es ist this Referenz.

Sie versuchen, diese Methode auf dem Basisentität setzen können:

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

Vielleicht möchten Sie dies versuchen, die Proxy-Typ (unter der Annahme, NH 2.0 +), um zu sehen:

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

Aber diese Art von Gießen oder „Typ späht“ sehr schlechte Praxis sowieso ...

Wenn wir haben mit dem gleichen Problem arbeiten das Problem ist, dass der Proxy generiert ist der Proxy von Tiere anstatt von Hund.

Die Lösung, die wir verwendet wurde, war das Objekt neu geladen werden:

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

Dies geht zurück auf der Sitzung und lädt das Objekt mit dem richtigen Typ.

Hope, das hilft

Wenn Sie Fluent NHibernate verwenden, können Sie eine Auto-Mapping-Überschreibung deaktivieren verzögertes Laden für diese Eigenschaft nur:

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
scroll top