Frage

Ich habe einen Proxy für eine faule Einheit, die durch das Laden einer untergeordnete Entität in der Sitzung erstellt wurde. Eine nachfolgende auf der übergeordneten Einheit fetch nur die NH-Proxy zurückgibt. Ich brauche die eigentliche Instanz die Art zu überprüfen (die Entität Subklassen beigetreten ist). Ich muss etwas fehlt, aber ich kann nicht einen Weg, dies zu tun finden. Session.Refresh (Proxy) scheint nicht zu helfen, noch hat jeden Geschmack von HQL, dass ich versucht habe.

Kann mir jemand helfen?

War es hilfreich?

Lösung

Um einen Proxy zu erzwingen geholt aus der Datenbank zu sein, können Sie die NHibernateUtil.Initialize(proxy) Methode verwenden, oder Zugriff auf eine Methode / Eigenschaft des Proxy.

var foo = session.Get<Foo>(id);
NHibernateUtil.Initialize(foo.Bar);

Um zu überprüfen, ob ein Objekt initialisiert ist oder nicht, können Sie die NHibernateUtil.IsInitialized(proxy) Methode verwenden.

Update:

Um ein Objekt aus der Sitzung Cache zu entfernen, verwenden Sie die Session.Evict(obj) Methode.

session.Evict(myEntity);

Info über Evict und andere Methoden für die Sitzung Cache Verwaltung kann in Kapitel 14.5 der NHibernate docs.

Andere Tipps

Meiner Meinung nach, eher dann dieses Problem zu lösen, sollten Sie lieber Ihr Design überdenken. Sind Sie absolut sicher, dass Sie nicht Polymorphismus in dieser Situation verwenden können - entweder direkt Einheit verantwortlich für den Betrieb machen Sie versuchen Besuchermuster auszuführen oder zu verwenden. Ich kam in dieser Ausgabe einige Male und immer entschieden Design zu ändern - es ist in klarem Code geführt. Ich schlage vor, Sie das gleiche tun, wenn Sie absolut sicher sind, dass auf Art unter Berufung ist die beste Lösung.

Das Problem

Um beispielsweise zumindest eine gewisse Ähnlichkeit mit der realen Welt, nehmen wir an, Sie haben folgende Einheiten:

public abstract class Operation
{
    public virtual DateTime PerformedOn { get; set; }
    public virtual double Ammount { get; set; }
}

public class OutgoingTransfer : Operation
{
    public virtual string TargetAccount { get; set; }
}

public class AtmWithdrawal : Operation
{
    public virtual string AtmAddress { get; set; }
}

Es wäre natürlich ein kleiner Teil des größeren Modells sein. Und jetzt stehen vor ein Problem: für jeden konkreten Typ der Operation, gibt es eine andere Art und Weise, um es anzuzeigen:

private static void PrintOperation(Operation operation)
{
    Console.WriteLine("{0} - {1}", operation.PerformedOn,
                      operation.Ammount);
}

private static void PrintOperation(OutgoingTransfer operation)
{
    Console.WriteLine("{0}: {1}, target account: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.TargetAccount);
}

private static void PrintOperation(AtmWithdrawal operation)
{
    Console.WriteLine("{0}: {1}, atm's address: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.AtmAddress);
}

Einfache, überladene Methoden werden in einfachen Fall arbeiten:

var transfer = new OutgoingTransfer
               {
                   Ammount = -1000,
                   PerformedOn = DateTime.Now.Date,
                   TargetAccount = "123123123"
               };

var withdrawal = new AtmWithdrawal
                 {
                     Ammount = -1000,
                     PerformedOn = DateTime.Now.Date,
                     AtmAddress = "Some address"
                 };

// works as intended
PrintOperation(transfer);
PrintOperation(withdrawal);

Leider sind die überladenen Methoden bei der Kompilierung gebunden, so sobald Sie einen Array / Liste / was auch immer von Operationen, nur eine generische (Operation Betrieb) Überlastung aufgerufen wird einzuführen.

Operation[] operations = { transfer, withdrawal };
foreach (var operation in operations)
{
    PrintOperation(operation);
}

Es gibt zwei Lösungen für dieses Problem, und beide Nachteile haben. Sie können eine abstrakte / virtuelle Methode in Betrieb einführen Informationen zu ausgewählten Strom zu drucken. Aber dies wird UI Bedenken in Ihr Modell mischen, so dass für Sie nicht akzeptabel ist (ich werde Ihnen zeigen, wie Sie diese Lösung verbessern, um Ihre Erwartungen in einem Moment zu treffen).

Sie können auch viele ifs in Form erstellen:

if(operation is (ConcreteType))
   PrintOperation((ConcreteType)operation);

Diese Lösung ist hässlich und fehleranfällig. Jedes Mal, wenn Sie hinzufügen / ändern / entfernen Art der Operation, müssen Sie jeden Ort durchlaufen Sie diesen Hack verwendet und es ändern. Und wenn Sie einen Platz verpassen, werden Sie wahrscheinlich nur in der Lage sein, diese Laufzeit zu fangen -. Keine strikten Kompilierung-Kontrollen für einige Fehler (wie ein Subtyp fehlt)

Darüber hinaus wird diese Lösung fehlschlagen, sobald Sie jede Art von Proxy vorstellen.

Wie Proxy-Werke

Sie den Code unten ist sehr einfach Proxy (in dieser Implementierung als Dekorateur Muster gleiche es ist - aber diese Muster nicht gleich sind im Allgemeinen Es würde einige zusätzliche Code nehmen diese beiden Muster zu unterscheiden.)

.
public class OperationProxy : Operation
{
    private readonly Operation m_innerOperation;

    public OperationProxy(Operation innerOperation)
    {
        if (innerOperation == null)
            throw new ArgumentNullException("innerOperation");
        m_innerOperation = innerOperation;
    }


    public override double Ammount
    {
        get { return m_innerOperation.Ammount; }
        set { m_innerOperation.Ammount = value; }
    }

    public override DateTime PerformedOn
    {
        get { return m_innerOperation.PerformedOn; }
        set { m_innerOperation.PerformedOn = value; }
    }
}

Wie Sie sehen können - es gibt nur eine Proxy-Klasse für die gesamte Hierarchie. Warum? Da sollten Sie Ihren Code in einer Art und Weise schreiben, die nicht auf konkreten Typ abhängt - nur vorgesehen Abstraktion. Dieser Proxy könnte Entität Laden in der Zeit verschieben - vielleicht werden Sie es nicht benutzen? Vielleicht werden Sie nur 2 von 1000 Einheiten verwenden? Warum sie alle dann laden?

So NHibernate verwendet Proxy wie oben auf (viel anspruchsvoller, obwohl) Einheit Belastung zu verschieben. Es könnte einen Proxy pro Untertyp erstellen, aber es würde Sinn und Zweck verzögertes Laden zerstört. Wenn Sie carefuly, wie NHibernate speichert aussehen Unterklassen werden sehen Sie, dass, um zu bestimmen, welche Art Person ist, müssen Sie ihn laden. So ist es unmöglich, konkrete Proxies haben -. Sie können nur die abstrakteste haben, OperationProxy

Altough die Lösung mit ifs es ist hässlich - es war eine Lösung. Nun, wenn Sie Proxies für Ihr Problem eingeführt - es ist nicht mehr zu arbeiten. Nun bleibt uns nur mit polymorpher Methode, die aufgrund der Vermischung UI Verantwortung für Ihr Modell nicht akzeptabel ist. Lassen Sie uns das in Ordnung bringen.

Dependency Inversion und Besuchermuster

Lassen Sie uns zunächst einen Blick auf, wie die Lösung mit virtuellen Methoden aussehen würde (nur hinzugefügt Code):

public abstract class Operation
{
    public abstract void PrintInformation();
}

public class OutgoingTransfer : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                      PerformedOn, Ammount, TargetAccount);
    }
}

