MongoDB: Herausforderungen und Lösungsansätze für die .NET-Serialisierung

Versionsvielfalt von NoSQL-Dokumenten
Kommentare

Ein großer Vorteil vieler NoSQL-Datenbanken ist die Flexibilität des Datenformats. Doch wie lässt sich eine schemalose Dokumentenstruktur in ein typisiertes Klassensystem überführen und erweiterbar halten? Dieser Artikel diskutiert Praxiserfahrungen aus einem größeren Entwicklungsprojekt.

Windows Developer

Der Artikel „Versionsvielfalt von NoSQL-Dokumenten“ von Christian Heger und Daniel Weber ist erstmalig erschienen im Windows Developer 8.2012

Im Gegensatz zu einer relationalen Datenbank werden in der NoSQL-Datenbank MongoDB Daten als Dokumente abgelegt, die in so genannten Collections organisiert sind. Dokumente sind hierbei Strukturen, die Attribute enthalten und somit mit relationalen Tabellen zu vergleichen sind. Ein Dokument kann aber auch Arrays und weitere Unterdokumente enthalten. Die Dokumente in einer einzigen Collection haben dabei nicht notwendigerweise die gleichen Eigenschaften, es gibt kein festes Schema. Obwohl technisch gesehen gar kein Zusammenhang zwischen den Dokumenten einer Collection bestehen muss, macht es meist Sinn, gleichartige Dokumente in einer Collection mit einem sinnvollen Namen zusammenzufassen. Anderenfalls könnte man ja genauso gut nur eine einzige große Collection für alles verwenden. Meist besitzen alle Dokumente in einer Collection dasselbe Format.

In .NET wiederum werden Daten in Objekten abgebildet, die durch ihre Klasse typisiert sind. Um Daten aus MongoDB in eine .NET-Applikation einlesen beziehungsweise später wieder „wegschreiben“ zu können, muss also eine Umwandlung vom Dokument zum Objekt stattfinden. Diese Umwandlung geschieht mithilfe der Serialisierung von Objekten durch den MongoDB-Treiber (mehr dazu im Kasten „Was tut der MongoDB-Treiber?“).

Was tut der MongoDB-Treiber?

Im Gegensatz zu vielen anderen NoSQL-Datenbanken verwendet MongoDB keine REST-Schnittstelle, die Daten in JSON-Notation via HTTP überträgt. Aus Effizienzgründen wird eine TCP-Verbindung verwendet, und die Daten werden im BSON-(Binary-JSON-)Format übertragen. BSON ist auf schnelle Verarbeitung hin optimiert, zum Beispiel ist ein Integer immer 32 Bit lang und muss nicht aus einem String interpretiert werden wie bei JSON. In der Sprachregelung von MongoDB ist ein Treiber eine Clientbibliothek, mit der die Applikation auf MongoDB zugreift. Es gibt mehrere Implementierungen von MongoDB-Treibern für .NET. Dieser Artikel bezieht sich auf den „offiziellen“ Treiber, der von 10gen entwickelt wird.

Ein Problem der Serialisierung stellt sich meist erst dann ein, wenn eine Applikation weiter entwickelt oder umstrukturiert wird. Neue Features ziehen meist neue Daten nach sich, die auch persistent abgelegt werden sollen. Damit passen bei naiver Implementierung die gespeicherten Dokumente nicht mehr zur aktualisierten Anwendung. Eine naheliegende, aus der klassischen SQL-Welt entlehnte Lösung besteht darin, die Daten über ein Updateskript in die neue Dokumentenstruktur zu überführen. Bei einer großen Datenbank kann das aber einige Zeit dauern und verursacht hohe Last auf den Servern. Bei Anwendungen, die permanent verfügbar sein müssen, stellt dieser Ansatz deshalb ein echtes Problem dar.

Weiterhin kann es sein, dass auf einen Teil der Dokumente nur selten zugegriffen wird. Als Beispiel denke man sich ein Dokument, das ein Nutzerprofil in einem Onlineshop abbildet. Dieses Dokument wird nur gelesen, wenn dieser spezielle Benutzer sich anmeldet, also eventuell nur alle paar Monate. Andererseits kann es sehr viele dieser Dokumente geben. Hier bietet es sich an, die Überführung in ein neues Format erst dann vorzunehmen, wenn das unbedingt erforderlich ist. Dafür muss man aber dafür sorgen, dass verschiedene Versionen eines Dokuments gelesen werden können. Dieses Vorgehen soll anhand eines kleinen Beispiels illustriert werden.

Mehrere Versionen desselben Dokuments

Sehen wir uns eine Collection aus einer einfachen Adressdatenbank an. Das ursprüngliche Format entspricht Listing 1. Um die einzelnen Adressinformationen zu einem Typen zusammenzufassen, wurde das Dokumentenformat refaktoriert, sodass es dem Format in Listing 2 entspricht. Da die Dokumente in einer Collection keinem festen Schema unterworfen sind, können jetzt beide Versionen in derselben MongoDB Collection vorkommen. In einer C#-Applikation, die auf die Adressdatenbank zugreift, ist eine Klasse Person definiert (Listing 3). Wie soll jetzt aber eine Abfrage auf Adressen in eine Liste von Objekten überführt werden, wenn jedes Dokument andere Eigenschaften aufweist? Wird versucht Dokumente in dem neuen Format einzulesen, tritt eine Ausnahme auf: Das Dokument enthält Felder, für die es keine Entsprechung im Objekt gibt. Das erkennt der Serializer als Fehler.

Listing 1: Dokument in Version 1

{
    "_id" : "mmustermann",
    "FirstName": "Max",
    "LastName" : "Mustermann",
    "Address" : "Musterstr. 1",
  "ZipCode" : "12345",
    "City" : "Musterstadt",
    "Country" : "Germany"
}  
Listing 2: Dokument in Version 2

{
    "_id" : "mmustermann",
    "FirstName" : "Max",
    "LastName" : "Mustermann",
    "Address" : {
        "Street" : "Musterstr. 1",
        "ZipCode" : "12345",
        "City" : "Musterstadt",
        "Country" : "Germany"
    }
}  
Listing 3: C#-Klasse

public class Person
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
}  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -