RSS FeedFeed TwitterTwitter XINGXING
  
Meine Bücher

Verteilte Systeme und Services mit .NET 4.0: Konzepte und Lösungen mit WCF 4.0,
Hanser Fachbuchverlag

Weitere Infos

 

.NET 4 Update,
Microsoft Press

Weitere Infos

 
Weitere Bücher
Meine Artikel

Leichtgewichtige Kommunikation: REST-basierte Services mit dem neuen API aus der ASP.NET-Familie,
windows.developer

Weitere Infos

 

EAI und EDI in der Cloud ,
windows.developer

Weitere Infos

 

Verteilt und doch ein Ganzes: Verteilte Systeme mit AppFabric Applications,
dot.net magazin

Weitere Infos

 

Vorschau auf Neuerungen der kommenden WCF-Version,
dot.net magazin

Weitere Infos

 

Windows Azure Tutorial, Teil 3: Verbindung zwischen Cloud- und lokalen Applikationen,
iX - Magazin für professionelle Informationstechnik

Weitere Infos

 

MVC, die dritte: ASP.NET MVC 3 RC 2,
dot.net magazin

Weitere Infos

 

Leichtgewichtiges O/R-Mapping - Code Only mit Entitiy Framework CTP 5,
dot.net magazin

Weitere Infos

 

Weitere Artikel

Training & Beratung

Gemeinsam mit meinen Kollegen aus dem IT-Visions Netzwerk unterstützte ich durch zielgerichtete Inhouse-Schulungen und Beratung Unternehmen bei der Planung und Umsetzung von großer Software-Systeme.

Mehr lesen

In Kontakt treten

 
Konferenzen

BASTA! on Tour von 4/25/2012 bis 4/27/2012 in Düsseldorf

Meine Talks

Web-Site der Konferenz

 

BASTA! Spring 2012 von 2/27/2012 bis 3/2/2012 in Darmstadt

Meine Talks

Web-Site der Konferenz

 

ADC - Advanced Developers Conference von 10/26/2011 bis 10/27/2011 in Frankental (Deutschland)

Meine Talks

Web-Site der Konferenz

 

Scandev on tour von 10/18/2011 bis 10/18/2011 in Stockholm (Schweden)

Meine Talks

Web-Site der Konferenz

 

BASTA 2011 von 9/26/2011 bis 9/30/2011 in Mainz

Meine Talks

Web-Site der Konferenz

 

Jazoon - International Conference on the modern art of software von 6/21/2011 bis 6/23/2011 in Zürich

Meine Talks

Web-Site der Konferenz

 

Scandinavian Developer Conference 2011 von 4/4/2011 bis 4/5/2011 in Göteborg (Schweden)

Meine Talks

Web-Site der Konferenz

 

SOA-Days von 3/30/2011 bis 3/31/2011 in Bonn

Meine Talks

Web-Site der Konferenz

 
Weitere Konferenzen
von Manfred, 23. October 2010 23:56

NserviceBus unterstützt mit dem Konzept der Saga langlaufende Transaktionen. Dieses Konzept soll hier anhand des Beispiels Ansuchen und Bearbeiten von Anforderungen, welches hier zum Download bereit steht, beschrieben werden. Im ersten Schritt sucht ein Mitarbeiter um ein Produkt an. Der jeweilige Vorgesetzte erhält eine Nachricht und hat nun die Aufgabe, das Ansuchen zu bearbeiten, also zu genehmigen oder abzulehnen.

Dabei ergeben sich zwei technische Aufgaben: Zum einen muss die Nachricht mit der Entscheidung des Vorgesetzten zu einer der aktiven Sagas zugeordnet werden, damit das System den richtigen Bedarfsträger über diese Entscheidung informiert bzw. die richtigen Produkte bestellt. Zum anderen müssen Zustandsinformationen über die Saga bei jedem mit ihr assoziierten Methodenaufruf geladen bzw. danach wieder gesichert werden. Diese Zustandsinformationen könnten im betrachteten Beispiel die Id, den Namen sowie die Email-Adresse des Mitarbeiters sowie Informationen über die im Falle einer Genehmigung zu bestellenden Produkte beinhalten. Glücklicherweise kümmert sich NServiceBus um diese beiden Aufgaben.

