ahhay
ahhay | Hinter den Höfen 20 | 37339 Haynrode/Germany | 036077 935077

MVVM Start mit NoSql-Datenbank RavenDB



Grundlagen

  • RavenDB ist eine dokumentenorientierte Datenbank, das bedeutet, dass man einzelne Zeilen in JSON speichert
  • RavenDB ist eine dokumentenorientierte Datenbank der 2. Generation
  • mit RavenDB steht einem ein ausgereiftes Produkt zur Verfügung, das hervorragend mit .Net zusammen spielt
  • zahlreiche Strategien hilfen mit RavenDB eine performante Anwendung zu schreiben
  • als OpenSource-Projekte kann man RavenDB kostenlos nutzen

Weiterführende Informationen findet man auf der RavenDB-Webseite, welche zudem auch eine sehr gute Dokumentation enthält:


RavenDB


Schritt 1

Um einen RavenDB-Client einbinden zu können, muss eine Solution erstellt werden.

Hierfür wurde eine WPF-Beispielsolution erstellt, welche nach dem MVVM-Pattern entwickelt wurde.

Das Model-View-ViewModel-Pattern ist das „State of the Art“-Pattern für WPF-Anwendungen. Es wurde bereits in zahlreichen Artikeln beschrieben. Der vorliegende Artikel soll nicht eine weitere Variante sein. Stattdessen wird hier gezeigt, wie sich aus einem ViewModel Datenbankoperationen auf der RavenDB durchführen lassen. Vorweg zeigt der Artikel auch noch einige Tipps zum Erstellen von ViewModel-Klassen und Verwenden von Commands.



Schritt 2

