Zum Inhalt

RavenDB: Eine Einführung

RavenDB ist eine dokumentenorientierte Datenbank. Dies bedeutet das man nicht einzelne Zeilen speichert, sondern ganze Dokumente (im Sinne von JSON und nicht eines Word-Dokuments).

RavenDB ist nicht die erste solche Datenbank. Ganz bewusst spricht man hier von einer 2. Generation. Die Vorteile von Produkten die den Begriff Dokumentenorientierte Datenbank begründeten (wie MongoDB) wurden aufgenommen und gleichzeitig hat man die grössten Probleme und Unschönheiten behoben.

Mit RavenDB steht einem ein ausgereiftes Produkt zu Verfügung das hervorragend mit .Net zusammen spielt. Mit zahlreichen Strategien hilft einem RavenDB eine performante Anwendung zu schreiben. Das dazu nötige Wissen sammelte der Hauptentwickler Ayende Rahien bei Projekten wie NHibernate und dem NH- und EF-Profiler.

Für OpenSource-Projekte kann man RavenDB kostenlos nutzen. Das Lizenzierungsschema für ClosedSource-Projekte ist recht umfangreich und hilft einem das zum Projekt passende Modell zu finden. Die Preise starten bei moderaten 7$ pro Monat mit die Basic-Version und steigen je nach Funktionsbedarf an.

Installation der Datenbank

Um den Server-Teil von RavenDB zu installieren gibt es 2 Wege:

  1. Der Download einer Zip-Datei
  2. Das NuGet-Paket RavenDB.Server

Für den Server ist die Option 1 der bevorzugte Weg. Nach dem Entpacken der Zip-Datei startet man das Programm start.cmd und RavenDB kümmert sich um den Rest. Die webbasierte Administrationsoberfläche ist von nun an über https://localhost:8080 (oder dem ersten freien Port darüber) erreichbar:

RavenDB Studio

Bevorzugt man lieber die Variante mit NuGet muss man für den Server-Teil ein eigenes Projekt anlegen. Dieses Paket ist nicht dazu gedacht mit Visual Studio verwendet zu werden und man braucht dieses separate Projekt einzig als Installationscontainer. Die Startdatei heisst in diesem Fall Raven.Server.exe und liegt im zur Solution gehörenden Verzeichnis packages\RavenDB.Server.1.0.960\. Abgesehen davon gibt es keine Unterschiede zwischen den beiden Ansätzen.

Versucht man den Server im gleichen Projekt zu installieren wie den Client wird dies in nicht auflösbaren Abhängigkeiten enden. Daher die beiden Teile unbedingt trennen!

Installation des Clients

Hier sollte man aufs NuGet-Paket setzen. Neben dem Client selber werden so auch gleich alle Abhängigkeiten aufgelöst. Die Installation des Paketes RavenDB.Client erfolgt entweder über die grafische Oberfläche oder mit diesem Befehl in der Package Manager Console:

Install-Package RavenDB.Client

Sofern man keine speziellen Einstellungen machen will ist die Installation damit abgeschlossen.

Daten speichern

Mit RavenDB lassen sich Daten ganz einfach speichern. Damit IntelliSense bei Abfragen helfen kann sollte man erst einmal eine Klasse definieren:

public class Book
{
    public string 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);
    }
}

Um Objekte vom Typ Book zu speichern genügt es die Main-Methode einer Konsolenapplikation mit diesem Code zu erweitern:

1
2
3
4
5
6
7
8
9
using (var ds = new DocumentStore { Url = "http://localhost:8081/" }.Initialize())
using (var session = ds.OpenSession())
{
    var book = new Book() { Title = "RavenDB Intro", ISBN = "123-4-5678", Pages = 200 };
    session.Store(book);
    session.SaveChanges();
    Console.WriteLine(book.Id);
// => books/1 'RavenDB Intro' (123-4-5678) has 200 pages
}

