1

Die letzten zwei Wochen waren meine ersten Erfahrungen mit Castle ActiveRecord und generell mit dem ActiveRecord-Muster. Ich arbeite an einem großen System, das es häufig verwendet, und ich habe einige seltsame SQL-Transaktionsprobleme (wie das folgende) gefunden, während ich daran arbeite. Ich werde eine vereinfachte Version des einen geben, der mich total verblüfft hat:Castle ActiveRecord - Immer Kinder aktualisieren, warum?

DER HINTERGRUND:

ich eine Activerecord-Klasse haben, machen wir es Benutzeraufruf.

Angenommen, dieser Benutzer hat viele "Pet" -Objekte.

[ActiveRecord] 
public class User: PersistentBase<User> 
{ 
//... 
     [PrimaryKey] 
    public long Id 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Date and time the object was first persisted to the database 
    /// </summary> 
    [Property, ValidateNonEmpty] 
    public DateTime CreationDate 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Date and time the object was last persisted to the database 
    /// </summary> 
    [Property, ValidateNonEmpty] 
    public DateTime ModificationDate 
    { 
     get; 
     set; 
    } 
    /// <summary> 
    /// Property used for optimistic concurrency 
    /// </summary> 
    [Version] 
    public int LockCount { get; set; } 

[HasMany(typeof(Pet), Cascade = ManyRelationCascadeEnum.SaveUpdate, Lazy = false, OrderBy = "Id")] 
     public IList<Pet> Pets { get; private set; } 

//... 

    protected override bool BeforeSave(IDictionary state) 
    { 
     bool retval = base.BeforeSave(state); 
     DateTime now = DateTime.Now; 
     state["CreationDate"] = now; 
     state["ModificationDate"] = now; 
     return retval; 
    } 

    /// <summary> 
    /// Called when a dirty object is going to be updated in the db. Use this 
    /// hook to update ModificationDate. 
    /// </summary> 
    /// <param name="id"></param> 
    /// <param name="previousState"></param> 
    /// <param name="currentState"></param> 
    /// <param name="types"></param> 
    /// <returns></returns> 
    protected override bool OnFlushDirty(object id, IDictionary previousState, IDictionary currentState, IType[] types) 
    { 
     bool retval = base.OnFlushDirty(id, previousState, currentState, types); 
     currentState["ModificationDate"] = DateTime.Now; 
     return retval; 
    } 

} 

[ActiveRecord] 
public class Pet : PersistentBase<Pet> 
{ 

    [PrimaryKey] 
    public long Id 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Date and time the object was first persisted to the database 
    /// </summary> 
    [Property, ValidateNonEmpty] 
    public DateTime CreationDate 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Date and time the object was last persisted to the database 
    /// </summary> 
    [Property, ValidateNonEmpty] 
    public DateTime ModificationDate 
    { 
     get; 
     set; 
    } 
    /// <summary> 
    /// Property used for optimistic concurrency 
    /// </summary> 
    [Version] 
    public int LockCount { get; set; }  

//... 

[BelongsTo("OwnerId")] 
public User User { get; set; } 

//... 

    protected override bool BeforeSave(IDictionary state) 
    { 
     bool retval = base.BeforeSave(state); 
     DateTime now = DateTime.Now; 
     state["CreationDate"] = now; 
     state["ModificationDate"] = now; 
     return retval; 
    } 

    /// <summary> 
    /// Called when a dirty object is going to be updated in the db. Use this 
    /// hook to update ModificationDate. 
    /// </summary> 
    /// <param name="id"></param> 
    /// <param name="previousState"></param> 
    /// <param name="currentState"></param> 
    /// <param name="types"></param> 
    /// <returns></returns> 
    protected override bool OnFlushDirty(object id, IDictionary previousState, IDictionary currentState, IType[] types) 
    { 
     bool retval = base.OnFlushDirty(id, previousState, currentState, types); 
     currentState["ModificationDate"] = DateTime.Now; 
     return retval; 
    } 

} 

nun beide verfügen über eine automatische ID-Felder (betreut von SQL Server 2005).

DAS PROBLEM:

Wenn ich ein neues Haustier in einem User gehen voran und fügen Sie die bereits vorhandene Haustiere hat und der Benutzer speichern, wie ich sehe, wenn ich den SQL Profiler ausgeführt, dass jeder einzelne der Haustiere hat UPDATE angerufen ... aber kein einziger wurde überhaupt verändert.

Ich warf überall Haltepunkte und fand, dass, wenn ich den Benutzer speichere, jedes der Haustiere "OnFlushDirty" genannt hat (wieder, obwohl sie sich nie änderten).

Ein externer Prozess, der diese Benutzer und Haustiere betrachtet (und gelegentlich ändert), führt zu schwerwiegenden Transaktionsproblemen, die vollständig vermieden werden könnten, wenn das obige Szenario NUR das hinzugefügte Pet einfügen würde (und nicht UPDATE der Tiere) wurden nicht geändert).

DIE FRAGE:

Mache ich etwas darüber eine große no-no in Bezug auf die dafür sorgen, ist eine solche Situation nicht geschieht?

Vielen Dank für Ihre Hilfe!

* EDIT 1: OnFlushDirty null previousState._values ​​hat *

EDIT: OH! Ich habe fast den seltsamsten Teil von allem vergessen!

Wenn OnFlushDirty wird auf diese Tiere genannt, die PreviousState und current existiert ... sie haben beide (ein Wörterbuch zu sein) eine interne Variable _values, die die Werte der vorherigen und aktuellen Zustände haben sollte ...

... nur currentStates hat diese Variable ausgefüllt. Die Variable "_values" von previousState wird auf "null" gesetzt. Beachten Sie, dass dies für alle Haustiere gilt, die vorher schon einmal existierten. previousState sollte immer mit etwas gefüllt sein, oder?

* EDIT 2: Nach dem automatischen Eigenschaften ersetzen ... *

ersetzt ich das Auto Eigenschaftslisten mit einem traditionellen Private-Mitglied Eigenschaftenaccessoren. Es schien keinen Unterschied zu machen. Ich habe NHProfiler auf dem System installiert und festgestellt, dass NHProfiler keine Verbindung zu meiner Web-App herstellen konnte, wenn ich es über IIS laufen ließ (ich verwende IIS7/Win7 mit Visual Studio 2008).

Ich dachte, ich würde versuchen, stattdessen Visual Studio "ASP.NET Development Server" zu verwenden, um zu sehen, ob NHProfiler die App dann sehen würde.

Zwei Dinge passiert ist, als ich dies tat:

1) sah NHProfiler meine App und begann das Sammeln von Daten 2) Die mehreren UPDATES auf die Kinder getan ging

weg

jedoch zu IIS7 Zurückschalten/Win7, die mehrere Updates weiterhin passieren.

Bedeutet dies, dass es möglicherweise eine Art von Konfigurationsproblem ist? Soweit ich weiß, sollte sich nichts in meiner Konfiguration ändern, außer der URL, zu der ich navigiere (http://localhost in IIS, http://localhost:(some_random_port) mit dem ASP.NET Entwicklungsserver), wenn ich die verschiedenen Servertypen verwende. Warum ändern sich die beiden obigen Situationen plötzlich?

Antwort

1

IIRC Schloss Activetrecord stützt sich auf NHib.

In solch einem Fall hat eine Liste eine spezielle Semantik in Nhib, da sie eine geordnete Liste sein soll. Auch wenn Sie möglicherweise keine Positionseigenschaft definiert haben, werden die Elemente in der sortierten Liste aktualisiert, als ob sie diese Spalte hätte.

Ich habe dies nicht verifiziert, aber IIRC, das ist dein Problem.

+0

Oh, interessant! Ich werde versuchen, das durch einen allgemeineren Mechanismus zu ersetzen ... würde ICollection das Problem stoppen? Oder vielleicht einfach IEnumerable? – EdgarVerona

+0

Überprüfen Sie diesen Link: https://www.hibernate.org/hib_docs/nhibernate/html_single/#collections-persistent und beachten Sie den Absatz beginnend mit "Alle Sammlungstypen außer ISet und Tasche haben eine Indexspalte" Es gibt weitere Informationen zu Ich hatte es, aber ich glaube, das sollte dir den Anfang machen. –

+0

Ah, danke! Ich schätze es sehr! Ich lese jetzt die Dokumente durch, ich werde es versuchen und Sie wissen lassen, wie es geht! – EdgarVerona

1

Es ist tot einfach. ActiveRecord kann C# -Autoproperties in Auflistungen nicht verarbeiten (wegen des Schnittstellentyps). Verwenden Sie ein Trägerfeld statt und initialisieren es mit

private IList<Pet> pets = new List<Pet>(); 

-Markus

+0

Oh, das habe ich nicht gewusst! Ist das irgendwo in der Dokumentation? Ich habe davon nichts erwähnt, aber es ist definitiv gut zu wissen. Ich werde es versuchen, sobald ich komme zurück zu ihr am Montag und lass es dich wissen! Kennen Sie irgendwelche gute Ressourcen, wo ich mehr über diese "Gotchas" in ActiveRecord lernen? – EdgarVerona

+0

Es muss einige gute Ressourcen irgendwo auf diesem ... Ich habe nur Aber ich finde es nicht: Entweder schaue ich nicht an der richtigen Stelle in den Castle Docs, oder es fehlen diese Informationen ... und ich kann keine gute externe Referenz finden, die mich auf solche Dinge hinweist . = ( – EdgarVerona

+0

Aye, das war es nicht. Ich habe alle Autoproperties der Sammlung im Programm durch explizite Eigenschaften ersetzt, und es machte keinen Unterschied ... es löst immer noch das OnFlushDirty-Ereignis für die unveränderten Kinder mit previousState._value ist immer noch null. – EdgarVerona

1

Es kommt vor, dass in Hibernate Sie merge verwenden müssen(), um die „Lasten“ die previuos Daten des abgelösten Objekt zu machen Ruhezustand, in Castle Das Äquivalent ist die SaveCopy() -Methode.

Verwandte Themen