It's a modern and generic data access structure for .NET and MongoDB. It supports UnitOfWork, Repository and QueryBuilder patterns. It also includes DbContext, IdGenerators and transactions support with replica set.
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
MongoDB.DataAccess is available on Nuget.
Install-Package MongoDB.Data.Infrastructure.Abstractions -Version 1.8.5
Install-Package MongoDB.Data.QueryBuilder.Abstractions -Version 1.8.5
Install-Package MongoDB.Data.Repository.Abstractions -Version 1.8.5
Install-Package MongoDB.Data.UnitOfWork.Abstractions -Version 1.8.5
Install-Package MongoDB.Data.Generators -Version 1.8.5
Install-Package MongoDB.Data.Infrastructure -Version 1.8.5
Install-Package MongoDB.Data.QueryBuilder -Version 1.8.5
Install-Package MongoDB.Data.Repository -Version 1.8.5
Install-Package MongoDB.Data.UnitOfWork -Version 1.8.5
P.S.: MongoDB.Data.UnitOfWork depends on the other packages, so installing this package is enough.
First of all, please register the dependencies into the MS Built-In container:
publicclassBloggingContext:MongoDbContext{publicBloggingContext(IMongoClientclient,IMongoDatabasedatabase,IMongoDbContextOptionsoptions):base(client,database,options){}}// Register the DbContextservices.AddMongoDbContext<IMongoDbContext,BloggingContext>(connectionString:Configuration.GetValue<string>("MongoSettings:Blogging:ConnectionString"),databaseName:Configuration.GetValue<string>("MongoSettings:Blogging:DatabaseName"),setupFluentConfigurationOptions: options =>options.ScanningAssemblies=new[]{typeof(BloggingContext).Assembly});// Register the UnitOfWorkservices.AddMongoDbUnitOfWork<BloggingContext>();For multiple databases:
publicclassBloggingContext:MongoDbContext{publicBloggingContext(IMongoClientclient,IMongoDatabasedatabase,IMongoDbContextOptionsoptions):base(client,database,options){}}publicclassAccountingContext:MongoDbContext{publicAccountingContext(IMongoClientclient,IMongoDatabasedatabase,IMongoDbContextOptionsoptions):base(client,database,options){}}// Register the DbContextsservices.AddMongoDbContext<IMongoDbContext,BloggingContext>(connectionString:Configuration.GetValue<string>("MongoSettings:Blogging:ConnectionString"),databaseName:Configuration.GetValue<string>("MongoSettings:Blogging:DatabaseName"),setupFluentConfigurationOptions: options =>options.ScanningAssemblies=new[]{typeof(BloggingContext).Assembly});services.AddMongoDbContext<IMongoDbContext,AccountingContext>(connectionString:Configuration.GetValue<string>("MongoSettings:Accounting:ConnectionString"),databaseName:Configuration.GetValue<string>("MongoSettings:Accounting:DatabaseName"),setupFluentConfigurationOptions: options =>options.ScanningAssemblies=new[]{typeof(AccountingContext).Assembly});// Register the UnitOfWorkservices.AddMongoDbUnitOfWork<BloggingContext>();services.AddMongoDbUnitOfWork<AccountingContext>();For multi-tenancy:
P.S: There are many approaches to implementing multi-tenancy in applications (Discriminator, Database per tenant, Schema per tenant...) and this one is just an example.
publicclassBloggingContext:MongoDbContext{publicBloggingContext(IMongoClientclient,IMongoDatabasedatabase,IMongoDbContextOptionsoptions):base(client,database,options){}}// Register the DbContextsservices.AddMongoDbContext<IMongoDbContext,BloggingContext>(connectionString:Configuration.GetValue<string>("MongoSettings:Blogging:TenantA:ConnectionString"),databaseName:Configuration.GetValue<string>("MongoSettings:Blogging:TenantA:DatabaseName"),setupDbContextOptions: options =>options.DbContextId=$"{nameof(BloggingContext)} - TenantA",setupFluentConfigurationOptions: options =>options.ScanningAssemblies=new[]{typeof(BloggingContext).Assembly});services.AddMongoDbContext<IMongoDbContext,BloggingContext>(connectionString:Configuration.GetValue<string>("MongoSettings:Blogging:TenantB:ConnectionString"),databaseName:Configuration.GetValue<string>("MongoSettings:Blogging:TenantB:DatabaseName"),setupDbContextOptions: options =>options.DbContextId=$"{nameof(BloggingContext)} - TenantB",setupFluentConfigurationOptions: options =>options.ScanningAssemblies=new[]{typeof(BloggingContext).Assembly});// Register the UnitOfWorkservices.AddMongoDbUnitOfWork<BloggingContext>();// Orservices.AddMongoDbUnitOfWork<BloggingContext>("BloggingContext - TenantA");services.AddMongoDbUnitOfWork<BloggingContext>("BloggingContext - TenantB");After that, use the structure in your code like that:
privatereadonlyIMongoDbUnitOfWork<BloggingContext>_unitOfWork;// InjectionpublicBlogsController(IMongoDbUnitOfWork<BloggingContext> unitOfWork)=> _unitOfWork =unitOfWork??thrownewArgumentNullException(nameof(unitOfWork),$"{nameof(unitOfWork)} cannot be null.");// Or// Factory for multi-tenancypublicBlogsController(IMongoDbUnitOfWorkFactory<BloggingContext> unitOfWorkFactory,ITenantProvider tenantProvider){if(unitOfWorkFactoryisnull){thrownewArgumentNullException(nameof(unitOfWorkFactory),$"{nameof(unitOfWorkFactory)} cannot be null.");}if(tenantProviderisnull){thrownewArgumentNullException(nameof(tenantProvider),$"{nameof(tenantProvider)} cannot be null.");}vartenantId=tenantProvider.GetTenantId();_unitOfWork=unitOfWorkFactory.Create(tenantId);}// Or// If the TenantId is known and constantpublicBlogsController([FromKeyedServices("BloggingContext - TenantA")]IMongoDbUnitOfWork<BloggingContext> unitOfWork)=>_unitOfWork=unitOfWork??thrownewArgumentNullException(nameof(unitOfWork),$"{nameof(unitOfWork)} cannot be null.");publicvoidGetAllBlogs(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery();varblogs=repository.Search(query);}publicvoidGetAllBlogsProjection(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery().Select(selector =>new{Name=selector.Title,Link=selector.Url,Type=selector.Type.Description});varblogs=repository.Search(query);}publicvoidGetAllOrderedBlogs(){varrepository=_unitOfWork.Repository<Blog>();IMongoDbQuery<Blog>query;IList<Blog>blogs;query=repository.MultipleResultQuery().OrderByDescending("Id");blogs=repository.Search(query);query=repository.MultipleResultQuery().OrderByDescending(blog =>blog.Id);blogs=repository.Search(query);}publicvoidGetTopBlogs(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery().Top(10);varblogs=repository.Search(query);}publicvoidGetPagedBlogs(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery().Page(1,20);varblogs=repository.Search(query);}publicvoidGetBlogsPagedList(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery().Page(1,20);varblogs=repository.Search(query).ToPagedList(query.Paging.PageIndex,query.Paging.PageSize,query.Paging.TotalCount);}publicvoidGetFilteredBlogs(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.MultipleResultQuery().AndFilter(blog =>blog.Url.StartsWith("/a/")).AndFilter(blog =>blog.Title.StartsWith("a")).AndFilter(blog =>blog.Posts.Any());varblogs=repository.Search(query);}publicvoidGetUrls(){varrepository=_unitOfWork.CustomRepository<ICustomBlogRepository>();varurls=repository.GetAllBlogUrls();}publicvoidGetBlogByUrl(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.SingleResultQuery().AndFilter(blog =>blog.Url.StartsWith("/a/")).OrderByDescending(blog =>blog.Id);varblogResult=repository.FirstOrDefault(query);}publicvoidGetBlogById(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.SingleResultQuery().AndFilter(blog =>blog.Id==1);varblogResult=repository.SingleOrDefault(query);}publicvoidGetBlogByIdProjection(){varrepository=_unitOfWork.Repository<Blog>();varquery=repository.SingleResultQuery().AndFilter(blog =>blog.Id==1).Select(selector =>new{selector.Id,Name=selector.Title,Link=selector.Url,Type=selector.Type.Description});varblogResult=repository.SingleOrDefault(query);}publicvoidExistsBlog(){varrepository=_unitOfWork.Repository<Blog>();varexists=repository.Any(blog =>blog.Url.StartsWith("/a/"));}publicvoidGetBlogCount(){varrepository=_unitOfWork.Repository<Blog>();varcount=repository.Count();varlongCount=repository.LongCount();}publicvoidMaxBlogId(){varrepository=_unitOfWork.Repository<Blog>();varid=repository.Max(blog =>blog.Id);}publicvoidMinBlogId(){varrepository=_unitOfWork.Repository<Blog>();varid=repository.Min(blog =>blog.Id);}publicvoidAddBlog(){varrepository=_unitOfWork.Repository<Blog>();repository.InsertOne(Seeder.SeedBlog(51));_unitOfWork.SaveChanges();}publicvoidUpdateBlog(){varrepository=_unitOfWork.Repository<Blog>();repository.ReplaceOne(x =>x.Id==id,model);_unitOfWork.SaveChanges();}publicvoidDeleteBlog(){varrepository=_unitOfWork.Repository<Blog>();repository.DeleteOne(x =>x.Id==id);_unitOfWork.SaveChanges();}publicvoidUpdateManyBlogs(){varrepository=_unitOfWork.Repository<Blog>();repository.UpdateMany(
blog =>blogIds.Contains(blog.Id),newDictionary<Expression<Func<Blog,object>>,object>{{ blog =>blog.Title,"updated-title"}});_unitOfWork.SaveChanges();}publicvoidBulkWriteBlogs(){varrepository=_unitOfWork.Repository<Blog>();varrequests=newList<WriteModel<Blog>>();foreach(varblogIdinblogIds){varfilter=Builders<Blog>.Filter.Eq(blog =>blog.Id,blogId);vardefinition=Builders<Blog>.Update.Set(blog =>blog.Title,$"a{blogId}-updated-title");requests.Add(newUpdateOneModel<Blog>(filter,definition));}repository.BulkWrite(requests);_unitOfWork.SaveChanges();}The operations above are also available as async.
Please check some available samples here
If you want to help with the project, feel free to open pull requests and submit issues.
If you would like to show your support for this project, then please feel free to buy me a coffee.