namespace WpfAppRavenDb
{
    public class ViewModel : INotifyPropertyChanged
    {
        #region Implementation of INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }
}
                                

Alle Besucher dieser Seite, welche kein Interesse an der MMV-Pattern Implementierung haben, können auf Schritt 9 wechseln.

Erstellung des ViewModel:

Das VM ist das Bindeglied zwischen View und Model und bildet somit den Kern des MVVM-Patterns, gewissermaßen den Controller. Das VM kann Eingaben aus dem View entgegennehmen oder mit einem anderen Dienst interagieren, um die Model-Daten an die View weiterzuleiten.




Schritt 3


<Window.DataContext>
    <local:ViewModel />
</Window.DataContext>
                                

Code der View:

Die Bindung des ViewModels an die View erfolt über den DataContext des Windows.




Schritt 4

<StackPanel Grid.Column="0" Orientation="Vertical">
    <TextBlock Height="20" Text="Liste der Operationen:" />
    <ListBox ItemsSource="{Binding Path=OperationMsg}"/>
</StackPanel>
                                

Code der View:

Die Seite ist in zwei Spalten eingeteilt. Links ist eine ListBox platziert, welche alle Datenbank-Operationen protokolliert.




Schritt 5

<StackPanel Grid.Column="1">
    <Button... Command="{Binding Path=CreateCommand}" Content="RavenDB Laden"/>
    <Button... Command="{Binding Path=ReadCommand}" Content="Default Buch auslesen"/>
    <Button... Command="{Binding Path=EditCommand}" Content="Vorhandenes Buch verändern"/>
    <Button... Command="{Binding Path=DeleteCommand}" Content="Vorhandenes Buch löschen"/>
</StackPanel>
                                

Code der View:

In der rechten Spalte sind vier Buttons mit Commands definiert. Mit diesen Commands können vier DB-Operationen nach dem Akronym CRUD durchgeführt werden.

Mit CRUD werden die grundlegenden Datenbankoperationen Create (Datensatz anlegen), Read oder Retrieve (Datensatz lesen), Update (Datensatz aktualisieren) und Delete oder Destroy (Datensatz löschen) bezeichnet.




Schritt 6

using System;
using System.Diagnostics;
using System.Windows.Input;
namespace WpfAppRavenDb
{
    public class RelayCommand : ICommand
    {
        #region Fields
        readonly Action < object> _execute;
        readonly Predicate< object> _canExecute;
        #endregion // Fields
        #region Constructors
        public RelayCommand(Action< object> execute)
            : this(execute, null)
        {
        }
        public RelayCommand(Action< object> execute, Predicate< object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors
        #region ICommand Members
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        #endregion // ICommand Members
    }
}
                                

Infrastruktur-Code für die Command-Weitergabe:




Schritt 7

RelayCommand _createCommand;
public ICommand CreateCommand
{
    get { return _createCommand ?? (_createCommand = new RelayCommand(param => this.Create())); }
}
                                

Das ViewModel kann die Commands über folgende Property vom Typ RelayCommand empfangen.

Nach Betätigung des Buttons wird über den Commend die ViewModel-Methode Create aufgerufen.




Schritt 8

using System;
namespace WpfAppRavenDb
{
    public class BookModel
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string ISBN { get; set; }
        public int Pages { get; set; }
        public override string ToString()
        {
            return String.Format("{0} '{1}' ({2}) has {3} pages", Id, Title, ISBN, Pages);
        }
    }
}
                                

Code des Models:

Das Model bildet die Datenzugriffsschicht für die Inhalte, die dem Benutzer angezeigt und von ihm manipuliert werden.




Schritt 9

In diesem Schritt soll der RavenDB-Client implementiert werden.

Den Client kann man leicht über NuGet laden



Schritt 10

DLL für den Client sind hinzugefügt worden.



Schritt 11

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<books xmlns:xsi="ht tp://www.w3.org/2001/XMLSchema-instance">
	<book>
		<Id>1</Id>
		<Title>books 1</T>
		<ISBN>ISBN-123456-01</ISBN>
		<Pages>201</Pages>
	</book>
	<book>
		<Id>2</Id>
		<Title>books 2</Title>
		<ISBN>ISBN-123456-02</ISBN>
		<Pages>301</Pages>
	</book>
    ...
                                

Um Testdaten in der RavenDB speichern zu können, wurden Excel-Daten erzeugt und als XML-Daten abgelegt.




Schritt 12

private const string Url = "http://localhost:8080/";       
private const string Database = "TestAndreas.Book";
...
private void Create()
{
    const string path = @"Data/BookDb.xml";
    OperationMsg.Add(string.Format("Open Path: {0}", path));
    var bookList = from c in XElement.Load(path).Elements("book") select c;
    using (var ds = new DocumentStore { Url = Url }.Initialize())
    using (var session = ds.OpenSession(Database))
    {
        var counter = 0;
        foreach (var bk in bookList)
        {
            var book = new BookModel()
            {
                Id = int.Parse(bk.Element("Id").Value),
                Title = bk.Element("Title").Value,
                ISBN = bk.Element("ISBN").Value,
                Pages = int.Parse(bk.Element("Pages").Value)
            };
            session.Store(book);
            session.SaveChanges();
            counter++;
            var id = string.Format("BookModels/{0}", counter);
            OperationMsg.Add(string.Format("Save Book with ID: {0}", id));
        }
    }
}
                                

create - Erzeugen und Speichern von Daten auf der RavenDB:

In der Save-Methode wird die XML-Datei geöffnet und zeilenweise ausgelesen.

Die einzelnen XElement-Objecte werden im Typ BookModel zwischengespeichert und anschließend auf der RavenDB abgelegt.




Schritt 13

using (var ds = new DocumentStore { Url = "http://localhost:8080/" }.Initialize())
using (var session = ds.OpenSession("TestAndreas.Book"))
{...
        var book = new BookModel()
        {...
        };
        session.Store(book);
        session.SaveChanges();
    }
}...
                                

Das eigentliche Speichern auf der RavenDB umfasst nur nebenstehende Codezeilen




Schritt 14