public class AtmWithdrawal : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          PerformedOn, Ammount, AtmAddress);
    }
}

public class OperationProxy : Operation
{
    public override void PrintInformation()
    {
        m_innerOperation.PrintInformation();
    }
}

Und jetzt, wenn Sie anrufen:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.PrintInformation();
}

alles funktioniert wie ein Charme.

Um diese UI Abhängigkeit im Modell zu entfernen, lassen Sie sich eine Schnittstelle erstellen:

public interface IOperationVisitor
{
    void Visit(AtmWithdrawal operation);
    void Visit(OutgoingTransfer operation);
}

Lassen Sie sich Modell ändert an dieser Schnittstelle abhängig:

Und nun eine Implementierung schaffen - ConsoleOutputOperationVisitor (I PrintInformation Methoden gelöscht haben):

public abstract class Operation
{
    public abstract void Accept(IOperationVisitor visitor);
}

public class OutgoingTransfer : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class AtmWithdrawal : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class OperationProxy : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        m_innerOperation.Accept(visitor);
    }
}

Was passiert hier? Wenn rufen Sie auf Betrieb ein Akzeptierennd passieren einen Besucher, die Umsetzung übernehmen wird aufgerufen, gegebenenfalls Überlastung des Besuchs Methode aufgerufen werden (Compiler kann Typ „dieser“ bestimmen). So kombinieren Sie „Power“ von virtuellen Methoden und Überlastungen geeignete Methode aufgerufen werden. Wie Sie sehen können -. Jetzt UI Referenz hier, Modell hängt nur von einer Schnittstelle, die in der Modellschicht enthalten sein können,

So, jetzt auf diese Arbeit zu bekommen, eine Implementierung der Schnittstelle:

 public class ConsoleOutputOperationVisitor : IOperationVisitor
 {
    #region IOperationVisitor Members
    public void Visit(AtmWithdrawal operation)
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.AtmAddress);
    }

    public void Visit(OutgoingTransfer operation)
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.TargetAccount);
    }

    #endregion
}

Und Code:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.Accept(visitor);
}

Ich bin mir sehr wohl bewusst, dass dies nicht eine perfekte Lösung. Sie werden immer noch die Schnittstelle und Besucher zu modifizieren, wie Sie neue Arten hinzu. Aber Sie bekommen kompilieren Zeit überprüft und wird nie etwas verpassen. Eine Sache, die wirklich schwer sein würde, mit dieser Methode zu erreichen, ist steckbar Subtypen zu bekommen - aber ich bin nicht überzeugt, dass dies ein gültiges Szenario ist sowieso. Sie werden auch dieses Muster ändern haben Ihre Bedürfnisse in konkretem Szenario gerecht zu werden, aber ich werde es dir lassen.

verzögertes Laden ausschalten, wird die tatsächliche Instanz erzwingen anstelle des NHibernate Proxy zurückgegeben.

zB ..

mapping.Not.LazyLoad ();

oder

<class name="OrderLine" table="OrderLine" lazy="false" >

Da der Proxy aus der Entity-Klasse abgeleitet ist, können Sie wahrscheinlich nur überprüfen entity.GetType (). Basetype Ihre Art zu erhalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top