Tra i principali linguaggi di programmazione utilizzati per la programmazione lato server, uno è certamente C#, che unitamente al framework .NET è spesso utilizzato nelle soluzioni basate su software Microsoft.
In questa lezione vedremo come interfacciarsi con un database MongoDB utilizzando il driver ufficiale per il linguaggio C#. Partiremo dall'installazione, e vedremo tutte le principali operazioni supportate.
Installazione
La prima cosa da fare è ottenere il driver per C#. Possiamo fare riferimento al sito ufficiale, dal quale possiamo scaricare il pacchetto da installare (seguendo le istruzioni riportate). Sebbene questa opzione sia comunque semplice, la soluzione migliore (soprattutto in considerazione del fatto che spesso si lavora su Visual Studio) è quella di affidarsi a NuGet, il package manager incluso già in tutte le versioni recenti della celebre IDE di Microsoft.
Facendo riferimento a Visual Studio 2013 (sebbene il procedimento sia pressocché analogo anche per le versioni successive), una volta creato il nostro progetto, è sufficiente selezionare la voce di menu Progetto, selezionando poi Gestisci pacchetti NuGet...
Nella finestra che si aprirà, inseriamo nel campo di ricerca in alto a destra la stringa "MongoDB", e premiamo il tasto Invio. Tra le opzioni visualizzate, troveremo MongoDB.Driver: selezioniamo la voce e premiamo sul pulsante Installa visualizzato a destra.
Completata l'installazione, assicuriamoci di aggiungere al nostro progetto i riferimenti alle .dll MongoDB.Bson.dll MongoDB.Driver.dll MongoDB.Driver.Core.dll
A questo punto, non resta che iniziare a programmare.
Connessione al database
Per interagire con il database, dobbiamo innanzitutto specificare quali riferimenti utilizzare, mediante le opportune clausole import:
using MongoDB.Bson;
using MongoDB.Driver;
Fatto ciò, sfruttiamo ora le funzionalità messe a disposizione dal driver C# per connetterci al database. Sono sufficienti le righe seguenti:
protected static IMongoClient client;
protected static IMongoDatabase database;
client = new MongoClient();
database = client.GetDatabase("test");
A questo punto, utilizzeremo l'oggetto database
Inserimento di documenti nel DB
Per inserire un documenti, dobbiamo innanzitutto generarlo. La creazione di un documento avviene mediante la classe BsonDocument
:
var document = new BsonDocument
{
{ "indirizzo", new BsonDocument
{
{ "via", "Viale Sarca, 336" },
{ "cap", "20126" },
{ "edificio", "16" }
}
},
{ "citta", "Milano" },
{ "stato", "Italia" },
{ "note", new BsonArray
{
new BsonDocument
{
{ "data", new DateTime(2017, 10, 2, 0, 0, 0, DateTimeKind.Utc) },
{ "contenuto", "Test 1" }
},
new BsonDocument
{
{ "data", new DateTime(2014, 2, 7, 0, 0, 0, DateTimeKind.Utc) },
{ "contenuto", "Test 2" }
}
}
},
{ "nome", "HTML.it" }
};
Definito il documento, l'inserimento avviene mediante il metodo InsertOneAsync
della classe MongoCollection
. Ovviamente dovremo specificare in quale collection inserire il documento. Procederemo quindi così:
var collection = database.GetCollection<BsonDocument>("lamiacollection");
await collection.InsertOneAsync(document);
Si noti che il metodo InsertOneAsync
non restituisce alcun valore di ritorno. Inoltre, se l'oggetto di tipo BsonDocument
, passato come argomento a InsertOneAsync
, non contiene un campo _id
, quest'ultimo verrà generato automaticamente all'atto dell'inserimento del documento nella collection.
Selezione: eseguire una query
Per selezionare (e quindi leggere) i dati dal database, il driver C# mette a disposizione 2 metodi: Find
e FindAsync
. Entrambi questi metodi ritornano un documento o un insieme di documenti, in base al criterio di filtraggio specificato.
Il metodo FindAsync
restituisce i risultati della query in un oggetto IAsyncCursor
, che è iterabile. Il metodo Find
, invece, produce un oggetto IFindFluent
, che può essere convertito in una lista di documenti tramite il metodo ToListAsync
.
Per ottimizzare l'uso della memoria, è consigliabile utilizzare i metodi asincroni, e per questo motivo opteremo per tale soluzione negli esempi che seguono.
Vediamo subito come richiedere la lista di tutti i documenti di una collection:
var collection = database.GetCollection<BsonDocument>("lamiacollection");
var filtro = new BsonDocument();
var count = 0;
using (var cursor = await collection.FindAsync(filtro))
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
// iterare su batch permette di leggere tutti i documenti (document)
count++;
}
}
}
In questo caso, il filtro rappresenta un oggetto BSON vuoto, che deve comunque essere passato al metodo FindAsync
while
MoveNextAsync()
Current
cursor
IAsyncCursor
Se vogliamo specificare opzioni di filtraggio più specifiche, è consigliabile sfruttare la classe Builders
, pensato proprio per questa esigenza. Supponiamo, per esempio, di volere selezionare tutti i documenti con l'attributo citta
uguale a Milano
:
var filtro = Builders<BsonDocument>.Filter.Eq("citta", "Milano");
Il metodo Eq
è quindi utilizzato per specificare filtri basati su condizioni di equivalenza. Alternativamente, possiamo utilizzare i metodi Lt
e Gt
(rispettivamente, less than - minore di - e greater than - maggiore di) per realizzare disuguaglianze.
Facendo riferimento alla struttura del documento vista in precedenza, possiamo anche specificare tali condizioni su parti di documento annidate (sfruttando la cosiddetta dot notation). Ad esempio, il codice seguente mostra come selezionare i documenti il cui campo indirizzo
ha un sottocampo cap
pari a 91021
:
var filtro = Builders<BsonDocument>.Filter.Eq("indirizzo.cap", "91021");
È altresì possibile combinare più filtri tramite gli operatore &
|
var builder = Builders<BsonDocument>.Filter;
var filtro = builder.Filter.Eq("indirizzo.cap", "91021") & builder.Filter.Eq("citta", "Milano");
Infine, per ordinare i risultati rispetto ad un certo campo, utilizziamo i metodi Sort.Ascending
Sort.Descending
var sort = Builders<BsonDocument>.Sort.Ascending("citta").Descending("indirizzo.cap");
L'oggetto sort
var result = await collection.Find(filtro).Sort(sort).ToListAsync();
Maggiori dettagli possono essere reperiti facendo riferimento al tutorial ufficiale.
Modificare un documento
Le operazioni di aggiornamento sono realizzate mediante i metodi UpdateOneAsync
(per aggiornare un solo documento) e UpdateManyAsync
(per aggiornare più documenti). Anche in questo caso, per costruire le query di aggiornamento, si utilizzano i Builders
.
Vediamo un esempio in cui aggiorniamo un campo di un documento:
var collection = database.GetCollection<BsonDocument>("lamiacollection");
var filtro = Builders<BsonDocument>.Filter.Eq("citta", "Milano");
var update = Builders<BsonDocument>.Update
.Set("stato", "San Marino")
.CurrentDate("lastModified");
var result = await collection.UpdateOneAsync(filtro, update);
Anche in questo caso effettuiamo una selezione mediante l'oggetto filtro
Update.Set
dot notation
UpdateOneAsync
UpdateManyAsync
var result = await collection.UpdateManyAsync(filtro, update);
Esiste anche la possibilità di sostituire un documento esistente
_id
ReplaceOneAsync
var result = await collection.ReplaceOneAsync(filtro, nuovo_documento);
Anche per le operazioni di aggiornamento, tutti i dettagli possono essere reperiti sul tutorial ufficiale.
Eliminazione dei documenti
L'ultima tipologia di operazioni che dobbiamo analizzare riguarda la possibilità di eliminare dati dal database. I metodi da utilizzare sono DeleteOneAsync
e DeleteManyAsync
. Vediamo subito un esempio:
var collection = database.GetCollection<BsonDocument>("lamiacollection");
var filtro = Builders<BsonDocument>.Filter.Eq("citta", "Milano");
var result = await collection.DeleteManyAsync(filtro);
Come si vede, il funzionamento è analogo a quelli precedenti: dobbiamo specificare un filtro (sempre mediante Builders
), e passarlo come argomento ai metodi preposti alla rimozione.
Un caso particolare è quello in cui si vogliano rimuovere tutti i documenti di un database (operazione rischiosa, ma talvolta utile). In questo caso è sufficiente utilizzare, come filtro, un documento BSON vuoto:
var result = await collection.DeleteManyAsync(new BsonDocument());
Se vogliamo rimuovere un'intera collection
DropCollectionAsync
await database.DropCollectionAsync("lamiacollection");
Ulteriori informazioni sulle operazioni di eliminazione dei dati sono disponibili nell'apposito tutorial ufficiale.