-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Setup(Mongo): Integrate MongoDB into the project
- Loading branch information
1 parent
5243946
commit 1292c84
Showing
17 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
src/Shared/StellarChat.Shared.Abstractions/Pagination/IPagedQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace StellarChat.Shared.Abstractions.Pagination; | ||
|
||
public interface IPagedQuery | ||
{ | ||
int Page { get; set; } | ||
int PageSize { get; set; } | ||
} | ||
|
||
public interface IPagedQuery<T> : IPagedQuery { } |
36 changes: 36 additions & 0 deletions
36
src/Shared/StellarChat.Shared.Abstractions/Pagination/Paged.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace StellarChat.Shared.Abstractions.Pagination; | ||
|
||
public class Paged<T> : PagedBase | ||
{ | ||
public IReadOnlyList<T> Items { get; set; } = Array.Empty<T>(); | ||
public bool Empty => Items is null || !Items.Any(); | ||
public bool HasNextPage => CurrentPage * ResultsPerPage < TotalResults; | ||
public bool HasPreviousPage => CurrentPage > 1; | ||
|
||
public Paged() | ||
{ | ||
TotalPages = 1; | ||
} | ||
|
||
public Paged(IReadOnlyList<T> items, | ||
int currentPage, int resultsPerPage, | ||
int totalPages, long totalResults) : | ||
base(currentPage, resultsPerPage, totalPages, totalResults) | ||
{ | ||
Items = items; | ||
} | ||
|
||
public static Paged<T> Create(IReadOnlyList<T> items, | ||
int currentPage, int resultsPerPage, | ||
int totalPages, long totalResults) | ||
=> new(items, currentPage, resultsPerPage, totalPages, totalResults); | ||
|
||
public static Paged<T> From(PagedBase result, IReadOnlyList<T> items) | ||
=> new(items, result.CurrentPage, result.ResultsPerPage, | ||
result.TotalPages, result.TotalResults); | ||
|
||
public static Paged<T> AsEmpty => new(); | ||
|
||
public Paged<TResult> Map<TResult>(Func<T, TResult> map) | ||
=> Paged<TResult>.From(this, Items.Select(map).ToList()); | ||
} |
20 changes: 20 additions & 0 deletions
20
src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
namespace StellarChat.Shared.Abstractions.Pagination; | ||
|
||
public abstract class PagedBase | ||
{ | ||
public int CurrentPage { get; set; } | ||
public int ResultsPerPage { get; set; } | ||
public int TotalPages { get; set; } | ||
public long TotalResults { get; set; } | ||
|
||
protected PagedBase() { } | ||
|
||
protected PagedBase(int currentPage, int resultsPerPage, | ||
int totalPages, long totalResults) | ||
{ | ||
CurrentPage = currentPage; | ||
ResultsPerPage = resultsPerPage; | ||
TotalPages = totalPages; | ||
TotalResults = totalResults; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace StellarChat.Shared.Abstractions.Pagination; | ||
|
||
public abstract class PagedQuery : IPagedQuery | ||
{ | ||
public int Page { get; set; } = 0; | ||
public int PageSize { get; set; } | ||
//public string OrderBy { get; set; } = string.Empty; | ||
//public string SortOrder { get; set; } = string.Empty; | ||
} | ||
|
||
public abstract class PagedQuery<T> : PagedQuery, IPagedQuery<Paged<T>> { } |
76 changes: 76 additions & 0 deletions
76
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Extensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using Microsoft.Extensions.Configuration; | ||
using MongoDB.Bson.Serialization.Conventions; | ||
using MongoDB.Bson.Serialization.Serializers; | ||
using MongoDB.Bson.Serialization; | ||
using MongoDB.Bson; | ||
using MongoDB.Driver; | ||
using StellarChat.Shared.Infrastructure.DAL.Mongo.Factories; | ||
using StellarChat.Shared.Infrastructure.DAL.Mongo.Seeders; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using StellarChat.Shared.Infrastructure.DAL.Mongo.Repositories; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public static class Extensions | ||
{ | ||
public static IServiceCollection AddMongo(this IServiceCollection services, IConfiguration configuration, Type? seederType = null) | ||
{ | ||
var section = configuration.GetSection("mongo"); | ||
var options = section.BindOptions<MongoOptions>(); | ||
services.Configure<MongoOptions>(section); | ||
|
||
if (!section.Exists()) | ||
{ | ||
return services; | ||
} | ||
|
||
var mongoClient = new MongoClient(options.ConnectionString); | ||
var database = mongoClient.GetDatabase(options.Database); | ||
services.AddSingleton<IMongoClient>(mongoClient); | ||
services.AddSingleton(database); | ||
|
||
services.AddTransient<IMongoSessionFactory, MongoSessionFactory>(); | ||
|
||
if (seederType is null) | ||
{ | ||
services.AddTransient<IMongoDbSeeder, MongoDbSeeder>(); | ||
|
||
using var scope = services.BuildServiceProvider().CreateScope(); | ||
var seeder = scope.ServiceProvider.GetRequiredService<IMongoDbSeeder>(); | ||
seeder.SeedAsync(database).GetAwaiter().GetResult(); | ||
} | ||
else | ||
{ | ||
services.AddTransient(typeof(IMongoDbSeeder), seederType); | ||
} | ||
|
||
RegisterConventions(); | ||
|
||
return services; | ||
} | ||
|
||
public static IServiceCollection AddMongoRepository<TEntity, TIdentifiable>(this IServiceCollection services, string collectionName) | ||
where TEntity : IIdentifiable<TIdentifiable> | ||
{ | ||
services.AddTransient<IMongoRepository<TEntity, TIdentifiable>>(sp => | ||
{ | ||
var database = sp.GetRequiredService<IMongoDatabase>(); | ||
return new MongoRepository<TEntity, TIdentifiable>(database, collectionName); | ||
}); | ||
|
||
return services; | ||
} | ||
|
||
private static void RegisterConventions() | ||
{ | ||
BsonSerializer.RegisterSerializer(typeof(decimal), new DecimalSerializer(BsonType.Decimal128)); | ||
BsonSerializer.RegisterSerializer(typeof(decimal?), | ||
new NullableSerializer<decimal>(new DecimalSerializer(BsonType.Decimal128))); | ||
ConventionRegistry.Register("mongo-conventions", new ConventionPack | ||
{ | ||
new CamelCaseElementNameConvention(), | ||
new IgnoreExtraElementsConvention(true), | ||
new EnumRepresentationConvention(BsonType.String), | ||
}, _ => true); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Factories/MongoSessionFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo.Factories; | ||
|
||
internal sealed class MongoSessionFactory : IMongoSessionFactory | ||
{ | ||
private readonly IMongoClient _client; | ||
|
||
public MongoSessionFactory(IMongoClient client) | ||
=> _client = client; | ||
|
||
public Task<IClientSessionHandle> CreateAsync() | ||
=> _client.StartSessionAsync(); | ||
} |
8 changes: 8 additions & 0 deletions
8
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IAppSettingsSeeder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public interface IAppSettingsSeeder | ||
{ | ||
Task SeedAsync(IMongoDatabase database); | ||
} |
6 changes: 6 additions & 0 deletions
6
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IIdentifiable.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public interface IIdentifiable<out T> | ||
{ | ||
T Id { get; } | ||
} |
9 changes: 9 additions & 0 deletions
9
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoDbSeeder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo | ||
{ | ||
public interface IMongoDbSeeder | ||
{ | ||
Task SeedAsync(IMongoDatabase database); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using MongoDB.Driver; | ||
using StellarChat.Shared.Abstractions.Pagination; | ||
using System.Linq.Expressions; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public interface IMongoRepository<TDocument, in TIdentifiable> where TDocument : IIdentifiable<TIdentifiable> | ||
{ | ||
IMongoCollection<TDocument> Collection { get; } | ||
IQueryable<TDocument> AsQueryable(); | ||
Task<TDocument> GetAsync(TIdentifiable id); | ||
Task<TDocument> GetAsync(Expression<Func<TDocument, bool>> predicate); | ||
Task<IReadOnlyList<TDocument>> FindAsync(Expression<Func<TDocument, bool>> predicate); | ||
IEnumerable<TProjected> FilterBy<TProjected>(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, TProjected>> projectionExpression); | ||
Task<Paged<TDocument>> BrowseAsync<TQuery>(Expression<Func<TDocument, bool>> predicate, TQuery query) where TQuery : IPagedQuery; | ||
Task AddAsync(TDocument document); | ||
Task AddManyAsync(ICollection<TDocument> documents); | ||
Task UpdateAsync(TDocument document); | ||
Task UpdateAsync(TDocument document, Expression<Func<TDocument, bool>> predicate); | ||
Task DeleteAsync(TIdentifiable id); | ||
Task DeleteAsync(Expression<Func<TDocument, bool>> predicate); | ||
Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression); | ||
Task<bool> ExistsAsync(Expression<Func<TDocument, bool>> predicate); | ||
} |
8 changes: 8 additions & 0 deletions
8
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoSessionFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public interface IMongoSessionFactory | ||
{ | ||
Task<IClientSessionHandle> CreateAsync(); | ||
} |
8 changes: 8 additions & 0 deletions
8
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/MongoOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
internal class MongoOptions | ||
{ | ||
public string ConnectionString { get; set; } = null!; | ||
public string Database { get; set; } = null!; | ||
public bool DisableTransactions { get; set; } | ||
} |
56 changes: 56 additions & 0 deletions
56
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Pagination.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using MongoDB.Driver; | ||
using MongoDB.Driver.Linq; | ||
using StellarChat.Shared.Abstractions.Pagination; | ||
using StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo; | ||
|
||
public static class Pagination | ||
{ | ||
public static async Task<Paged<T>> PaginateAsync<T>(this IMongoQueryable<T> collection, IPagedQuery query) | ||
=> await collection.PaginateAsync(query.Page, query.PageSize); | ||
|
||
public static async Task<Paged<T>> PaginateAsync<T>(this IMongoQueryable<T> collection, | ||
int page, int resultsPerPage) | ||
{ | ||
if (page <= 0) | ||
{ | ||
page = 1; | ||
} | ||
if (resultsPerPage <= 0) | ||
{ | ||
resultsPerPage = 10; | ||
} | ||
var isEmpty = await collection.AnyAsync() == false; | ||
if (isEmpty) | ||
{ | ||
return Paged<T>.AsEmpty; | ||
} | ||
var totalResults = await collection.CountAsync(); | ||
var totalPages = (int)Math.Ceiling((decimal)totalResults / resultsPerPage); | ||
var data = await collection.Limit(page, resultsPerPage).ToListAsync(); | ||
|
||
return Paged<T>.Create(data, page, resultsPerPage, totalPages, totalResults); | ||
} | ||
|
||
public static IMongoQueryable<T> Limit<T>(this IMongoQueryable<T> collection, IPagedQuery query) | ||
=> collection.Limit(query.Page, query.PageSize); | ||
|
||
public static IMongoQueryable<T> Limit<T>(this IMongoQueryable<T> collection, | ||
int page, int resultsPerPage) | ||
{ | ||
if (page <= 0) | ||
{ | ||
page = 1; | ||
} | ||
if (resultsPerPage <= 0) | ||
{ | ||
resultsPerPage = 10; | ||
} | ||
var skip = (page - 1) * resultsPerPage; | ||
var data = collection.Skip(skip) | ||
.Take(resultsPerPage); | ||
|
||
return data; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Repositories/MongoRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System.Linq.Expressions; | ||
using MongoDB.Driver; | ||
using MongoDB.Driver.Linq; | ||
using StellarChat.Shared.Abstractions.Pagination; | ||
|
||
namespace StellarChat.Shared.Infrastructure.DAL.Mongo.Repositories; | ||
|
||
internal class MongoRepository<TDocument, TIdentifiable> : IMongoRepository<TDocument, TIdentifiable> | ||
where TDocument : IIdentifiable<TIdentifiable> | ||
{ | ||
public MongoRepository(IMongoDatabase database, string collectionName) | ||
{ | ||
Collection = database.GetCollection<TDocument>(collectionName); | ||
} | ||
|
||
public IMongoCollection<TDocument> Collection { get; } | ||
|
||
public IQueryable<TDocument> AsQueryable() => Collection.AsQueryable(); | ||
|
||
public Task<TDocument> GetAsync(TIdentifiable id) | ||
=> GetAsync(e => e.Id.Equals(id)); | ||
|
||
public Task<TDocument> GetAsync(Expression<Func<TDocument, bool>> predicate) | ||
=> Collection.Find(predicate).SingleOrDefaultAsync(); | ||
|
||
public async Task<IReadOnlyList<TDocument>> FindAsync(Expression<Func<TDocument, bool>> predicate) | ||
=> await Collection.Find(predicate).ToListAsync(); | ||
|
||
public IEnumerable<TProjected> FilterBy<TProjected>(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, TProjected>> projectionExpression) | ||
=> Collection.Find(filterExpression).Project(projectionExpression).ToEnumerable(); | ||
|
||
public Task<Paged<TDocument>> BrowseAsync<TQuery>(Expression<Func<TDocument, bool>> predicate, | ||
TQuery query) where TQuery : IPagedQuery | ||
=> Collection.AsQueryable().Where(predicate).PaginateAsync(query); | ||
|
||
public Task AddAsync(TDocument document) | ||
=> Collection.InsertOneAsync(document); | ||
|
||
public async Task AddManyAsync(ICollection<TDocument> documents) | ||
=> await Collection.InsertManyAsync(documents); | ||
|
||
public Task UpdateAsync(TDocument document) | ||
=> UpdateAsync(document, e => e.Id.Equals(document.Id)); | ||
|
||
public Task UpdateAsync(TDocument document, Expression<Func<TDocument, bool>> predicate) | ||
=> Collection.ReplaceOneAsync(predicate, document); | ||
|
||
public Task DeleteAsync(TIdentifiable id) | ||
=> DeleteAsync(e => e.Id.Equals(id)); | ||
|
||
public Task DeleteAsync(Expression<Func<TDocument, bool>> predicate) | ||
=> Collection.DeleteOneAsync(predicate); | ||
|
||
public Task<bool> ExistsAsync(Expression<Func<TDocument, bool>> predicate) | ||
=> Collection.Find(predicate).AnyAsync(); | ||
|
||
public async Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression) | ||
=> await Collection.DeleteManyAsync(filterExpression); | ||
} |
Oops, something went wrong.