繁体   English   中英

如何使用.net驱动程序2.7.3将新文档同时插入到MongoDB 4中的集合中

[英]how to concurrently insert a new document into a collection in MongoDB 4 with .net driver 2.7.3

我正在使用MongoDB 4和MongoDB .net驱动程序2.7.3。 我想同时在一个集合中插入一个新文档,以便该集合中应该只有一个文档。 将文档插入到集合中之后(即,将sequenceValue设置为1),我们只需要更新文档(即,将sequenceValue增加1),而不用再插入任何新文档。

在这个名为“ countersCollection”的集合中,新文档的结构是一个Counter类,例如:

    public class Counter
    {
        [BsonId]
        public ObjectId _id { get; set; }
        public string name { get; set; }
        public long sequenceValue {get;set;}
        public DateTime date {get;set;}
    }

我的代码是这样的:

    Counter c = await this.countersCollection.AsQueryable().FirstOrDefaultAsync(x => x.name == counterName);                
    DateTime utcNow = DateTime.UtcNow;                                                                            
    if (c == null) // empty document
    {
        var options = new FindOneAndUpdateOptions<Counter, Counter>() {ReturnDocument = ReturnDocument.After, IsUpsert = true };            
        var filter = new FilterDefinitionBuilder<Counter>().Where(x => x.name == counterName);
        var update = new UpdateDefinitionBuilder<Counter>().Set(x => x.sequenceValue, 1).Set(x => x.date, utcNow);                                        
        seq = await this.countersCollection.FindOneAndUpdateAsync<Counter>(filter, update, options);                    
    }

上面的代码在非并发环境中运行良好,但不能同时运行。 如果多个线程同时调用上述代码,则可能在countersCollection中创建多个计数器文档。

有什么办法可以使其同时工作。

谢谢。

您可以使用乐观并发方法来处理

算法可以像:

  1. 你发现Countername == counterName
  2. var filter = new FilterDefinitionBuilder<Counter>().Where(x => x.name == counterName && x.sequenceValue == c.sequenceValue );一样构建您的过滤var filter = new FilterDefinitionBuilder<Counter>().Where(x => x.name == counterName && x.sequenceValue == c.sequenceValue );
  3. 尝试查找和更新,如果结果为null重试

好。 让我们写一些代码。

  1. 为OptimisticConcurrency创建单独的异常

     public class OptimisticConcurrencyException : Exception { } 
  2. 创建Counter

     public class Counter { [BsonId] public ObjectId Id { get; set; } public string Name { get; set; } public long Version { get; set; } public DateTime Ddate { get; set; } } 
  3. 创建一些简单的重试逻辑

      public class CounterRepository { private readonly IMongoCollection<Counter> _countersCollection; public CounterRepository(IMongoCollection<Counter> countersCollection) { _countersCollection = countersCollection ?? throw new ArgumentNullException(nameof(countersCollection)); } public async Task<Counter> TryInsert(Counter counter) { var policy = Policy.Handle<OptimisticConcurrencyException>() .WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(7) }); return await policy.ExecuteAsync(() => TryInsertInternal(counter)); } private async Task<Counter> TryInsertInternal(Counter counter) { var existingCounter = await _countersCollection.Find(c => c.Id == counter.Id).FirstOrDefaultAsync(); if (existingCounter == null) return await InsertInternal(counter); return await IncreaseVersion(existingCounter); } private async Task<Counter> InsertInternal(Counter counter) { try { counter.Version = 1; await _countersCollection.InsertOneAsync(counter); return counter; } // if smbd insert value after you called Find(returns null at that moment) // you try to insert entity with the same id and exception will be thrown // you try to handle it by marking with optimistic concurrency and retry policy // wait some time and execute the method and Find returns not null now so that // you will not insert new record but just increase the version catch (MongoException) { throw new OptimisticConcurrencyException(); } } private async Task<Counter> IncreaseVersion(Counter existing) { long versionSnapshot = existing.Version; long nextVersion = versionSnapshot + 1; var updatedCounter = await _countersCollection.FindOneAndUpdateAsync( c => c.Id == existing.Id && c.Version == versionSnapshot, new UpdateDefinitionBuilder<Counter>().Set(c => c.Version, nextVersion)); // it can be null if smbd increased existing version that you fetched from db // so you data is now the newest one and you throw OptimisticConcurrencyException if (updatedCounter == null) throw new OptimisticConcurrencyException(); return updatedCounter; } } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM