Zum Hauptinhalt springen

C# Driver

  • offizielle Bibliothek für C#-Anwendungen
  • ermöglicht Interaktion mit MongoDB-Datenbanken

Grundlagen für C#-Anwendung mit MongoDB C# Driver

Installation

Überprüfung:

dotnet --list-sdks

Installation:

sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0

Projekt erstellen

Konsolenprojekt:

dotnet new console --name MongoDBApp --framework net8.0

Web-API-Projekt:

dotnet new web --name WebApi --framework net8.0

MongoDB-Driver zu Projekt hinzufügen

cd projectDir
dotnet add package MongoDB.Driver

Projekt starten

dotnet run

MongoDB-Docker-Container starten

docker run --name mongodb -p:27017:27017 -d frm1971/m165-15
docker run --name mongodb -p:27017:27017 -d mongo

Zugriff auf MongoDB

Verbindung herstellen

MongoClient client = new MongoClient("mongodb://localhost:27017");

Zugriff auf Datenbank

IMongoDatabase database = client.GetDatabase("mydatabase");

Repräsentation von Dokumenten

MongoDB-Datenbank-Dokumente werden in C# als Klassen repräsentiert. Eine solche kann wie folgt aussehen:

class Movie(string title, int year, string summary, List<string> actors)
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("_id")]
public ObjectId Id { get; set; }

public string Title { get; set; } = title;
public int? Year { get; set; } = year;
public List<string> Actors { get; set; } = actors;
}
AttributBedeutung
BsonIdId des Dokumentes
BsonRepresentation(BsonType.<>)Typ der Eigenschaft
BsonElement("<elementName>")Name der Eigenschaft des MongoDB-Dokumentes

Zugriff auf Collection

Die MongoDB-Collection wird in C# als IMongoCollection<T> repräsentiert, wobei T die Repräsentationsklasse bzw. der Typ des Dokuments ist.

IMongoCollection<Movie> movies = _database.GetCollection<Movie>("movies");

Beispiel

