This library is representation of repository pattern in .net core for MongoDB using for internal need of company.
Based on MongoDb.Driver 2.12.4 and .net 5
- Package Manager:
Install-Package Unicreo.Framework.Db.MongoDB.Repository -Version 1.0.6
- .NET CLI:
dotnet add package Unicreo.Framework.Db.MongoDB.Repository --version 1.0.6
- PackageReference
<PackageReference Include="Unicreo.Framework.Db.MongoDB.Repository" Version="1.0.6" />
Use DI for config.
You have to implement ICollectionNameProvider
interface that has
to implement string GetCollectionName(Type entityType)
method which
allows to get name of collection in mongodb by type.
Also you have to set connection string to your mongodb.
And you should set logger;
Example:
services.AddTransient<ICollectionNameProvider, CollectionNameProvider>(); // You have to implement this interface by yourself
services.AddTransient<IMongoDbContext>(sp =>
{
string connectionString = "Your connection string";
ILogger<MongoDbContext> logger = sp.GetRequiredService<ILogger<MongoDbContext>>();
ICollectionNameProvider collectionNameProvider = sp.GetRequiredService<ICollectionNameProvider>;
return new MongoDbContext(connectionString, collectionNameProvider);
});
services.AddTransient<IMongoDbDataRepository, MongoDbDataRepository>();
All mongodb entities should be inherited from BaseEntity that contain ObjectId as Id.
public class ToDo: BaseEntity {
public string Name { get; set;}
public bool Done { get; set; } = false;
}
Use DI to provide MongoDataRepository
public class ToDoService {
private readonly IMongoDbDataRepository _dataRepository;
public ToDoService(IMongoDbDataRepository dataRepository) {
_dataRepository = dataRepository;
}
}
Initialize collections:
var collectionNames = new List<string> {"ToDos", "Users"}; // better way is to get names from CollectionNameProvider
await _dataRepository.Initialize(collectionNames);
Create entity:
var todo = new ToDo { Name = "buy smth"};
await _dataRepository.AddAsync(todo);
Create list:
var todoList = new List<ToDo> {
new ToDo {Name = "buy smth"},
new ToDo {Name = "sell smth"}
};
await _dataRepository.AddListAsync(todoList);
Get entity:
string id = ObjectId.GenerateNewId().ToString();
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id); //you should ensure that id is valid ObjectId
var todo = await _dataRepository.GetDocumentAsync<ToDo>(item => item.Id == ObjectId.Parse(id) && !item.Done);
Also in get methods you can use projection
var projection = Builders<ToDo>.Projection
.Include(entity => entity.Name)
.Exclude(entity => entity.Done);
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id, projection);
It works for GetList methods too.
GetList:
with pagination
int skip = 10;
int take = 10;
var todoList = await _dataRepository.GetListAsync(skip, take, entry => !entry.Done);
without pagination
var todoList = await _dataRepository.GetListAsync(entry => !entry.Done);
Update:
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id);
todo.Name = "New name";
await _dataRepository.UpdateAsync<ToDo>(todo);
Update one:
allow to update only one property
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id);
await _dataRepository.UpdateOneAsync(todo, entity => entity.Name, "new name");
or you can use builders
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id);
var update = Builders<ToDo>.Update.Set(entity => entity.Name, "new name");
await _dataRepository.UpdateOneAsync(todo, update);
Tip: with builders you can update multiple fields at once
var update = Builders<ToDo>.Update.Set(entity => entity.Name, "new name").Set(entity => entity.Title, "new title");
Update many:
takes documents you want to modify and updates the field in documents with the given value
await _dataRepository.UpdateManyAsync<ToDo, string>(todo => todo.Any(e => e.CreatedDate <= new DateTime.Now), todo => todo.Name, "new name");
this operation sets a new name to all created before todos
the same operation with builders
var update = Builders<ToDo>.Update.Set(entity => entity.Name, "new name");
await _dataRepository.UpdateManyAsync<ToDo>(todo => todo.Any(e => e.CreatedDate <= new DateTime.Now), update);
Tip: if your field is an array and you want to change only one item of this array, you can access the item by passing an index array[index] or use a positional operator - $. The positional $ operator acts as a placeholder for the first element that matches the query document. In C# Mongo.Driver positional operator is array[-1].
var update = Builders<ToDo>.Update.Set(entity => entity.Comments[-1].Message, "new message");
await _dataRepository.UpdateManyAsync<ToDo>(todo => todo.Comments.Any(comment => comment.authorId == userId), update);
this operation sets the message of the first comment with authorId equals userId in all todos
Delete:
var todo = await _dataRepository.GetDocumentAsync<ToDo>(id);
await _dataRepository.DeleteAsync(todo);
You also can use transactions
Methods you can use in transactions:
- AddAsync
- AddListAsync
- UpdateAsync
- UpdateOneAsync
- UpdateManyAsync
- DeleteAsync
Note: you should pass session object in every method that you want to use in transaction.
Example:
using (var session = _dataRepository.StartSession())
{
session.StartTransaction();
var todo1 = new ToDo {Name = "buy smth"};
var todo2 = new ToDo {Name = "sell smth"};
try {
await _dataRepository.AddAsync(todo1, session);
await _dataRepository.AddAsync(todo2, session);
await session.CommitTransactionAsync();
}
catch {
await session.AbortTransactionAsync();
}
}
For deeper usage of database you can use MongoDbContext that implements next interface:
interface IMongoDbContext
{
IMongoCollection<T> GetCollection<T>();
IMongoQueryable<T> GetQueryableCollection<T>();
IMongoDatabase Database { get; }
IMongoClient Client { get; }
Task CreateCollectionsAsync(IEnumerable<string> collectionNames);
}
Information about usage IMongoCollection, IMongoQueryable, IMongoDatabase, IMongoClient you can find here.