Alles was man braucht um mit RavenDB Objekte zu speichern ist eine laufende Datenbank, eine Session und das zu speichernde Objekt. RavenDB nutzt für alle Operationen das Unit of Work Pattern. Bei so kleinen Demo-Anwendungen mag dies übertrieben erscheinen. Will man RavenDB aber produktiv einsetzen wird man sehr schnell die damit verbundenen Vorteile (wie Batch-Operationen) zu schätzen lernen.

Abfragen mit LINQ

RavenDB arbeitet sehr gut mit .Net zusammen. Dies wird nicht zu Letzt bei den Abfragen mittels LINQ deutlich. Möchte man alle Bücher die mit „Raven“ beginnen kann man diese ganz gewöhnliche LINQ-Abfrage schreiben:

using (var ds = new DocumentStore { Url = "http://localhost:8081/" }.Initialize())
using (var session = ds.OpenSession())
{
    var book = session.Load<Book>("books/1");
    Console.WriteLine(book);

    var ravenBooks = from books in session.Query<Book>()
            where books.Title.StartsWith("Raven")
            select books;

    foreach (var foundBook in ravenBooks)
    {
        Console.WriteLine(foundBook.Id + ": " + foundBook.Title);
        // => books/1 : RavenDB Intro
    }
}

Die Abfragen können beliebig verfeinert werden. Alles was man sonst so von LINQ kennt und schätzen gelernt hat funktioniert auch mit dem Provider für RavenDB.

Wenn man die ID eines Objektes kennt kann man auf LINQ verzichten und es direkt über die Session laden:

var book = session.Load<Book>("books/1");

Verändern oder Löschen

Daten können ganz einfach verändert werden. Und werden die Daten nicht mehr benötigt lassen sie sich auch ohne grossen Aufwand löschen. Zum Editieren erzeugt man jeweils erst eine Abfrage die einem die gewünschten Objekte holt. Nach dem Verändern müssen die Änderungen der Session bekannt gemacht werden – auch hier nutzt man ja das Unit of Work Pattern.

using (var ds = new DocumentStore { Url = "http://localhost:8081/"}.Initialize())
using (var session = ds.OpenSession())
{
    var book = session.Load<Book>("books/1");
    Console.WriteLine(book);

    book.Title = "Another Book about RavenDB";
    session.Store(book);
    session.SaveChanges();

    var renamedBook = session.Load<Book>("books/1");
    Console.WriteLine(renamedBook);
    // => books/1 'RavenDB Intro' (123-4-5678) has 200 pages
    // => books/1 'Another Book about RavenDB' (123-4-5678) has 200 pages
}

Zum Löschen eines Objektes beginnt man ebenfalls mit einer Abfrage und markiert das Objekt als gelöscht:

using (var ds = new DocumentStore { Url = "http://localhost:8081/" }.Initialize())
using (var session = ds.OpenSession())
{
    var book = session.Load<Book>("books/1");
    Console.WriteLine(book);

    session.Delete(book);
    session.SaveChanges();

    var renamedBook = session.Load<Book>("books/1");
    if (renamedBook == null)
    {
        Console.WriteLine("Book with id 1 not found");    
    }
    // => books/1 'RavenDB Intro' (123-4-5678) has 200 pages
    // => Book with id 1 not found
}

Vergisst man das Objekt erst zu holen wirft RavenDB eine entsprechende Exception. Der Vorteil davon ist man löscht auch wirklich nur das gewünschte einzelne Objekt.

Fazit

Mit RavenDB kommt man ohne grosse Vorbereitung schnell zu einer Dokumentendatenbank. Die Standardeinstellungen sind Ideal um sich ins Thema NoSQL einzuarbeiten. Und auch das Unit of Work Pattern kann man ohne grossen Aufwand in Aktion erleben.

Will man RavenDB produktiv einsetzten wird man aber noch ein wenig mehr Aufwand treiben müssen als für diese Demobeispiele. Zu vielen Bedürfnissen einer Produktionsumgebung bietet einem RavenDB eine optimale Unterstützung. Diese Teile werde ich in einem eigenen Blogeintrag genauer beschreiben.