Um mit NServiceBus eine Saga zu implementieren, sind zunächst Klassen für die Nachrichten sowie eine Klasse, welche den Zustand einer Saga repräsentiert, zu erstellen. Die Nachrichtenklassen implementieren per Definition IMessage; die Klasse mit den Zustandsinformationen ISageEntity (siehe Listing 1). Bei IMessage handelt es sich um ein Marker-Interface; ISageEntity gibt hingegen die Eigenschaften Id, OriginalMessageId und Originator vor, welche NServiceBus-intern Verwendung finden.

Listing 1: Nachrichtenklassen und Saga-Entität
   1:  public class Request : IMessage
   2:  {
   3:      public Request()
   4:      {
   5:          Id = Guid.NewGuid().ToString();
   6:      }
   7:  
   8:      public String Id { get; set; }
   9:      public String ProductName { get; set; }
  10:      public double Price { get; set; }
  11:  }
  12:  
  13:  public class Decision : IMessage
  14:  {
  15:      public String RequestId { get; set; }
  16:      public bool Approved { get; set; }
  17:  }
  18:  
  19:  public class RequestSagaData : ISagaEntity  
  20:  {
  21:      public virtual String RequestId { get; set; }
  22:      public virtual DateTime? RequestDate { get; set; }
  23:      public virtual bool? Approved { get; set; }
  24:      public virtual DateTime? DecisionDate { get; set; }
  25:  
  26:      public virtual String ProductName { get; set; }
  27:      public virtual double Price { get; set; }
  28:  
  29:  
  30:      public virtual Guid Id
  31:      {
  32:          get;
  33:          set;
  34:      }
  35:  
  36:      public virtual string OriginalMessageId
  37:      {
  38:          get;
  39:          set;
  40:      }
  41:  
  42:      public virtual string Originator
  43:      {
  44:          get;
  45:          set;
  46:      }
  47:  }


Zur Behandlung der einzelnen innerhalb der Saga erwarteten Nachrichten, wird eine Subklasse von Saga bereitgestellt (Listing 2). Dabei ist Saga mit der zu verwendeten SagaEntity-Implementierung zu typisieren. Über die von dieser Klasse geerbte Methode ConfigureHowToFindSaga wird festgelegt, wie Folgenachrichten zu bereits laufenden Sagas zugewiesen werden sollen. Im betrachteten Beispiel wird festgelegt, dass zu diesem Zwecke die RequestId von Decision mit der in den Zustandsinformationen der Saga gespeicherten RequestId zu vergleichen ist. Bei Gleichheit wird die Nachricht mit der jeweiligen Saga assoziiert.

Für jede zu behandelnde Nachrichten ist darüber hinaus ISagaStartedBy<T> oder IHandleMessage<T> zu implementieren, wobei diese mit der jeweiligen Nachrichtenklasse zu typisieren sind. Mit ISagaStartedBy wird angezeigt, dass eine neue Saga bei Empfang einer durch den Typparameter repräsentierten Nachricht gestartet wird. Im Gegensatz dazu muss bei Verwendung von IHandleMessage die jeweilige Saga bereits aktiv sein. Beide Interfaces geben die Methode Handle(T) vor, deren Aufgabe das Behandeln der jeweiligen Nachrichten ist. Über die Eigenschaft Data, welche den Typ der festgelegten SagaEntity aufweist, kann auf die Zustandsinformationen der Saga zugegriffen werden. Zum Laden und Speichern dieser Informationen verwendet NServiceBus den O/R-Mapper NHibernate. Ein Zutun des Entwicklers ist hierbei nicht von Nöten. Beendet eine Handle-Implementierung die Saga, ruft diese per Definition die Methode MarkAsComplete auf. Dies führt dazu, dass die Zustandsinformationen aus der mit NHibernate eingebundenen Datenbank gelöscht werden.


