Praktické použitie MongoDB v .NET

dotnet logo

NoSQL databázy naberajú v súčasnosti na populárnosti, dlhšie som sa chystal nejakú z nich vyskúšať. Pri práci na aktuálnom projekte som mal na to konečne možnosť, zvolil som riešenie Microsoft .NET.

Zadanie problému

Aplikácia, na ktorej som pracoval, komunikovala s MS SQL databázou a LDAPom bežiacimi na vzdialených počítačoch. Jej úlohou bolo každý deň natiahnúť dáta z LDAPu, transformovať ich, porovnať ich stav s aktuálnym stavom v MS SQL a premietnuť do MS SQL všetky zmeny za posledný deň. Hlavný dôraz sa kládol na rýchlosť.

Keďže dopyty do MS SQL databázy (ktorá naviac beží na vzdialenom počítači) sú dosť pomalé, jasnou voľbou bolo čiastkové natiahnutie potrebných záznamov do pamäte a práca s nimi v pamäti. Platforma Microsoft .NET však na 32-bitovom operačnom systéme obsahuje obmedzenie maximálnej alokovanej pamäte na cca 2 GB, vzhľadom na veľké množstvo záznamov (rádovo státisíce) nebolo možné tieto záznamy v pamäti udržiavať. Bolo potrebné nájsť iné rýchle riešenie s podporou dopytovania.

Dôvod použitia NoSQL databázy

Ako dobrý nápad sa zdalo použitie nejakej NoSQL databázy. NoSQL databázy bývajú na rozdiel od klasických SQL databáz optimalizované na veľké množstvo read/write operácií. Celý nápad spočíval v tom, že na stroji, kde beží aplikácia, bude bežať lokálne aj NoSQL databáza. Aplikácia natiahne záznamy zo vzdialeného MS SQL servera a uloží ich do lokálnej NoSQL databázy. Dotazovanie bude namiesto do hash mapy v pamäti prebiehať do NoSQL databázy, kde kľúč pôvodnej hash mapy bude zodpovedať kľúču NoSQL databázy. Dané riešenie bude určite rýchlejšie ako dopytovanie do vzdialenej MS SQL databázy a nenarazí na limit pamäte.

MongoDB

Ako NoSQL databáza bola zvolená MongoDB. Výhodou je, že MongoDB nie je potrebné inštalovať, je dostupná pre rôzne platformy, skladá sa z pár binárnych súborov a vyžaduje len adresár na uloženie dát. Môže byť teda jednoducho distribuovaná spolu s vašim programom. Stačí, ak túto NoSQL databázu spustíte pred behom vášho programu a ukončíte spolu s ním. Prípadne môžete spustenie a ukončenie MongoDB ovládať programovo.

MongoDB a C#

Keďže MongoDB je dokumentová databáza, bolo potrebné ukladané objekty previesť na dokumenty (MongoDB Document). Ovládač MongoDB pre .NET síce obsahuje možnosť ukladať priamo objekty ľubovoľného typu, nefunguje to však úplne spoľahlivo (občasný StackOverflow v mscore.lib).

Prevod objektov na MongoDB Document

Prevod ľubovoľnej triedy na dokument a jej nasledujúcu rekonštrukciu z dokumentu je možné jednoducho vykonať pomocou reflexie, nie je však možné ukladať ako položky dokumentu iné objekty. Ak nemôžete priamo zasahovať do definícií použitých tried môžete v C# použiť extension method alebo partial classes:

partial class AccountInTime {
  public Document ToDocument()
  {
      Document document = new Document(); document["_id"] = AccountId;
      Type accountInTimeType = this.GetType();
      PropertyInfo[] fieldInfo = accountInTimeType.GetProperties();
      string[] bannedProperties = {"User", "Account"};
      foreach (PropertyInfo info in fieldInfo) {
        if (!bannedProperties.Contains(info.Name))
        {
          document[info.Name] = info.GetValue(this, null);
        }
      }
    return document;
  }
      
  public AccountInTime(Document document) {
      Type accountInTimeType = this.GetType();
      PropertyInfo[] fieldInfo = accountInTimeType.GetProperties();
      foreach (PropertyInfo info in fieldInfo) {
        info.SetValue(this,document[info.Name],null);
      }
  }
}

