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#中的工作单元

不要太担心打开和关闭连接。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