mongodb和c#中的工作单元
本文关键字:工作 单元 mongodb | 更新日期: 2023-09-27 18:06:37
我知道MongoDB不应该支持工作单元等。但是我认为最好实现一个存储库,它只存储意图(类似于标准),然后将它们提交给DB。否则,在存储库中的每个方法中,您都必须创建到DB的连接,然后关闭它。如果我们将到DB的连接放在某个BaseRepository类中,那么我们就将我们的存储库绑定到具体的DB上,并且很难测试存储库,测试解析存储库的IoC。
在MongoDB创建会话是一个坏主意吗?是否有一种方法可以将连接逻辑与存储库分离?
下面是Rob Conery编写的一些代码。它是一个好主意,总是连接到您的数据库在每个请求?什么是最佳实践?还有件事。假设我想为一个集合提供一个索引。以前我在构造函数中做过,但是用Rob的方法,在那里做似乎不合逻辑。
using Norm;
using Norm.Responses;
using Norm.Collections;
using Norm.Linq;
public class MongoSession {
private string _connectionString;
public MongoSession() {
//set this connection as you need. This is left here as an example, but you could, if you wanted,
_connectionString = "mongodb://127.0.0.1/MyDatabase?strict=false";
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
//not efficient, NoRM should do this in a way that sends a single command to MongoDB.
var items = All<T>().Where(expression);
foreach (T item in items) {
Delete(item);
}
}
public void Delete<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.GetCollection<T>().Delete(item);
}
}
public void DeleteAll<T>() where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.DropCollection(typeof(T).Name);
}
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
T retval = default(T);
using(var db = Mongo.Create(_connectionString))
{
retval = db.GetCollection<T>().AsQueryable()
.Where(expression).SingleOrDefault();
}
return retval;
}
public IQueryable<T> All<T>() where T : class, new() {
//don't keep this longer than you need it.
var db = Mongo.Create(_connectionString);
return db.GetCollection<T>().AsQueryable();
}
public void Add<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(item);
}
}
public void Add<T>(IEnumerable<T> items) where T : class, new() {
//this is WAY faster than doing single inserts.
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(items);
}
}
public void Update<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().UpdateOne(item, item);
}
}
//this is just some sugar if you need it.
public T MapReduce<T>(string map, string reduce) {
T result = default(T);
using(var db = Mongo.Create(_connectionString))
{
var mr = db.Database.CreateMapReduce();
MapReduceResponse response =
mr.Execute(new MapReduceOptions(typeof(T).Name) {
Map = map,
Reduce = reduce
});
MongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>();
MapReduceResult<T> r = coll.Find().FirstOrDefault();
result = r.Value;
}
return result;
}
public void Dispose() {
_server.Dispose();
}
}
不要太担心打开和关闭连接。MongoDB c#驱动程序维护一个内部连接池,因此您不会在每次创建新的MongoServer
对象时打开和关闭实际连接的开销。
你可以创建一个存储库接口来公开你的数据逻辑,并构建一个MongoDB实现,在需要的地方注入。这样,特定于MongoDB的连接代码就从应用程序中抽象出来了,应用程序只能看到IRepository。
要小心尝试用MongoDB实现工作单元类型模式。不像SQL Server,你不能在一个事务中登记多个查询,如果一个失败可以回滚。
对于一个存储库模式的简单示例,其中包含MongoDB, SQL Server和JSON实现,请查看NBlog存储代码。它使用Autofac IoC将具体的存储库注入到ASP中。. NET MVC应用程序在研究设计模式时,我正在为。net Core和MongoDB创建一个基本的存储库模式。在阅读MongoDB文档时,我看到了一篇关于MongoDB事务的文章。文章中明确指出:
从4.0版本开始,MongoDB提供了执行的能力基于副本集的多文档事务。
环顾管道,我遇到了一个库,它确实很好地实现了MongoDB的工作单元模式。
如果您对类似于Rob Connery和NBlog存储代码的实现感兴趣,但使用mongodb csharp驱动程序2.0(即异步),您可以查看:
https://github.com/alexandre-spieser/mongodb-generic-repository你可以从BaseMongoRepository继承一个自定义存储库。
public interface ITestRepository : IBaseMongoRepository
{
void DropTestCollection<TDocument>();
void DropTestCollection<TDocument>(string partitionKey);
}
public class TestRepository : BaseMongoRepository, ITestRepository
{
public TestRepository(string connectionString, string databaseName) : base(connectionString, databaseName)
{
}
public void DropTestCollection<TDocument>()
{
MongoDbContext.DropCollection<TDocument>();
}
public void DropTestCollection<TDocument>(string partitionKey)
{
MongoDbContext.DropCollection<TDocument>(partitionKey);
}
}
简介
可以使用这个nuget-package UnitOfWork.MongoDb。这是MongoDb.Driver
的包装器,具有一些有用的功能和特性。您还可以找到示例代码和视频(ru)。
读取连接设置
// read MongoDb settings from appSettings.json
services.AddUnitOfWork(configuration.GetSection(nameof(DatabaseSettings)));
// --- OR ----
// use hardcoded
services.AddUnitOfWork(config =>
{
config.Credential = new CredentialSettings { Login = "sa", Password = "password" };
config.DatabaseName = "MyDatabase";
config.Hosts = new[] { "Localhost" };
config.MongoDbPort = 27017;
config.VerboseLogging = false;
});
注射namespace WebApplicationWithMongo.Pages
{
public class IndexModel : PageModel
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<IndexModel> _logger;
public IndexModel(IUnitOfWork unitOfWork, ILogger<IndexModel> logger)
{
_unitOfWork = unitOfWork;
_logger = logger;
}
public IPagedList<Order>? Data { get; set; }
}
}
注入后可以得到repository。
得到库
public async Task<IActionResult> OnGetAsync(int pageIndex = 0, int pageSize = 10)
{
var repository = _unitOfWork.GetRepository<Order, int>();
Data = await repository.GetPagedAsync(pageIndex, pageSize, FilterDefinition<Order>.Empty, HttpContext.RequestAborted);
return Page();
}
GetPagedAsync
是一些有用的实现之一
如果您需要ACID操作(事务),您可以使用IUnitOfWork
这样的东西。(复制集应该正确设置)。例如:
await unitOfWork.UseTransactionAsync<OrderBase, int>(ProcessDataInTransactionAsync1, HttpContext.RequestAborted, session);
方法ProcessDataInTransactionAsync1
可以像这样:
async Task ProcessDataInTransactionAsync1(IRepository<OrderBase, int> repositoryInTransaction, IClientSessionHandle session, CancellationToken cancellationToken)
{
await repository.Collection.DeleteManyAsync(session, FilterDefinition<OrderBase>.Empty, null, cancellationToken);
var internalOrder1 = DocumentHelper.GetInternal(99);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder1, null, cancellationToken);
logger!.LogInformation("InsertOne: {item1}", internalOrder1);
var internalOrder2 = DocumentHelper.GetInternal(100);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder2, null, cancellationToken);
logger!.LogInformation("InsertOne: {item2}", internalOrder2);
var filter = Builders<OrderBase>.Filter.Eq(x => x.Id, 99);
var updateDefinition = Builders<OrderBase>.Update.Set(x => x.Description, "Updated description");
var result = await repositoryInTransaction.Collection
.UpdateOneAsync(session, filter, updateDefinition, new UpdateOptions { IsUpsert = false }, cancellationToken);
if (result.IsModifiedCountAvailable)
{
logger!.LogInformation("Update {}", result.ModifiedCount);
}
throw new ApplicationException("EXCEPTION! BANG!");
}
这个nuget是开源的Calabonga.UnitOfWork.MongoDb