RavenDB - Neue Datenbank anlegen 1:

Um Daten überhaupt speichern zu können, muss eine RavenDB auf den Rechner vorhanden sein.

RavenDB Download: RavenDB




Schritt 15

RavenDB - Neue Datenbank anlegen 2:

Über den Button "New Database" kann eine neue Datenbank erzeugt werden. Diese ist über den lokalen Port 8080 und den vergebenen Namen erreichbar.




Schritt 16

Über den Button "RavenDB Laden" werden die Daten in der neu erstellten Datenbank gespeichert.




Schritt 17

Über das lokale Web-Interface können die Daten direkt auf der RavenDB angesehen werden.




Schritt 18

private void Read()
{
    const string search = "BookModels/5";
    using (var ds = new DocumentStore { Url = Url }.Initialize())
    using (var session = ds.OpenSession(Database))
    {
        var book = session.Load< BookModel>(search);
        OperationMsg.Add(string.Format("book with id {0}", search));
        OperationMsg.Add(string.Format("book title {0}", book.Title));
        OperationMsg.Add(string.Format("book pages {0}", book.Pages));
        OperationMsg.Add(string.Format("book isbn {0}", book.ISBN));
    }
}
                                

Das Auslesen der Daten erfolgt über die RavenDB-Methode Load.




Schritt 19

Das Ergebnis des Auslesen ist im Operationsfenster dargestellt und stimmt mit den Werten auf der Datenbank überein.




Schritt 20

private void Edit()
{
    const string search = "BookModels/1";
    using (var ds = new DocumentStore { Url = Url }.Initialize())
    using (var session = ds.OpenSession(Database))
    {
        var book = session.Load< BookModel>(search);
        OperationMsg.Add(string.Format("book with id {0} edit.", search));
        var oldTitle = book.Title;
        book.Title = "andreas has edit";
        session.Store(book);
        session.SaveChanges();
        var renamedBook = session.Load< BookModel>(search);
        OperationMsg.Add(string.Format("book with id: {0}. new title {1} and old title {2}.", search, renamedBook.Title, oldTitle));
    }
}
                                

Das Bearbeiten der Daten erfolgt ebenfalls über die RavenDB-Methode Load.

Die Daten werden ausgelesen, bearbeitet und wieder unter der ID gespeichert.




Schritt 21

Das Ergebnis des Editierens ist im Operationsfenster dargestellt und in der Datenbank kann man den neuen Buchtitel sehen.




Schritt 22

private void Delete()
{
    const string search = "BookModels/10";
    using (var ds = new DocumentStore { Url = Url }.Initialize())
    using (var session = ds.OpenSession())
    {
        var book = session.Load< bookmodel>(search);
        if (book == null) OperationMsg.Add(string.Format("book not found, book with ID: {0}", search));
        OperationMsg.Add(string.Format("delete book with ID: {0}", search));
        session.Delete(book);
        session.SaveChanges();
        var renamedBook = session.Load< bookmodel>(search);
        OperationMsg.Add(renamedBook == null
            ? string.Format("Book with id {0} not found", search)
            : string.Format("Book with id {0} found! Not delete.", search));
    }    
}
                                

Das Löschen der Daten erfolgt über die RavenDB-Methode Delete.




Schritt 23

Auf der Datenbank kann man sehen, dass das Buch mit der ID BookModels/10 fehlt.




Zusammenfassung

Die Verwendung der NoSQL-Datenbank RavenDB gestaltet sich für einen ersten Einstieg einfach, aber die sehr gute Dokumentation auf der Herstellerseite erleichert auch die Verwendung bei komplexeren Aufgaben.

Dieser Artikel soll ein erste Einstieg in die Thematik sein und zeigt die einfachste Verwendung.

Die Bearbeitung/Veränderung der Daten erfolgt hierbei ausschließlich im C#-Code, weil hier eine vollständige Testabdeckung erreicht werden kann.