Listing 2: Message-Handler
   1:  class RequestSaga: 
   2:      Saga<RequestSagaData>,
   3:      ISagaStartedBy<Request>,
   4:      IHandleMessages<Decision>
   5:  {
   6:      public override void ConfigureHowToFindSaga()
   7:      {
   8:          this.ConfigureMapping<Decision>(
   9:              saga => saga.RequestId,
  10:              decision => decision.RequestId);
  11:  
  12:      }
  13:  
  14:      public void Handle(Request message)
  15:      {
  16:          Console.WriteLine("GOT REQUEST:");
  17:          Console.WriteLine("Message Id: " + message.Id);
  18:          Console.WriteLine("Product Name:" + message.ProductName);
  19:          Console.WriteLine("Price: " + message.Price);
  20:          Console.WriteLine();
  21:  
  22:          this.Data.ProductName = message.ProductName;
  23:          this.Data.Price = message.Price;
  24:  
  25:          Data.RequestDate = DateTime.Now;
  26:          Data.RequestId = message.Id;
  27:  
  28:             
  29:      }
  30:  
  31:      private static void SaveHotel()
  32:      {
  33:          using (var s = HibernateHelper.OpenSession())
  34:          {
  35:              using (var t = new TransactionScope())
  36:              {
  37:                  Hotel h;
  38:                  h = new Hotel
  39:                  {
  40:                      Bezeichnung = "Hotel zum großen Test",
  41:                      Sterne = 4
  42:                  };
  43:                  s.Save(h);
  44:                  t.Complete();
  45:              }
  46:          }
  47:      }
  48:  
  49:      public void Handle(Decision message)
  50:      {
  51:          Console.WriteLine("GOT DECISION:");
  52:          Console.WriteLine("Message Id: " + message.RequestId);
  53:          Console.WriteLine("Product Name:" + Data.ProductName );
  54:          Console.WriteLine("Price: " + Data.Price);
  55:          Console.WriteLine("Approved: " + message.Approved);
  56:          Console.WriteLine();
  57:  
  58:          Data.Approved = message.Approved;
  59:          Data.DecisionDate = DateTime.Now;
  60:  
  61:          SaveHotel();
  62:  
  63: 
  64:  
  65:          this.MarkAsComplete();
  66:             
  67:      }
  68:  }


Damit die gezeigten Sagas zur Ausführung gebracht werden können, wurde NServiceBus mit den Methoden Sagas und NHibernateSagaPersister konfiguriert (Listing 3). Erstere Aktiviert die Unterstützung für Sagas; letztere aktiviert den SagaPersister, welche die Zustände mit Hilfe von NHibernate in einer Datenbank speichert.

Listing 3: Konfigurieren von NServiceBus
   1:  static void Main(string[] args)
   2:  {
   3:      Configure  
   4:          .With()  
   5:          .Log4Net()
   6:          .DefaultBuilder()
   7:          .XmlSerializer()  
   8:          .MsmqTransport()  
   9:          .IsTransactional(true)  
  10:          .PurgeOnStartup(false)  
  11:          .UnicastBus()  
  12:          .LoadMessageHandlers()  
  13:          .ImpersonateSender(false)  
  14:          .Sagas() 
  15:          .NHibernateSagaPersister()
  16:          .CreateBus()  
  17:          .Start();
  18:             
  19:      Console.ReadLine();
  20:  }
  21:  
  22:  [...]
  23:  


Darüber hinaus wurden Konfigurationsdaten für den SagaPersister in der Konfiguration abgelegt (Listing 4).

Listing 4: Konfiguration des SagaPersisters
   1:    <NHibernateSagaPersisterConfig>
   2:      <NHibernateProperties>
   3:       
   4:        <add Key="connection.provider"
   5:             Value="NHibernate.Connection.DriverConnectionProvider"/>
   6:        <add Key="connection.driver_class"
   7:             Value="NHibernate.Driver.SqlClientDriver"/>
   8:        <add Key="connection.connection_string"
   9:             Value="Server=[…];initial catalog=[…];Integrated Security=SSPI"/>
  10:        <add Key="dialect"
  11:             Value="NHibernate.Dialect.MsSql2005Dialect"/>
  12:      </NHibernateProperties>
  13:    </NHibernateSagaPersisterConfig>

 

Kategorien: .Net 4