Metóda ToDocument nastaví kľúč _id na vlastnosť AccountId, pomocou reflexie prejde všetky vlastnosti objektu (okrem explicitne zakázaných) a uloží ich do dokumentu. Konštruktor rovnako pomocou reflexie prejde všetky vlastnosti objektu a naplní ich hodnotami z dokumentu.

Práca s MongoDB

Práca so samotnou MongoDB može byť implementovaná napríklad pomocou Singleton triedy, ktorá vytvára spojenie s MongoDB:

internal sealed class MongoRepository
{
  private static MongoRepository _mongoRepository;

  public static MongoRepository GetInstance()
  {
    return _mongoRepository ?? (_mongoRepository = new MongoRepository());
  }
      
  private readonly Mongo _mongo = new Mongo();
      
  public MongoRepository() {
      
      _mongo = new Mongo();
      _mongo.Connect();
  }
}

Pri vkladaní záznamov do MongDB je potrebné získať referenciu na databázu, na požadovanú kolekciu a na vloženie dokument slúži metóda Insert:

public void Insert(IEnumerable<accountintime> accounts)
{
      
      //databáza
      var db = _mongo.GetDatabase("to2");
      
      //kolekcia
      var collection = db.GetCollection("accountintimes");
      
      //zmazanie všetkých záznamov
      collection.Remove(new Document() { });

      foreach (AccountInTime account in accounts)
      {
      //vloženie záznamu
      collection.Insert(account.ToDocument());
      }
      
}

Premenná db reprezentuje databázu a premenná collection „tabuľku“. Ak daná databáza alebo kolekcia neexistuje, automaticky sa vytvorí. Metóda collection.Remove() maže záznamy vyhovujúce poskytnutému selektoru, new Document() je univerzálny selektor, ktorému vyhovujú všetky záznamy. Napríklad na zmazanie záznamov s _id=123 by bol použitý selektor new Document(){_id=123}.

Prístup k záznamom uloženým v MongoDB je možný napríklad pomocou IEnumerable rozhrania vhodného na jednorazovú iteráciu:

public IEnumerable<document> AccountInTimes
{
      get {

      //databáza
      var db = _mongo.GetDatabase("to2");

      //kolecia
      var collection = db.GetCollection("accountintimes");

      //výber všetkých záznamov
      return collection.FindAll().Documents;
      }
}

V prípade potreby vyhľadania záznamu pomocou nejakého kľúča je možné použiť LINQ a nájdený záznam následne previesť naspäť na objekt pôvodného typu:

AccountInTime dbAccountInTime = new AccountInTime(mongoRepository.AccountInTimes.Where(l => l["_id"].ToString() == accountId.ToString()).SingleOrDefault());

Záver

Použité riešenie s MongoDB fungovalo a vyriešilo pôvodný problém. Prístup k záznamom v lokálnej MongoDB databáze bol síce pomalší ako prístup k záznamov pämati, bol však výrazne rýchlejši ako dopytovanie do vzdialenej MS SQL databázy. Ak budete niekedy potrebovať uložiť veľké množstvo prevažne plochých záznamov do štruktúry podobnej hash mape, určite siahnite po nejakej NoSQL databáze, oplatí sa to.

Autor je absolventom softvérového inžinierstva na Univerzite Karlovej v Prahe, pracuje ako Windows Azure a Windows Phone vývojár v Inmite, občas publikuje a prednáša.

Čtení na léto

Jaké knihy z oboru plánujete přečíst během léta? Pochlubte se ostatním ve čtenářské skupině Zdrojak.cz na Goodreads.com.

Komentáře: 11

Přehled komentářů

Tomáš Herceg Rychlost
igorkulman Re: Rychlost
Logik Re: Rychlost
igorkulman Re: Rychlost
Roman Antoň Re: Rychlost
fos4 MongoDB - XML
ghjfghj Re: MongoDB - XML
Ladislav Thon Re: Praktické použitie MongoDB v .NET
Majkl Konzistence dat
ghjfghj Mono
xor Re: Mono
Zdroj: http://www.zdrojak.cz/?p=3444