class MovieService : IMovieService
{

private static readonly string CONNECTION = "mongodb://localhost:27017";
private static readonly string DATABASE_NAME = "mydatabase";
private static readonly string MOVIE_COLLECTION_NAME = "movies";

private readonly MongoClient _client;
private readonly IMongoDatabase _database;
private readonly IMongoCollection<Movie> _movies;

public MovieService()
{
_client = new MongoClient(CONNECTION);
_database = _client.GetDatabase(DATABASE_NAME);
_movies = _database.GetCollection<Movie>(MOVIE_COLLECTION_NAME);
}

Allgemeine Befehle

Datenbanken ermitteln

List<string> databaseNames = _client.ListDatabaseNames().ToList();

Collections ermitteln

List<string> collectionNames = _database.ListCollectionNames().ToList();

CRUD-Operationen (sync)

Create

InsertOne()

Restaurant newRestaurant = new() { Name = "Best Pizza", ... };

_restaurantsCollection.InsertOne(newRestaurant);

Nachdem das Dokument in die Collection eingefügt wurde, wird das _id-Feld von der Datenbank automatisch in das übergebene Objekt geschrieben. Dies passiert jedoch nur, wenn das C#-Objekt ein Feld vom Typ ObjectId oder String mit dem Attribut [BsonId] enthält. Siehe Repräsentation von Dokumenten und StackOverflow - Get _id of an inserted document in MongoDB?.

InsertMany()

List<Restaurant> newRestaurants = [
new() { Name = "Best Pizza", ... },
new() { Name = "Sushi Place", ... }
];

_restaurantsCollection.InsertMany(newRestaurants);

Read

Folgende Möglichkeiten gibt es, um das Ergebnis zu erhalten:

MethodeRückgabe
FirstOrDefault()erstes Dokument oder null, wenn kein Dokument gefunden wurde
ToList()alle gefundenen Dokumente als Liste
ToCursor()Cursor, um über alle Elemente zu iterieren
......

Variante 1: Find mit Filter

var filter = Builders<Restaurant>.Filter
.Eq(r => r.Name, "Bagels N Buns");

Restaurant restaurant = _restaurantsCollection
.Find(filter)
.ToList();

Alle Dokumente können mit einem leeren Filter zurückgegeben werden:

var filter = Builders<Restaurant>.Filter.Empty;

List<Restaurant> restaurants = _restaurantsCollection
.Find(filter)
.ToList();

Variante 2: Find mit Query

List<Restaurant> restaurants = _restaurantsCollection
.Find(r => r.Name == "Bagels N Buns" && r.City == "New York")
.ToList();

Variante 3: Find mit LINQ

var result = _restaurantsCollection.AsQueryable()
.Where(r => r.Name == "Bagels N Buns")
.FirstOrDefault();

oder

var builder = Builders<Flower>.Filter;
var filter = builder.Lt("Price", 20) & builder.Eq("Category", "Perennial");
var query = collection.AsQueryable()
.Where(f => filter.Inject());

oder

var result = from r in _restaurantsCollection.AsQueryable()
where r.Name == "Bagels N Buns"
select r;

Links:

Weitere Befehle

Dokumente zählen:

var count = _restaurantsCollection.Find(filter).CountDocuments();

Duplikate herausfiltern:

var restaurants = _restaurantsCollection
.Distinct(r => r.Cuisine, filter);

Ergebnisse begrenzen:

var restaurants = _restaurantsCollection.Find(filter).Limit(3).ToList();

Ergebnisse überspringen:

var restaurants = _restaurantsCollection.Find(filter).Skip(2).ToList();

Sortierung:

var restaurants =  _restaurantsCollection.Find(filter)
.SortBy(r => r.Name).ToList();

Projection (Auswahl von Feldern):

var projection = Builders<Restaurant>.Projection
.Include(restaurant => restaurant.Name)
.Include(restaurant => restaurant.Borough)
.Exclude(restaurant => restaurant.Id);
var restaurants = _restaurantsCollection.Find(filter).Project(projection).ToList();

Update

// filter for the document(s) to update
var filter = Builders<Restaurant>.Filter
.Eq(restaurant => restaurant.Name, "Bagels N Buns");

// update definition
var update = Builders<Restaurant>.Update
.Set(restaurant => restaurant.Name, "2 Bagels 2 Buns");

// update one or all filtered documents
UpdateResult result1 = _restaurantsCollection.UpdateOne(filter, update);
UpdateResult result2 = _restaurantsCollection.UpdateMany(filter, update);

Replace:

var replaceResult = _restaurantsCollection.ReplaceOne(filter, newPizzaRestaurant);
var replaceResult = _restaurantsCollection.ReplaceMany(filter, newPizzaRestaurant);

UpdateResult

MongoDB C# Driver API Reference - Class Update Result

Updates geben immer ein UpdateResult-Objekt zurück, das Informationen über die durchgeführte Aktualisierung enthält.

PropertyBedeutung
MatchedCountAnzahl der gefilterten Dokumente
ModifiedCountAnzahl der geänderten Dokumente
......

Delete

// filter for the document(s) to delete
var filter = Builders<Restaurant>.Filter
.Eq(r => r.Name, "Ready Penny Inn");

// delete one or all filtered documents
DeleteResult result1 = _restaurantsCollection.DeleteOne(filter);
DeleteResult result2 = _restaurantsCollection.DeleteMany(filter);

Builders<T>

Builders<T> ist eine Hilfsklasse, die das Erstellen von Filtern, Updates und Sortierungen vereinfacht. Sie bietet statische Methoden, um Filter, Updates und Sortierungen zu erstellen.

Links:

Filter

Erstellung:

var builder = Builders<Movie>.Filter;

Vergleichs-Operatoren:

builder.Eq(m => m.Title, "Star Wars");
builder.Ne(m => m.Title, "Star Wars");
builder.Gt(m => m.Year, 1985);
builder.Lt(m => m.Year, 2000);
builder.Gte(m => m.Year, 1985);
builder.Lte(m => m.Year, 2000);

// or with string-based field names
builder.Eq("Title", "Star Wars");
builder.Gt("Year", 1985);

logische Operatoren:

var filter1 = builder.And(
builder.Gte(m => m.Year, 1985),
builder.Ne(m => m.Name, "Kiesel am Strand")
);
// is identical with
var filter2 = builder.Gte(m => m.Year, 1985) & builder.Ne(m => m.Name, "Kiesel am Strand");

Array-Operatoren:

// if array contains one element which matches one condition
builder.Eq("Actors", "Actor One");
builder.Lt(doc -> doc.Numbers, 20);
builder.Gt(doc -> doc.Numbers, 20);
// ...

// if array contains one element which matches all conditions
Builders<BsonDocument>.Filter
.ElemMatch<BsonValue>("Numbers", new BsonDocument { { "$gt", 22 }, { "$lt", 30 } });

builder.All(m => m.Actors, new[] { "Mark Hamill", "Harrison Ford" });
builder.ElemMatch(m => m.Actors, new[] { "Mark Hamill", "Harrison Ford" });

// if array has exactly n elements
builder.Size(m => m.Actors, 3);

Weitere Operatoren:

builders.Exists(m => m.Actors);
builders.Regex(m => m.Title, "^Star Wars");

Sort

var builder = Builders<Flower>.Sort;
var sort = Builders<Flower>.Sort
.Ascending(f => f.Price)
.Descending(f => f.Category);

Update

var builder = Builders<Flower>.Update;
var update = builder
.Set(f => f.SunRequirement, "Full sun")
.Unset(f => f.WaterRequirement)
.Mul(f => f.Price, 0.9);
MethodeMongoDB-OperatorBeschreibung
Set(m => m.Title, "New Title")$setsetzt Wert eines Feldes oder fügt Feld hinzu
Unset(m => m.Genre$unsetentfernt Feld
Inc(m => m.Views, 8)$incerhöht/verringert Wert eines numerischen Feldes
Mul(m => m.Price, 5)$mulmultipliziert Wert eines numerischen Feldes mit Faktor
Rename(m => m.Summary, "Plot")$renamebenennt vorhandenes Feld um
Push(m => m.Actors, "Emma Watson")$pushfügt Element zu Array-Feld hinzu oder erstellt Array
AddToSet(m => m.Actors, "Emma Watson")$addToSetfügt Element zu Array-Feld hinzu, falls es noch nicht existiert (verhindert Duplikate)
PopFirst(m => m.Actors)$pop (-1)entfernt erstes Element aus Array-Feld
PopLast(m => m.Actors)$pop (1)entfernt letztes Element aus Array-Feld
Pull(m => m.Actors, "Tom Hanks")$pullentfernt alle Instanzen eines angegebenen Wertes aus Array-Feld
PullAll(m => m.Actors, new [] {"Brad Pitt", "Angelina Jolie"})$pullAllentfernt alle Instanzen aller angegebenen Wertes aus Array-Feld
CurrentDate(m => m.LastUpdated)$currentDatesetzt Wert eines Feldes auf aktuelles Datum und Uhrzeit (Date/Timestamp)
Min(m => m.LowestPrice, 12)$minaktualisiert Wert eines Feldes nur, wenn neue Wert kleiner ist als der aktuelle Wert
Max(m => m.HighestRating, 9.5)$maxaktualisiert Wert eines Feldes nur, wenn neue Wert grösser ist als der aktuelle Wert
SetOnInsert(m => m.CreatedAt)$setOnInsertsetzt Wert eines Feldes nur, wenn Update-Operation ein neues Dokument einfügt (siehe hier)
Combine(update1, update2)C#-spezifischermöglicht Senden von mehreren Update-Operatoren in einziger Update-Anfrage an Datenbank
new EmptyPipelineDefinition<Movie>().AppendStage<Movie, Movie, Movie>("{ $set:{...}}");Update-Aggregations-Pipelinesermöglicht Verwendung von Aggregations-Pipelines innerhalt einer Update-Operation

Beispiele

SetOnInsert():

// set 'CreatedAt' only when inserting a new document (Upsert)
var filter = Builders<Movie>.Filter.Eq(m => m.Title, "Ein brandneuer Film");
var update = Builders<Movie>.Update
.Set(m => m.Year, 2024)
.SetOnInsert(m => m.CreatedAt, DateTime.UtcNow);
var options = new UpdateOptions { IsUpsert = true };
await _moviesCollection.UpdateOneAsync(filter, update, options);

Indexe

var index = Builders<Restaurant>.IndexKeys
.Ascending(r => r.Cuisine);
_restaurantsCollection.Indexes
.CreateOne(new CreateIndexModel<Restaurant>(index));