2

我想问你下面的代码是否会在一个原子操作中执行。我正在使用 mongodb c# 驱动程序。

该方法的输入是我要更新的对象的 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);
}

当我有数以百万计的文件要更新时,我对这个案子很感兴趣。如果在此更新期间出现故障(电源或硬件问题)会怎样?数据库会处于一致状态吗?

谢谢。

4

2 回答 2

1

要求

MongoDB >= 4.0 C# Driver >= 2.7 我是怎么做到的...

TL;博士

转到“示例代码”


会话

在我可以访问我的客户端 (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>))
}
于 2019-06-01T20:49:01.817 回答
0

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

您可以在Mongodb 的文档中查看这一点

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

于 2013-10-14T15:32:39.610 回答