需求
MongoDB >= 4.0
C# Driver >= 2.7
以下是我的操作步骤...
简短版本
前往 "示例代码"
会话
在我的DbContext类中,我可以访问我的客户端(IMongoClient),我定义了会话:
public IClientSessionHandle StartSession()
{
return _client.StartSession(new ClientSessionOptions());
}
public async Task<IClientSessionHandle> StartSessionAsync()
{
return await _client.StartSessionAsync(new ClientSessionOptions());
}
根据文档,如果这些事务是一个接一个地到达的,它们可以执行多个事务。
会话用于将一系列操作分组在一起,这些操作彼此相关并应使用相同的会话选项执行。会话也用于事务。
这些会话应该在您结束操作后立即关闭。因此,您应该像以下方式编写代码,或根据您的情况手动处理清理:
using(var session = _db.StartSessionAsync()) {
}
关于 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")
{
}
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>))
}