MongoDb c#驱动程序在一个原子操作中更新多个文档

时间:2013-10-14 10:09:15

标签: c# mongodb mongodb-.net-driver

我想问你是否会在一个原子操作中执行以下代码。我正在使用mongodb c#driver。

方法的输入是我想要更新的对象的id列表。

public void Update(IEnumerable<string> ids)
{
    var query = Query<T>.Where(t => ids.Contains(t.Id));
    var update = Update.Set("Modified", DateTime.Now); //this is just example of update

    var options = new MongoUpdateOptions {Flags = UpdateFlags.Multi};

    Collection.Update(query, update, options);
}

当我有数百万份文件需要更新时,我很有兴趣。如果在此更新期间出现故障(电源或硬件问题),会发生什么?数据库是否处于一致状态?

感谢。

2 个答案:

答案 0 :(得分:0)

MongoDB不支持事务原子多文档。 MongoDB仅对一个文档执行原子操作。

您可以在the documentation of Mongodb

中查看此内容

因此,如果您在此操作期间使用查询更新1000个文档并且服务器崩溃,则某些文档可能会更新,而其他文档则不会更新。

答案 1 :(得分:0)

要求

MongoDB> = 4.0 C#驱动程序> = 2.7 我是怎么做到的...

TL; DR

转到“示例代码”


会话

在我可以访问我的客户端(IMongoClient)的DbContext类中,我定义了会话:

public IClientSessionHandle StartSession()
{
    return _client.StartSession(new ClientSessionOptions());
}

public async Task<IClientSessionHandle> StartSessionAsync()
{
    return await _client.StartSessionAsync(new ClientSessionOptions());
}

正如文档所述,如果仅这些事务接连进行,它们便能够一次又一次地执行多个事务

  

会话用于将一系列操作分组在一起   彼此相关,应该在同一会话中执行   选项。会话也用于交易。

这些会议说,应在您结束操作后立即关闭它们。

所以您实际上应该像下面这样编写它,或者根据您的情况执行手动处理:

// db is what i named my context, where i defined all my collections and database related stuffs. 
// if you have direct access to client, you can call `StartSession/Async` exactly over the `client` object
using(var session = _db.StartSessionAsync()) {
    //... All transactional code will go here
} 
// Here, on ending the block, Will automatically call `Dispose` method, and the object will no longer exists

session对象上

交易

  

使用以下方法启动,落实或中止交易   IClientSession。一个会话一次只能执行一个事务,   但是一个会话可以执行多个事务,只要每个事务   事务在下一个事务开始之前已提交或中止。

在此步骤中,您需要先启动事务,然后才能对数据库进行实际更改...

session.StartTransaction();

会话开始后,您应该执行交易,并在最后...

如果处理成功,则应致电:

session.CommitTransaction();

否则,您需要回滚

session.AbortTransaction();

示例代码

如您所见,我在mongodb上有两个写操作,另一个对我来说很关键的外部过程,我需要将这三个一起执行,..前两个由事务管理,并且只要第三个不会引发异常,数据库应保持其新状态。

bool error;
using (var session = await _db.StartSessionAsync())
{
    session.StartTransaction();

    try
    {
        var deletedImage = _db.GetUserStore<ApplicationUser>().CollectionInstance.UpdateOneAsync(
            Builders<ApplicationUser>.Filter.Where(w => w.Id == userId),
            Builders<ApplicationUser>.Update.Pull(x => x.ProfilePictures, photoFromRepo));

        await _db.ProfilePicture().DeleteAsync(new ObjectId(photoFromRepo.ImageId));

        if (photoFromRepo.CloudinaryPublicId != null)
        {
            var deleteParams = new DeletionParams(photoFromRepo.CloudinaryPublicId);
            var result = _cloudinary.Destroy(deleteParams);
            if (result.Result == "ok")
            {
                // ignore
            }
            else
            {
                throw new Exception("Cannot delete file from cloud service...");
            }
        }

        await session.CommitTransactionAsync();
        error = false;
    }
    catch (Exception ex)
    {
        await session.AbortTransactionAsync();
        error = true;
    }
}

这有效吗?它支持多个集合吗?只有上帝知道,我是根据文档和今天早些时候在回家的路上看到的一些示例编写的,以及我认为可能是正确和可能的...

其他注意事项:

有些选项可以传递会话,其中一个选项控制读/写问题,另一个选项控制在执行事务之前应尽早处理多少数据(这是什么意思?我自己没有得到,如果您理解,请编辑我的帖子)

public class ClientSessionOptions
{
    public bool? CausalConsistency { get; set; }
    public TransactionOptions DefaultTransactionOptions { get; set; }
}

public class TransactionOptions
{
    public ReadConcern ReadConcern { get; };
    public ReadPreference ReadPreference { get; };
    public WriteConcern WriteConcern { get; };

    public TransactionOptions(
        Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
        Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
        Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>));

    public TransactionOptions With(
        Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
        Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
        Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>))
}