Dieser Artikel wurde maschinell übersetzt.
Datenzugriff
Erstellen einer Aufgabenanwendung auf dem Desktop mit NHibernate
Oren Eini
NHibernate ist ein Objekt-Relational Mapper (OR / M), tasked mit wodurch es so einfach, es ist zum Arbeiten mit Objekten im Speicher mit einer Datenbank zu arbeiten. Es ist eines der beliebtesten OR / M Frameworks für die Entwicklung von Microsoft .NET Framework. Aber die meisten Benutzer von NHibernate sind dadurch im Kontext von Webanwendungen, daher ist es relativ wenig Informationen über das Erstellen von NHibernate-Anwendungen auf dem Desktop.
Bei Verwendung von NHibernate in einer Webanwendung tendenziell ich die Sitzung pro Anforderung Formatvorlage verwenden, die viel Auswirkungen, die leicht hat zu übersehen sind. Ich kümmern nicht der Sitzung einen Verweis auf die geladenen Entitäten verwalten, da ich erwarte, dass die Sitzung sofort in Kürze aufgenommen wird. Ich habe keinen Fehlerbehandlung (vieles) kümmern, wie ich nur die aktuelle Anforderung und die zugeordnete Sitzung abbrechen können, wenn ein Fehler auftritt.
Die Gültigkeitsdauer der Sitzung ist klar definiert. Ich benötige keine anderen Sitzungen über alle Änderungen zu aktualisieren, die ich vorgenommen. Ich benötige keine Sorgen darüber halten eine lange Transaktion in der Datenbank oder sogar gedrückt halten, eine Verbindung über einen langen Zeitraum geöffnet werden, da Sie für die Dauer einer Anforderung nur aktiv sind.
Wie Sie sich vorstellen können, sind die Probleme in einer Desktopanwendung. Um darüber klar sein, bin einer Anwendung, die Kommunikation mit einer Datenbank direkt Rede. Wird eine Anwendung, die eine Art von Remotediensten verwendet NHibernate auf dem Remoteserver unter dem Szenario pro Anforderung und nicht den Schwerpunkt dieses Artikels. Dieser Artikel behandelt keine zeitweise verbundene Szenarien, obwohl ein Großteil der Diskussion hier für diese Szenarios gelten würde.
Erstellen einer desktop NHibernate-basierte Anwendung ist nicht viel anders als Erstellen einer Desktopanwendung mithilfe einer beliebigen anderen Persistenz-Technologie. Viele der Herausforderungen, die ich in diesem Artikel gliedern möchten, werden alle Datenzugriff Technologien gemeinsam:
- Verwalten den Bereich der Arbeitseinheiten.
- Verringern die Dauer der geöffneten Datenbankverbindungen.
- Weitergeben von Entität in alle Teile der Anwendung geändert.
- Bidirektionale Datenbindung unterstützen.
- Reduzieren die Startzeiten.
- Blockieren des UI-Threads während des Zugriffs auf die Datenbank zu vermeiden.
- Behandeln und Lösen von Parallelitätskonflikten.
Während die Lösungen, die ich Ihnen geben ausschließlich mit NHibernate, mindestens die Mehrzahl der arbeiten können Sie auch auf andere Technologien Datenzugriff angewendet werden. Eine solche Herausforderung freigegeben durch alle Datenzugriff-Technologien, die ich kennen, wie der Umfang der Anwendung Arbeitseinheit verwaltet wird – oder des NHibernate ausgedrückt, die Lebensdauer von Sitzungen.
Verwalten von Sitzungen
Ungültige üblich mit NHibernate Desktopanwendungen ist eine einzelne globale Sitzung für die gesamte Anwendung. Es ist ein Problem für viele Gründe, jedoch drei von Ihnen am wichtigsten sind. Da eine Sitzung einen Verweis auf Alles, die es geladen speichert, soll der Benutzer auf die Anwendung arbeiten eine Entität zu laden, damit arbeiten ein wenig und vergessen Sie dabei. Aber da die einzelne globale Sitzung einen Verweis darauf beibehalten wird, wird die Entität nie freigegeben. Im Wesentlichen müssen Sie einen Speicherverlust in der Anwendung.
Besteht das Problem der Fehlerbehandlung. Wenn Sie eine Ausnahme (z. B. StaleObjectStateException aufgrund eines Parallelitätskonflikts) erhalten, sind Ihre Sitzung und seine geladenen Entitäten Toast, da mit NHibernate, von einer Sitzung ausgelöste Ausnahme dieser Sitzung in einem nicht definierten Zustand verschoben wird. Sie können dieser Sitzung oder alle geladenen Entitäten nicht mehr verwenden. Wenn Sie nur eine einzige globale Sitzung verfügen, bedeutet dies, dass wahrscheinlich müssen Sie die Anwendung neu starten, die wodurch die wahrscheinlich keine gute Idee ist.
Schließlich ist das Problem der Transaktion und Verbindung Fehlerbehandlung. Öffnen einer Sitzung nicht mit dem auf eine Datenbankverbindung zu öffnen, andererseits mit eine einzige Sitzung, dass Sie sehr viel wahrscheinlicher, Transaktionen zu halten und Verbindung länger als Sie sollte.
Eine andere gleichermaßen schlecht und, leider fast als gemeinsamen üben mit NHibernate Mikroverwaltung die Sitzung ist. Ein typisches Beispiel ist Code wie folgt:
public void Save(ToDoAction action) {
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
session.SaveOrUpdate(action);
tx.Commit();
}
}
Das Problem durch diese Art von Code ist, dass es viele der Vorteile, die Sie bei der Verwendung von NHibernate entfernt. NHibernate tut ziemlich etwas Arbeit, Änderungsverwaltung und transparente Dauerhaftigkeit für Sie zu behandeln. Mikroverwaltung die Sitzung Deaktivieren des NHibernate dazu ausgeschnitten und verschiebt die Onus der dadurch, die für Sie arbeiten. Und das ist auch ohne die Probleme, die Sie mit der Zeile nach unten verzögertes Laden verursacht erwähnen. In fast jedem System, in denen ich ein solcher Ansatz hat versucht gesehen haben, mussten die Entwickler härter arbeiten, um diese Probleme zu beheben.
Eine Sitzung sollte nicht zu lange geöffnet bleibt werden, aber es sollte auch nicht frei für eine Zeit damit zu kurz geöffnet, die Funktionen des NHibernate verwenden. Im Allgemeinen versuchen Sie, die Lebensdauer von Sitzungen auf die tatsächlichen Aktion entsprechen, die vom System durchgeführt wird.
Die empfohlene Vorgehensweise für Desktopanwendungen ist die Verwendung eine Sitzung pro Formular, so dass jedes Formular in der Anwendung eine eigene Sitzung hat. Jedes Formular stellt in der Regel eine unterschiedliche Arbeitskomponente, die der Benutzer ausführen möchten, damit übereinstimmenden Sitzungsdauer an die Lebensdauer Formular in der Praxis recht gut funktioniert. Der zusätzliche Vorteil besteht darin, dass Sie nicht mehr ein Problem mit Speicherverlusten, da beim Schließen eines Formulars in der Anwendung Sie auch die Sitzung verkaufen. Dadurch würden alle Entitäten, die von der Sitzung für die Wiedergewinnung vom Garbage Collector (GC) geladen wurden.
Es gibt weitere Gründe für eine Sitzung pro Formular bevorzugen. Sie können der NHibernate des Änderungsverfolgung, nutzen so, dass Sie alle Änderungen in die Datenbank übertragen wird, wenn Sie die Transaktion ein Commit ausgeführt. Außerdem erstellt Sie eine Isolierung Barriere zwischen verschiedenen Formularen, damit Sie auf eine einzelne Entität Änderungen können ohne Änderungen an anderen Entitäten, die auf anderen Formularen angezeigt werden, kümmern.
Während diese Art der Verwaltung der Lebensdauer von Sitzungen als eine Sitzung pro Formular beschrieben wird, verwalten in der Praxis Sie in der Regel die Sitzung pro Vortragenden. Der Code in Abbildung 1 stammt aus der Presenter-Basisklasse.
Abbildung 1 Presenter Sitzungsverwaltung
protected ISession Session {
get {
if (session == null)
session = sessionFactory.OpenSession();
return session;
}
}
protected IStatelessSession StatelessSession {
get {
if (statelessSession == null)
statelessSession = sessionFactory.OpenStatelessSession();
return statelessSession;
}
}
public virtual void Dispose() {
if (session != null)
session.Dispose();
if (statelessSession != null)
statelessSession.Dispose();
}
Wie Sie sehen können, ich verzögert Öffnen einer Sitzung (oder eine statusfreie Sitzung) und lassen es geöffnet, bis ich den Vortragenden verkaufen. Dieser Stil entspricht ziemlich gut an die Lebensdauer des Formulars selbst und kann ich jeden Vortragender eine separate Sitzung zugeordnet.
Verwalten von Verbindungen
In der Vortragende müssen Sie sich keine Gedanken darüber öffnen oder Schließen der Sitzung nicht. Beim ersten Zugriff, dass Sie eine Sitzung es für Sie geöffnet ist, und es wird selbst sowie ordnungsgemäß dispose. Aber was ist die Datenbankverbindung der Sitzung zugeordnet? Sind Sie hält eine Datenbankverbindung für geöffnet, solange der Benutzer das Formular angezeigt wird?
Die meisten Datenbanken dislike eine Transaktion für längere Zeiträume geöffnet halten zu müssen. Es führt i. d. r. verursacht Fehler oder Deadlocks, um die Zeile nach unten. Geöffnete Verbindungen können ähnliche Probleme verursachen, da eine Datenbank nur so viele Verbindungen akzeptieren kann, bevor der Ressourcen, behandeln die Verbindung nicht mehr ausreicht.
Um die Leistung des Datenbankservers zu maximieren, bewahren Sie Transaktion Lebensdauer einer minimalen auch so bald wie möglich Schließen Verbindungen auf Verbindungs-pooling um schnelle Antwortzeiten sicherzustellen, dass beim Öffnen einer neuen Verbindung verlassen.
Mit NHibernate ist die Situation ähnlich, außer dass NHibernate mehrere Features enthält, die zur explizit zur Erleichterung vorhanden sind. Die Sitzung NHibernate keine Eins-zu-Eins-Zuordnung mit einer Datenbankverbindung. NHibernate stattdessen verwaltet die Datenbankverbindung intern, öffnen und schließen es nach Bedarf. Das bedeutet, dass Sie nicht um eine Art von Zustand in der Anwendung trennen und erneut eine Verbindung zum Herstellen der Datenbank nach Bedarf zu verwalten. Standardmäßig werden NHibernate im größtmöglichen Umfang die Dauer Minimieren in der eine Verbindung geöffnet wird.
Sie müssen sich keine Gedanken darüber machen Transaktionen so klein wie möglich. Insbesondere ist eines der Dinge, die Sie nicht möchten eine Transaktion für die Lebensdauer des Formulars geöffnet halten. Dies zwingt NHibernate, um die Verbindung für die Dauer der Transaktion offen zu halten. Und da die Lebensdauer eines Formulars in menschliche Antwortzeiten gemessen wird, mehr als wahrscheinlich, dass Sie einen Transaktion und eine Verbindung für längere Zeit landen würde als wirklich fehlerfrei ist gedrückt halten.
Sie jedoch in der Regel werden offenen separaten Transaktionen für jede Operation, die Sie vornehmen. Betrachten Sie ein Formular Nachbildung, die eine einfache Aufgabenliste der Beispielanwendung Wahl zeigt Let’s (siehe Abbildung 2 ). Der Code für die Behandlung dieses Formulars ist recht einfach, wie Sie sehen können, im Abbildung 3 .
Abbildung 2 A Aufgabenleiste List Application
Abbildung 3 erstellen die Aufgabenleiste Forms
public void OnLoaded() {
LoadPage(0);
}
public void OnMoveNext() {
LoadPage(CurrentPage + 1);
}
public void OnMovePrev() {
LoadPage(CurrentPage - 1);
}
private void LoadPage(int page) {
using (var tx = StatelessSession.BeginTransaction()) {
var actions = StatelessSession.CreateCriteria<ToDoAction>()
.SetFirstResult(page * PageSize)
.SetMaxResults(PageSize)
.List<ToDoAction>();
var total = StatelessSession.CreateCriteria<ToDoAction>()
.SetProjection(Projections.RowCount())
.UniqueResult<int>();
this.NumberOfPages.Value = total / PageSize +
(total % PageSize == 0 ? 0 : 1);
this.Model = new Model {
Actions = new ObservableCollection<ToDoAction>(actions),
NumberOfPages = NumberOfPages,
CurrentPage = CurrentPage + 1
};
this.CurrentPage.Value = page;
tx.Commit();
}
}
Ich habe drei Operationen: Laden das Formular zum ersten Mal, dem die erste Seite angezeigt wird, die und hin-und paging durch die Datensätze.
Für jeden Arbeitsgang ich und-Durchführung eine separate Transaktion. Auf diese Weise, die ich keine Ressourcen in der Datenbank beanspruchen und müssen sich keine Sorgen machen über lange Transaktionen. NHibernate wird automatisch eine Verbindung zur Datenbank geöffnet, wenn ich eine neue Transaktion beginnen, und schließen Sie es nach dem Abschluss der Transaktion ist.
Statusfreie Sitzungen
Es ist ein weiteres kleines Besonderheit, die ich beachten sollten: Ich verwende kein ISession. Vielmehr verwende ich IStatelessSession an seiner Stelle, um die Daten zu laden. Statusfreie Sitzungen werden in Massen Datenbearbeitung in der Regel verwendet, aber ich bin in diesem Fall ausführenden Verwendung einer statusfreien Sitzung zur Behebung von Speicherbelegungsproblemen.
Eine statusfreie Sitzung ist, nun, statusfreie. Im Gegensatz zu einer normalen Sitzung verwalten nicht aber einen Verweis auf die Entitäten, die es lädt. Als solche ist es durchaus geeignet, beim Laden der Entitäten für Zwecke nur angezeigt. Für diese Art von Aufgabe Sie in der Regel die Entitäten aus der Datenbank geladen, Sie auf das Formular auslösen und vergessen über diese. Eine statusfreie Sitzung ist nur in diesem Fall benötigen Sie.
Doch statusfreie Sitzungen mit einer Reihe von Einschränkungen. Leiter/in zwischen Ihnen in diesem Fall ist, dass statusfreie Sitzungen keine für verzögertes Laden Unterstützung bieten, selbst im normalen Modus NHibernate Ereignis nicht beteiligt und nehmen Sie keine von NHibernate Verwenden des Features Zwischenspeichern.
Für dieser Gründe verwende ich in der Regel diese für einfache Abfragen, in denen ich möchte nur dem Benutzer die Informationen anzeigen, ohne etwas kompliziert. In Fällen, in denen ich eine Entität für die Bearbeitung anzeigen möchten, konnte ich immer noch, einer statusfreien Sitzung verwenden, aber ich häufig, bevorzugt anstelle einer normalen Sitzung zu vermeiden.
Im Formular Hauptanwendung ich erreicht werden soll, damit die Daten nur angezeigt, und versuchen Sie, vornehmen der statusfreien Sitzungen allein verwenden. Das Hauptformular lebt für, solange die Anwendung geöffnet ist und eine statusbehaftete Sitzung soll ein Problem nicht nur, weil es einen Verweis auf den Entitäten beibehalten wird, die es geladen, aber da es wahrscheinlich zu Problemen führen, wenn die Sitzung eine Ausnahme auslöst.
NHibernate betrachtet eine Sitzung, die ausgelöst eine Ausnahme in einem nicht definierten Zustand hat (nur Dispose in diesem Fall ein definiertes Verhalten aufweist). Sie müssen die Sitzung ersetzen, und einen Verweis darauf beibehalten, da Entitäten durch eine statusbehaftete Sitzung geladen, müssten Sie alle Entitäten zu deaktivieren, die von der jetzt außer Kraft gesetzte Sitzung geladen wurden. Es ist viel einfacher, eine statusfreie Sitzung in diesem Fall verwenden.
Geladen von statusfreien Sitzungen Entitäten nicht für den Status der Sitzung wichtig sind, und Wiederherstellung ein Fehler in einer statusfreien Sitzung ist ebenso einfach wie die aktuelle Sitzung statusfreie schließen und öffnen eine neue.
Bearbeiten von Daten
Abbildung 4 zeigt die Nachbildung bearbeiten Bildschirm. Welche Herausforderungen werden Sie konfrontiert, wenn Sie zum Bearbeiten von Entitäten benötigen?
Abbildung 4 Bearbeiten von Entitäten
Nun, haben Sie tatsächlich zwei separate Herausforderungen hier. Erstens soll NHibernate verwenden können des Änderungsnachverfolgung, so dass Sie eine Entität (oder ein Entität-Objektdiagramm) anzuzeigen und müssen lediglich NHibernate können bleiben es, wenn Sie fertig sind. Nachdem Sie eine Entität gespeichert haben, möchten Sie Zweitens stellen Sie sicher, dass jedes Formular, das diese Entität auch zeigt mit den neuen Werten aktualisiert.
Das erste Element des Unternehmens ist tatsächlich ziemlich einfach zu behandeln. Dazu müssen Sie lediglich stellen Verwendung der Sitzung mit dem Formular verknüpft sind, und das ist alles. Abbildung 5 zeigt den Code, die diesem Bildschirm gesteuert.
Abbildung 5 Bearbeiten einer Entität in der Sitzung
public void Initialize(long id) {
ToDoAction action;
using (var tx = Session.BeginTransaction()) {
action = Session.Get<ToDoAction>(id);
tx.Commit();
}
if(action == null)
throw new InvalidOperationException(
"Action " + id + " does not exists");
this.Model = new Model {
Action = action
};
}
public void OnSave() {
using (var tx = Session.BeginTransaction()) {
// this isn't strictly necessary, NHibernate will
// automatically do it for us, but it make things
// more explicit
Session.Update(Model.Action);
tx.Commit();
}
EventPublisher.Publish(new ActionUpdated {
Id = Model.Action.Id
}, this);
View.Close();
}
Sie erhalten die Entität aus der Datenbank in der Initialize(id)-Methode, und aktualisieren Sie es in der OnSave-Methode. Beachten Sie, dass in zwei separaten Transaktionen, anstatt eine Transaktion für einen längeren Zeitraum aktiv halten möchten. Es ist auch dieser merkwürdige EventPublisher-Aufruf. Was ist das alles?
EventPublisher ist hier, um eine weitere Herausforderung behandeln: Wenn jedes Formular seine Sitzung besitzt, hat jedes Formular unterschiedliche instances der Arbeit mit Entitäten. Auf der Oberfläche der, sieht, wie eine Verschwendung. Warum sollten Sie die gleichen Aktion mehrmals werden geladen?
In tatsächlich wird diese Trennung zwischen den Formularen müssen die Anwendung erheblich vereinfacht. Beachten Sie, was geschieht, wenn Sie die Entitätsinstanzen der über das gesamte freigegeben. In dieser Situation würde sich selbst bei einem Problem finden Sie in jedem Szenario denkbar bearbeiten. Beachten Sie, was geschieht, wenn Sie eine Entität in zwei Formen anzuzeigen, die diese Entität Bearbeiten zulassen. Eine bearbeitbare Raster und detaillierte in einem Bearbeitungsformular, z. B. sein können. Wenn Sie die Entität im Raster ändern, öffnen Sie das Bearbeitungsformular detaillierte und dann speichern, was der Änderung geschieht im Raster bearbeitet werden vorgenommenen?
Wenn Sie eine einzelne Entitätsinstanz in der gesamten Anwendung einsetzen, ist es wahrscheinlich, das Details-Formular speichern Sie die Änderungen, die mit dem Raster auch verursachen würde. Dies ist wahrscheinlich nicht etwas, das Sie tun möchten. Freigeben einer Entitätsinstanz erleichtert auch viel schwieriger, Aktionen, wie ein Bearbeitungsformular abbrechen und alle nicht gespeicherten Änderungen sofort zu wechseln.
Diese Probleme sind einfach nicht vorhanden, wenn Sie eine Entitätsinstanz pro Formular, der eine gute Sache, wie dies mehr oder weniger obligatorisch, ist Wenn Sie eine Sitzung pro Formular Ansatz verwenden.
Veröffentlichen von Ereignissen
Aber ich noch nicht vollständig den Zweck der EventPublisher noch erläutert. Es ist tatsächlich ziemlich einfach. Anstatt eine einzelne Instanz der Entität in der Anwendung zu verwenden, Sie müssen möglicherweise viele, aber der Benutzer weiterhin möchten finden Sie in die Entität (einmal richtig gespeichert), die auf alle Formulare, die dieser Entität anzeigen aktualisiert.
In meinem Beispiel dazu ich explizit. Jedes Mal, wenn ich eine Entität speichern, veröffentlichen Sie ein Ereignis besagt, dass ich also und auf welche Entität hat. Dies ist ein Standardereignis .NET nicht. Ein Ereignis .NET erfordert eine Klasse, um es direkt zu abonnieren. Die nicht tatsächlich für diese Art der Benachrichtigung verwendet werden, da es jedes Formular, um auf Ereignisse in allen anderen Formularen zu registrieren erforderlich wäre. Nur ein Versuch, zu verwalten, wäre ein Albtraum.
Die EventPublisher ist eine Veröffentlichung - Mechanismus, mit denen ich einen Herausgeber seine Abonnenten entkoppeln abonnieren. Die einzige Gemeinsamkeit zwischen Ihnen ist die EventPublisher-Klasse. Ich verwende den Ereignistyp (ActionUpdated im Abbildung 5 ), zu entscheiden, wer über das Ereignis zu informieren.
Let’s sehen Sie sich die Gegenseite, jetzt. Wenn ich eine Aufgabe Aktion aktualisieren, möchte ich die aktualisierten Werte in das Hauptformular anzeigen, die ein Raster von Aktionen der Aufgabe anzeigt. Hier ist der relevante Code aus diesem Formular Vortragenden:
public Presenter() {
EventPublisher.Register<ActionUpdated>(
RefreshCurrentPage);
}
private void RefreshCurrentPage(
ActionUpdated actionUpdated) {
LoadPage(CurrentPage);
}
Beim Starten registrieren Sie ich die RefreshCurrentPage-Methode, um das ActionUpdated-Ereignis. Jetzt, sobald dieses Ereignis ausgelöst wird, wird ich einfach die aktuelle Seite vom aufrufenden LoadPage aktualisieren, die Sie bereits kennen.
Dies ist tatsächlich ziemlich verzögerte Implementierung. Ich nicht wichtig, wenn die aktuelle Seite die bearbeitete Entität angezeigt wird; ich einfach aktualisieren es trotzdem. Eine komplexere (und effiziente) Implementierung würde nur die Rasterdaten aktualisieren, wenn die aktualisierte Entität auf dieser Seite angezeigt wird.
Der Hauptvorteil der Verwendung von dem Prinzip des Publizierens-abonnieren Mechanismus in dieser Weise ist der Verleger und Abonnenten entkoppeln. Ich sorgt nicht im Hauptformular, dass mit dem Bearbeitungsformular das ActionUpdated-Ereignis veröffentlicht. Die Idee des Veröffentlichens Ereignis und Veröffentlichen Abonnieren ist ein Eckpfeiler beim Erstellen von lose Benutzeroberflächen gekoppelt und wird ausführlich in der Microsoft Patterns & Practices-Team in den Composite Application Guidance (msdn.microsoft.com/library/cc707819 ) behandelt.
Es ist ein weiterer Fall sollten: Was würde passieren, wenn Sie zwei bearbeiten Formulare die gleiche Entität zur selben Zeit geöffnet haben? Wie können Sie abgerufen die neuen Werte aus der Datenbank und an den Benutzer anzeigen?
Der folgende Code stammt vom Presenter Formular bearbeiten:
public Presenter() {
EventPublisher.Register<ActionUpdated>(RefreshAction);
}
private void RefreshAction(ActionUpdated actionUpdated) {
if(actionUpdated.Id != Model.Action.Id)
return;
Session.Refresh(Model.Action);
}
Dieser Code für das ActionUpdated-Ereignis registriert, und wenn Sie die Entität, die Sie gerade bearbeiten, bitten Sie NHibernate, aus der Datenbank zu aktualisieren.
Dieses explizite Modell der Aktualisierung der Entität aus der Datenbank gibt Ihnen außerdem die Möglichkeit, Entscheidungen über was jetzt geschehen soll. Sollten Sie automatisch aktualisieren, löschen alle Benutzeränderungen? Werden der Benutzer gefragt? Versuchen Sie es, um die Änderungen im Hintergrund? Dies sind alle Entscheidungen haben Sie jetzt die Möglichkeit, auf einfache Weise behandeln.
In den meisten Fällen finde allerdings ich, dass die Entität einfach aktualisieren durchaus ausreichend,, ist da Sie in der Regel nicht zulassen einer einzelnen Entität parallel (zumindest nicht von einem einzelnen Benutzer) aktualisieren.
Während dieser Entität aktualisieren Code tatsächlich die Werte für die Entitätsinstanz aktualisiert wird, wie wird die UI-Antworten an diese Änderung vornehmen? Sie haben die Entität Werte an die Formularfelder gebunden, aber Sie müssen einige mitteilen, der Benutzeroberfläche, die diese Werte geändert haben.
Microsoft .NET Framework bietet die INotifyPropertyChanged-Schnittstelle, welche den meisten UI-Frameworks zu verstehen und wissen, wie mit arbeiten. Hier ist die INotifyPropertyChanged-Definition:
public delegate void PropertyChangedEventHandler(
object sender, PropertyChangedEventArgs e);
public class PropertyChangedEventArgs : EventArgs {
public PropertyChangedEventArgs(string propertyName);
public virtual string PropertyName { get; }
}
public interface INotifyPropertyChanged {
event PropertyChangedEventHandler PropertyChanged;
}
Ein Objekt, das diese Schnittstelle implementiert, sollte das PropertyChanged-Ereignis mit dem Namen der Eigenschaft auslösen, das geändert wurde. Die Benutzeroberfläche wird das PropertyChanged-Ereignis abonnieren, und sobald eine Änderung an einer Eigenschaft ausgelöst wird, das gebunden ist, aktualisieren Sie die Bindung.
Diese Implementierung ist recht einfach:
public class Action : INotifyPropertyChanged {
private string title;
public virtual string Title {
get { return title; }
set {
title = value;
PropertyChanged(this,
new PropertyChangedEventArgs("Title"));
}
}
public event PropertyChangedEventHandler
PropertyChanged = delegate { };
}
Beim einfachen es ist ziemlich sich wiederholende Code und ist nur erforderlich, um die Benutzeroberfläche Infrastruktur Bedenken zu erfüllen.
Abfangen von Entität erstellen
Ich möchte Code schreiben, nur um die Benutzeroberfläche Datenbindung ordnungsgemäß erhalten haben. Und wie sich herausstellt, tatsächlich müssen nicht.
Eine der Anforderungen von NHibernate ist, dass Sie alle Eigenschaften und Methoden auf Ihre Klassen virtuellen vornehmen. NHibernate erfordert dies verzögertes Laden Bedenken ordnungsgemäß verarbeiten, aber Sie profitieren von dieser Anforderung auch aus anderen Gründen.
Was Sie tun können ist dem virtual-Schlüsselwort eigene Verhalten in der Mischung einzuschleusen nutzen. Hierzu mithilfe eines Verfahrens Aspect-Oriented Programmierung (AOP) aufgerufen. Im Wesentlichen eine Klasse in Anspruch nehmen und fügen Sie zusätzliche Verhaltensweisen zu dieser Klasse zur Laufzeit hinzu. Der genaue Mechanismus, wie Sie dies implementieren, wird außerhalb des Gültigkeitsbereichs des Artikels, aber es wird gekapselt, in der DataBindingFactory-Klasse, deren Definition ist:
public static class DataBindingFactory {
public static T Create<T>();
public static object Create(Type type);
}
Die gesamte Implementierung der Klasse ist ca. 40 Zeilen nicht allzu komplex. Funktion ist einen Typ in Anspruch nehmen und erzeugen eine Instanz dieses Typs, die ebenfalls vollständig den INotifyPropertyChanged-Vertrag implementiert. Anders ausgedrückt, funktioniert der folgende Test:
ToDoAction action = DataBindingFactory.Create<ToDoAction>();
string changedProp = null;
((INotifyPropertyChanged)action).PropertyChanged
+= (sender, args) => changedProp = args.PropertyName;
action.Title = "new val";
Assert.Equal("Title", changedProp);
Angesichts, ist müssen Sie nun jedes Mal, wenn Sie eine neue Klasse in die Vortragenden Erstellen der DataBindingFactory nutzen. Der wichtigste Vorteil, die Sie über ein solches System ist, nun, wenn Sie, stellen möchten des Domänenmodells NHibernate in einem Kontext nicht Präsentation verwenden, Sie können einfach nicht nutzen die DataBindingFactory, und Sie erhalten ein Domänenmodell völlig frei von Präsentation bedenken.
Es liegt weiterhin ein Problem, jedoch. Während Sie erstellen Neuer Instanzen von Entitäten können müssen mithilfe der DataBindingFactory ein Großteil der Zeit Sie für den Umgang mit Instanzen, die von NHibernate erstellt wurden. Natürlich NHibernate weiß nichts über Ihre DataBindingFactory und kann davon verwenden. Doch bevor Sie zunächst, können Sie eines der nützlichsten Erweiterungspunkte mit NHibernate, das Interceptors verwenden. NHibernate des Interceptors ermöglicht es Ihnen, im Wesentlichen, einige der Funktionen übernehmen, die die NHibernate intern durchführt.
Eine der Funktionen, die die Interceptors gegenüber ermöglicht ist das neue Instanzen von Entitäten erstellen. Abbildung 6 zeigt ein Interceptors, durch die Instanzen der Entitäten, die mit der DataBindingFactory erstellt.
Abbildung 6 abfangen Entity Creation
public class DataBindingInterceptor : EmptyInterceptor {
public ISessionFactory SessionFactory { set; get; }
public override object Instantiate(string clazz,
EntityMode entityMode, object id) {
if(entityMode == EntityMode.Poco) {
Type type = Type.GetType(clazz);
if (type != null) {
var instance = DataBindingFactory.Create(type);
SessionFactory.GetClassMetadata(clazz)
.SetIdentifier(instance, id, entityMode);
return instance;
}
}
return base.Instantiate(clazz, entityMode, id);
}
public override string GetEntityName(object entity) {
var markerInterface = entity as
DataBindingFactory.IMarkerInterface;
if (markerInterface != null)
return markerInterface.TypeName;
return base.GetEntityName(entity);
}
}
Sie überschreiben die Methode Instantiate und verarbeiten den Fall, wo wir eine Entität mit einem Typ erhalten, die Sie erkennen. Sie fortfahren, dann erstellen Sie eine Instanz der Klasse und Ihre ID-Eigenschaft festlegen. Sie müssen auch NHibernate zu unterrichten, wie Sie verstehen, welche Art eine Instanz über DataBindingFactory erstellt, gehört, Sie in der GetEntityName-Methode, der die Intercepter.
Das einzige, was nun links besteht darin, mit der neuen Interceptors NHibernate einzurichten. Der folgende Code stammt aus der BootStrapper-Klasse, für das Einrichten der Anwendung verantwortlich:
public static void Initialize() {
Configuration = LoadConfigurationFromFile();
if(Configuration == null) {
Configuration = new Configuration()
.Configure("hibernate.cfg.xml");
SaveConfigurationToFile(Configuration);
}
var intercepter = new DataBindingIntercepter();
SessionFactory = Configuration
.SetInterceptor(intercepter)
.BuildSessionFactory();
intercepter.SessionFactory = SessionFactory;
}
Ignorieren Sie vorläufig die Semantik Konfiguration – ich beziehen, die in einem Bit. Der entscheidende Punkt ist, dass Sie die Interceptors erstellen, setzen Sie ihn von der Konfiguration und die Sitzung Factory erstellen. Die Sitzung Factory ist das letzte Schritt auf die Interceptors festlegen. Es ist ein wenig umständlich, werde ich zugeben, aber das ist die einfachste Möglichkeit zum Abrufen der entsprechenden Sitzung Factory in der Interceptors.
Sobald die Interceptors verkabelt ist, unterstützt jede Entitätsinstanz, die NHibernate erstellt jetzt INotifyPropertyChanged Benachrichtigungen ohne dass Sie überhaupt keine Arbeit führen. Ich Sehe als dies ziemlich eine elegante Lösung für das Problem.
Es sind einige, die sagen würde, dass eine solche Lösung auswählen ein Problem im Hinblick auf die Leistung ist über die harte Codierung der Implementierung. In der Praxis erweist, die sich eine false Annahme. Das Tool, dass (Castle dynamische Proxy) zum Ausführen dieser on-the-Fly Erweiterung von Klassen verwendet wird wurde stark optimiert, um eine optimale Leistung sicherzustellen.
Adressierung Leistung
Sprechen Leistung, spielt eine zusätzliche in Desktopanwendungen, die nicht in Webanwendungen verfügen Startzeit. In Webanwendungen ist es häufig zu entscheiden, längere Startzeiten zur Leistungssteigerung der Anforderung zu bevorzugen. In Desktopanwendungen möchten Sie die Startzeit so weit wie möglich zu verringern. Tatsächlich ist eine allgemeine betrügen mit desktop-Anwendung einfach ein Bildschirmabbild der Anwendung dem Benutzer angezeigt, bis die Anwendung beendet, gestartet.
Leider ist NHibernate Startzeit etwas lang. Dies ist vor allem da NHibernate viel Initialisierung durchführen ist und überprüft beim Start, so dass es während des normalen Betriebs schneller ausführen kann. Es gibt zwei gängige Methoden zur Behandlung dieses Problems.
Die erste besteht darin, NHibernate in einem Hintergrundthread zu starten. Während dies bedeutet, die Benutzeroberfläche viel schneller angezeigt wird dass, erstellt er auch eine Komplikation for die Applikation selbst, da Sie dem Benutzer nicht alle Elemente aus der Datenbank anzeigen können bis Sie den Factory beim Start von Sitzung abgeschlossen haben.
Die andere Option ist zum Serialisieren des NHibernate Configuration-Klasse. Eine große Menge an die Kosten im Zusammenhang mit Start NHibernate bezieht sich auf die Kosten für die Überprüfung der Konfiguration Klasse übergebenen Informationen. Die Konfiguration-Klasse ist eine serialisierbare Klasse und daher Sie können Zahlen dieser Preis nur einmal nach dem können Sie die Kosten für die Verknüpfung durch eine bereits überprüfte Instanz aus dem permanenten Speicher zu laden.
Das ist der Zweck der LoadConfigurationFromFile und SaveConfigurationToFile, serialisieren und Deserialisieren des NHibernate-Konfiguration. Mit diesen, die Sie nur die Uhrzeit der ersten Konfiguration erstellen müssen, starten Sie die Anwendung. Es gibt jedoch ein kleiner Haken, dem Sie sich bewusst sein sollten: Sie sollten die zwischengespeicherte Konfiguration ungültig, wenn die Entitäten Assembly oder die Konfigurationsdatei NHibernate geändert hat.
Der Beispielcode für diesen Artikel enthält eine vollständige Implementierung, ‘ s dies berücksichtigen und erklärt die zwischengespeicherte Datei ungültig, wenn die Entitäten oder die Konfiguration geändert haben.
Es ist ein weiteres Leistungsproblem der, die Sie für den Umgang mit. Aufrufen der Datenbank ist eine der teureren Operationen, die die Anwendung macht. Daher ist es nicht etwas, das Sie würden auf dem Benutzeroberflächenthread Anwendung tun möchten.
Solche Aufgaben werden häufig um einen Hintergrundthread verwendet, und Sie können dasselbe mit NHibernate, aber Beachten Sie, dass die NHibernate-Sitzung nicht threadsicher ist. Während Sie vornehmen können einer Sitzung in mehreren Threads (hat also keine Threadaffinität) verwenden, müssen Sie nicht verwenden eine Sitzung (oder Ihre Entitäten) auf mehreren Threads parallel. Mit anderen Worten, es ist durchaus in Ordnung, wenn Sie die Sitzung in einem Hintergrundthread zu verwenden, aber Sie müssen Zugriff auf die Sitzung zu serialisieren und gleichzeitigen Zugriff darauf nicht zulassen. Verwenden die Sitzung von mehreren Threads parallel führt zu nicht definiertem Verhalten; das heißt, es sollte vermieden werden.
Glücklicherweise sind ein paar relativ einfache Maßnahmen, die Sie ergreifen können, um sicherzustellen, dass der Zugriff auf die Sitzung serialisiert wird. Die System.ComponentModel.BackgroundWorker-Klasse wurde explizit entwickelt, um diese Art von Aufgaben zu behandeln. Sie können eine Aufgabe in einem Hintergrundthread ausführen und benachrichtigt Sie, wenn er abgeschlossen ist, dauert sorgt für die Synchronisationsproblem UI Thread, die in Desktopanwendungen so wichtig ist.
Sie haben weiter oben gesehen wie Bearbeiten einer vorhandenen Entität, die ich getan habe, direkt auf dem Benutzeroberflächenthread verwaltet. Jetzt, eine neue Entität let’s in einem Hintergrundthread zu speichern. Der folgende Code ist die Initialisierung des Präsentators neu erstellen:
private readonly BackgroundWorker saveBackgroundWorker;
public Presenter() {
saveBackgroundWorker = new BackgroundWorker();
saveBackgroundWorker.DoWork +=
(sender, args) => PerformActualSave();
saveBackgroundWorker.RunWorkerCompleted +=
(sender, args) => CompleteSave();
Model = new Model {
Action = DataBindingFactory.Create<ToDoAction>(),
AllowEditing = new Observable<bool>(true)
};
}
Die BackgroundWorker wird verwendet, um den aktuellen Speichervorgang, ausführen, die in zwei verschiedene Teile geteilt wurde. Abgesehen von diesen Teilung ähnelt sehr der Art und Weise, wie ich es in das Szenario bearbeiten behandelt. Eine andere interessante Bit, die besondere Aufmerksamkeit schenken müssen AllowEditing-Eigenschaft; diese Eigenschaft wird verwendet, um die Benutzeroberfläche im Formular sperren, wenn Sie einen Speichervorgang durchführen Vorgang. Auf diese Weise können Sie bedenkenlos verwenden die Sitzung in einem anderen Thread zu wissen, dass nicht gleichzeitiger Zugriff auf die Sitzung oder keines seiner Entität durch das Formular werden.
Der speichern-Prozess selbst sollten schon ziemlich vertraut sein. Let’s suchen der OnSave-Methode zuerst:
public void OnSave() {
Model.AllowEditing.Value = false;
saveBackgroundWorker.RunWorkerAsync();
}
Diese Methode ist verantwortlich für das Deaktivieren der Bearbeitung in das Formular und dann aus der Hintergrund-Prozess gestartet. Im Hintergrund führen Sie die tatsächliche speichern. Der Code sollte nicht als ein plötzliches stammen:
private void PerformActualSave() {
using(var tx = Session.BeginTransaction()) {
Model.Action.CreatedAt = DateTime.Now;
Session.Save(Model.Action);
tx.Commit();
}
}
Wenn die tatsächliche Speichern der Datenbank abgeschlossen ist, wird der BackgroundWorker CompleteSave Teil des Prozesses in der UI-Thread ausgeführt:
private void CompleteSave() {
Model.AllowEditing.Value = true;
EventPublisher.Publish(new ActionUpdated {
Id = Model.Action.Id
}, this);
View.Close();
}
Aktivieren Sie das Formular wieder, veröffentlichen Sie eine Benachrichtigung, die eine Aktion wurde aktualisiert, dass die relevanten Bildschirme ebenfalls aktualisiert wird, und schließlich das Formular zu schließen. Ich nehmen Sie an die Benutzeroberfläche aktivieren nicht unbedingt notwendig ist, dass ich es gibt es für Abschluss Willen enthalten.
Mit diesem Verfahren können Sie ohne den threading Vertrag für die Sitzung Instanzen verletzen Hintergrundverarbeitung nutzen. Wie immer threading ist eine hervorragende Möglichkeit, eine reaktionsfähigere Anwendung erstellen, aber Multithread-Programmierung ist keine Aufgabe leicht erreicht werden, was diese Technik mit Vorsicht.
Im Zusammenhang mit Parallelität
Parallelität ist bestenfalls an, wie oft ein komplexes Thema, und es ist nicht mit threading allein beschränkt. Betrachten Sie, auf dem Sie zwei Benutzer verfügen, die dieselbe Entität zur selben Zeit bearbeiten. Darauf soll zuerst, drücken Sie die Schaltfläche zum Absenden die Änderungen in der Datenbank speichern. Die Frage ist, was geschehen soll, wenn der zweite Benutzer den Speichervorgang Treffer Schaltfläche?
Dies ist einen Konflikt Parallelität bezeichnet und NHibernate hat einige Möglichkeiten zum Erkennen eines solchen Konflikts. Die ToDoAction-Entität besitzt ein < Version / > Feld, das NHibernate mitteilt, dass Sie explizit auf vollständige Parallelität Überprüfungen durchführen muss. Eine umfassende Erläuterung zu den Parallelitätsoptionen, die NHibernate bietet, finden Sie in meinem Blogbeitrag unter ayende.com/Blog/archive/2009/04/15/nhibernate-mapping-concurrency.aspx .
Im Wesentlichen, fallen die Parallelität Lösungen in zwei Hauptkategorien:
- Eingeschränkte Parallelitätssteuerung, die erforderlich ist, Sie halten Sperren auf die Datenbank und die Transaktion für einen längeren Zeitraum geöffnet lassen. Wie ich bereits erwähnt, ist dies nicht empfehlenswert in einer Desktopanwendung.
- Steuerung durch vollständige Parallelität, was bedeutet, dass Sie die Datenbankverbindung während des Benutzers schließen können “ Reaktionszeit. ” Die meisten Optionen, die NHibernate zu bieten hat, sind auf der optimistischen Seite, sodass mehrere Strategien, um Konflikte zu erkennen.
Da die Steuerung durch eingeschränkte Parallelität solche starker Leistungseinbußen führt, ist im Allgemeinen nicht akzeptabel. Dies wiederum bedeutet, dass Sie die Steuerung durch vollständige Parallelität bevorzugen sollte. Mit der vollständigen Parallelität Sie versuchen, die Daten normalerweise zu speichern, aber Sie sind vorbereitet, den Fall behandeln, in dem die Daten von einem anderen Benutzer geändert wurde.
NHibernate wird dies als eine StaleObjectStateException während des Speicherns von Manifesten oder Prozesse übernehmen. Anwendungen sollten diese Ausnahme abfangen und entsprechend Verhalten. In der Regel bedeutet dies, dass Sie müssen eine Art einer Nachricht für dem Benutzer anzeigen, erläutern, dass die Entität von einem anderen Benutzer bearbeitet wurde, und, dass der Benutzer benötigt, um Ihre Änderungen zu wiederholen. Gelegentlich müssen Sie zum Ausführen komplexer Vorgänge, z. B. anbieten, die Informationen zusammenzuführen, oder der Benutzer entscheiden, welche Version beibehalten.
Da die erste Option – eine Meldung anzuzeigen, und dass den Benutzer alle Änderungen wiederherstellen – viel häufiger, ich werde zeigen, wie die mit NHibernate implementieren, und besprechen Sie anschließend kurz andere Lösungen implementiert werden können.
Ein interessantes Problem auf, dem Sie sofort konfrontiert besteht, dass eine Ausnahme ausgelöst wird, von der Sitzung die Sitzung bedeutet kann nicht mehr verwendet werden. Konflikt mit beliebiger Parallelität wird in NHibernate als Ausnahme. Das einzige, was, das Sie ausführen können, um eine Sitzung, nachdem er eine Ausnahme ausgelöst hat, ist darauf Dispose aufrufen, eine anderen Operation führt zu nicht definiertem Verhalten.
Ich werde gehen Sie zurück zum Bildschirm bearbeiten und Implementieren von Parallelverarbeitung dort als Beispiel. Ich wird eine Schaltfläche erstellen Parallelität Konflikt auf dem Bildschirm bearbeiten hinzugefügt, dem der folgende Code ausgeführt wird:
public void OnCreateConcurrencyConflict() {
using(var session = SessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var anotherActionInstance =
session.Get<ToDoAction>(Model.Action.Id);
anotherActionInstance.Title =
anotherActionInstance.Title + " -";
tx.Commit();
}
MessageBox.Show("Concurrency conflict created");
}
Dies erstellt eine neue Sitzung und ändert die Title-Eigenschaft. Dadurch wird eine Parallelitätskonflikts ausgelöst, wenn ich versuche, die Entität im Formular zu speichern, da die Sitzung auf dem Formular diese Änderungen nicht berücksichtigt. Abbildung 7 dargestellt wie ich, Behandlung bin.
Ich gewrappt einfach den Code, der speichert in der Datenbank in einem Try-Catch-Block und behandelt die veralteten Zustand Ausnahme durch den Benutzer darüber informiert, dass ich eines Parallelitätskonflikts erkannt. Ich ersetzen dann die Sitzung.
Abbildung 7. Behandeln von Parallelität Konflikte
public void OnSave() {
bool successfulSave;
try {
using (var tx = Session.BeginTransaction()) {
Session.Update(Model.Action);
tx.Commit();
}
successfulSave = true;
}
catch (StaleObjectStateException) {
successfulSave = false;
MessageBox.Show(
@"Another user already edited the action before you had a chance to do so. The application will now reload the new data from the database, please retry your changes and save again.");
ReplaceSessionAfterError();
}
EventPublisher.Publish(new ActionUpdated {
Id = Model.Action.Id
}, this);
if (successfulSave)
View.Close();
}
Beachten Sie, dass ich immer Aufrufen von ActionUpdated, auch wenn ich eine Parallelitätskonflikts erhalten. Ist hier warum: Selbst wenn es eines Parallelitätskonflikts erschien, der Rest der Anwendung nicht wahrscheinlich darüber wissen, und die Entität wurde in der Datenbank geändert, so dass ich auch dem Rest der Anwendung Ihnen die Möglichkeit, die Benutzer auch die neuen Werte angezeigt geben könnte.
Schließlich ich nur das Formular schließen Wenn ich im Speichern der Datenbank erfolgreich erwiesen haben. Bisher, es gibt nichts viel, es ist jedoch die Sitzung und Entität Ersetzung, die ich immer noch in Betracht ziehen (siehe Abbildung 8 ).
Abbildung 8 aktualisieren, Sitzungen und Entitäten
protected void ReplaceSessionAfterError() {
if(session!=null) {
session.Dispose();
session = sessionFactory.OpenSession();
ReplaceEntitiesLoadedByFaultedSession();
}
if(statelessSession!=null) {
statelessSession.Dispose();
statelessSession = sessionFactory.OpenStatelessSession();
}
}
protected override void
ReplaceEntitiesLoadedByFaultedSession() {
Initialize(Model.Action.Id);
}
Wie Sie sehen können, ersetzen ich die Sitzung oder die statusfreie Sitzung, indem Sie disposing und neue zu öffnen. Im Fall von einer Sitzung bitten ich auch den Vortragenden, die Entitäten zu ersetzen, die durch die fehlende Sitzung geladen wurden. NHibernate Entitäten sind für Ihre Sitzung eng miteinander verknüpft, und wenn die Sitzung nicht mehr verwendet werden, es empfiehlt sich auch die Entitäten zu ersetzen. Es ist nicht erforderlich von – die Entitäten werden nicht plötzlich nicht mehr ausgeführt werden – aber Dinge wie verzögertes Laden nicht mehr funktionieren. Ich Bezahle würden stattdessen die Kosten des Ersetzens der Entitäten als versuchen herauszufinden, wenn ich können oder das Objektdiagramm in bestimmten Fällen zu durchlaufen.
Die Implementierung des Ersetzungsvorgangs Entität erfolgt durch die Initialize-Methode nur in diesem Fall aufrufen. Dies ist die gleiche Initialize-Methode, die ich im Fall Formular bearbeiten beschrieben. Diese Methode ruft die Entität aus der Datenbank einfach und wird in die Modell-Eigenschaft – aufregend nichts. In komplexeren Szenarien kann es mehrere Entitätsinstanzen der ersetzen, die in einem Formular verwendet werden.
Für diese Frage enthält der gleiche Ansatz nicht nur für Parallelitätskonflikte, jedoch für alle Fehler, die Sie aus der NHibernate Sitzungen erhalten. Nachdem Sie eine Fehlermeldung erhalten, müssen Sie die Sitzung ersetzen. Und wenn Sie die Sitzung ersetzen, Sie wahrscheinlich sollte alle Entitäten erneut zu laden, die Sie mit die alte Sitzung in dem neuen Namen, nur auf der sicheren Seite werden geladen.
Konflikt-Management
Das letzte Thema, das ich in diesem Artikel nach dem retuschieren möchten, ist die komplexeren Parallelität Konflikt Verwaltungstechniken. Es ist im Grunde nur eine Option: ermöglichen Sie es dem Benutzer zum Treffen einer Entscheidung zwischen der Version in der Datenbank und die Version, die der Benutzer gerade geändert.
Abbildung 9 zeigt den Seriendruck Bildschirm Nachbildung. Wie Sie sehen können, hier werden dem Benutzer beide Optionen einfach angezeigt und in dem Sie gefragt werden, wählen Sie akzeptieren. Alle Konfliktlösung Parallelität basiert auf diese Idee irgendwie. Möglicherweise möchten Sie es in eine andere Weise präsentieren jedoch, Funktionsweise und Sie können von hier aus extrapolieren.
Abbildung 9 Benutzeroberfläche für das Verwalten von ändern Konflikte
Ändern Sie im Bildschirm bearbeiten die Konfliktlösung folgendermaßen:
catch (StaleObjectStateException) {
var mergeResult =
Presenters.ShowDialog<MergeResult?>(
"Merge", Model.Action);
successfulSave = mergeResult != null;
ReplaceSessionAfterError();
}
Ich das Seriendruck-Dialogfeld anzeigen, und wenn der Benutzer eine Entscheidung über den Seriendruck vorgenommen hat, ich entscheiden, dass es sich um einen erfolgreichen Speichervorgang war (die mit dem Bearbeitungsformular schließen würde). Beachten Sie, dass ich derzeit bearbeitete Aktion zum Dialog "Seriendruck", übergeben damit es den aktuellen Zustand der Entität weiß.
Der Seriendruck Dialogfeld Presenter ist einfach:
public void Initialize(ToDoAction userVersion) {
using(var tx = Session.BeginTransaction()) {
Model = new Model {
UserVersion = userVersion,
DatabaseVersion =
Session.Get<ToDoAction>(userVersion.Id),
AllowEditing = new Observable<bool>(false)
};
tx.Commit();
}
}
Beim Starten ich die aktuelle Version aus der Datenbank abrufen und anzeigen, es selbst und die Version, die der Benutzer geändert. Wenn der Benutzer die Version der Datenbank akzeptiert, erhalten nicht ich viel zu tun, damit ich einfach das Formular zu schließen:
public void OnAcceptDatabaseVersion() {
// nothing to do
Result = MergeResult.AcceptDatabaseVersion;
View.Close();
}
Wenn der Benutzer Ihre eigene Version erzwingen möchte, ist es nur etwas komplizierter:
public void OnForceUserVersion() {
using(var tx = Session.BeginTransaction()) {
//updating the object version to the current one
Model.UserVersion.Version =
Model.DatabaseVersion.Version;
Session.Merge(Model.UserVersion);
tx.Commit();
}
Result = MergeResult.ForceDatabaseVersion;
View.Close();
}
Ich verwende NHibernate des Seriendruck-Funktionen, um die permanente Werte in der Version des Benutzers übernehmen, und kopieren Sie Sie auf die Entitätsinstanz innerhalb der aktuellen Sitzung. Tatsächlich führt die beiden Instanzen, die Benutzer Werte über den Datenbankwert erzwingen zusammen.
Dies ist auch tatsächlich sicher auch mit der anderen Sitzung inaktiver und nicht mehr angezeigt werden, da der Merge-Methode Vertrag sichergestellt, dass es wird nicht versuchen, verzögerte geladene Zuordnungen zu durchlaufen.
Beachten Sie, dass vor dem Versuch des Seriendrucks ich Version-Eigenschaft, des Benutzers auf Version-Eigenschaft, die Datenbank festgelegt. Dies ist notwendig, da in diesem Fall explizit auf diese Version überschrieben werden soll.
Dieser Code versuchen nicht, behandeln rekursive Parallelitätskonflikte (die aufgrund einer ersten Auflösen eines Parallelitätskonflikts erhält). Bleibt als Übung für Sie.
Trotz der Anzahl der in diesem Artikel beschriebenen Herausforderungen nicht das Erstellen einer Desktopanwendung NHibernate alle schwieriger als das Erstellen einer Webanwendung NHibernate. Und in beiden Szenarien ich glaube, dass mithilfe von NHibernate erleichtern wird Ihr Leben einfacher, die Anwendung robuster und die Gesamtleistung des Systems ändern und damit zu arbeiten.
Oren Eini (Werke, die unter dem Pseudonym Ayende Rahien) ist ein aktives Mitglied mehrere open-Source-Projekte (NHibernate und Castle untereinander) und ist der Gründer von vielen anderen, (Rhino Mocks, NHibernate Query Analyzer und Rhino Commons untereinander). Eini ist auch verantwortlich für die NHibernate Profiler ( nhprof.com ), einen visual Debugger für NHibernate. Sie können die Arbeit am ayende.com/Blog-Eini des folgen.
Dank an die folgenden technischen Experten für die Überprüfung dieses Artikels: Howard Dierking