Транзакция с несколькими документами не работает в C# с использованием сервера сообщества mongodb 4.08
Мне нужно обновить несколько документов, используя транзакцию mongodb, версия сервера сообщества mongodb - 4.08, а драйвер mongodb для.net - 2.9 бета (также пробовал 2.8). После отладки я вижу, что он выполнил 'session.AbortTransaction();', но данные все еще были вставлены.
var client = new MongoClient(_config.GetConnectionString(ProductMongoDBContext.DATABASE_CONNECTION_STRING));
var session = client.StartSession();
try
{
session.StartTransaction();
//var database = session.Client.GetDatabase(ProductMongoDBContext.DATABASE_NAME);
var orders = session.Client.GetDatabase(ProductMongoDBContext.DATABASE_NAME).GetCollection<DALOrder>(ProductMongoDBContext.TABLE_NAME_ORDER);
var products = session.Client.GetDatabase(ProductMongoDBContext.DATABASE_NAME).GetCollection<DALProduct>(ProductMongoDBContext.TABLE_NAME_PRODUCT);
DateTime dtNow = DateTime.Now.ToUniversalTime();
await orders.InsertOneAsync(new DALOrder
{
ID = order.ID,
ProductID = Guid.Parse(order.ProductID),
Size = order.Size,
Taste = order.Taste,
TextOnCake = order.TextOnCake,
Consignee = order.Consignee,
ConsigneeAddress = order.ConsigneeAddress,
ConsigneePhone = order.ConsigneePhone,
DeliveryTime = order.DeliveryTime.ToUniversalTime(),
DeliveryWay = order.DeliveryWay,
OrderDepartment = order.OrderDepartment,
Remarks = order.Remarks,
State = OrderState.New.ToString(),
CreatedTime = dtNow,
UpdatedTime = dtNow
});
// After order created, decrease product inventory by one
var productInfo = products.Find<DALProduct>(p => p.ID.ToString().Equals(order.ProductID)).FirstOrDefault();
productInfo.Inventory -= 1;
await products.ReplaceOneAsync<DALProduct>(p => p.ID.ToString().Equals(order.ProductID), productInfo);
session.CommitTransaction();
return true;
}
catch (Exception e)
{
session.AbortTransaction();
order.Message = e.Message;
}
Ожидайте, что вставленные данные заказа могут быть откатом, фактический результат - данные были вставлены в БД.
Кстати, ошибка возникает в
var productInfo = products.Find<DALProduct>(p => p.ID.ToString().Equals(order.ProductID)).FirstOrDefault();
I define ID as GUID in model like below
[BsonId]
public Guid ID { get; set; }
будет выбрасывать исключение как "{document}{_id}.ToString() is not supported."
Как этого избежать
1 ответ
Я вижу, что он выполнил 'session.AbortTransaction();', но данные все еще были вставлены.
Причина, по которой операции выполняются (т. Е. Данные все еще были вставлены после прерывания), потому что операции не содержатся в транзакционном сеансе.
Все операции CRUD должны иметь перегруженные методы, определяющие IClientSessionHandle
в качестве первого аргумента. Например:
Task InsertOneAsync(IClientSessionHandle session, TDocument document, InsertOneOptions options = null, CancellationToken cancellationToken = default(CancellationToken));
См. InsertOneAsync и ReplaceOneAsync в драйвере MongoDB .NET/C# v2.8.1.
Чтобы гарантировать, что операции содержатся в транзакционном сеансе, передайте сеанс в качестве аргумента операциям CRUD. Любые операции, не имеющие объекта сеанса, будут выполняться вне сеанса.
В Visual Studio вы можете навести курсор на Find<DALProduct>
метод, и вы увидите, что он ожидает параметр типа Expression<Func<DALProduct, bool>>
, Это означает, что это выражение будет переведено на язык запросов MongoDB.
К сожалению невозможно перевести ToString()
в запрос MongoDB с помощью драйвера C#. Хорошей новостью является то, что вам не нужно этого делать - вы можете сравнивать значения guid напрямую (сохраняются как BinData
в MongoDB), используя следующий код:
var guid = Guid.Parse(order.ProductID);
var productInfo = products.Find<DALProduct>(p => p.ID == guid).FirstOrDefault();
РЕДАКТИРОВАТЬ: чтобы исправить вашу транзакцию, вам нужно пройти session
к каждой операции CRUD (InsertOneAsync
перегружен и одна из его версий занимает IClientSessionHandle
как первый параметр)
await orders.InsertOneAsync(session, new DALOrder
{
ID = order.ID,
ProductID = Guid.Parse(order.ProductID),
Size = order.Size,
Taste = order.Taste,
TextOnCake = order.TextOnCake,
Consignee = order.Consignee,
ConsigneeAddress = order.ConsigneeAddress,
ConsigneePhone = order.ConsigneePhone,
DeliveryTime = order.DeliveryTime.ToUniversalTime(),
DeliveryWay = order.DeliveryWay,
OrderDepartment = order.OrderDepartment,
Remarks = order.Remarks,
State = OrderState.New.ToString(),
CreatedTime = dtNow,
UpdatedTime = dtNow
});