3

I need update multiple documents using mongodb transaction, mongodb community server version is 4.08, and mongodb driver for .net is 2.9 beta(also tried 2.8). From debugging, I can see it executed 'session.AbortTransaction();', but data was still inserted.

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;
}

Expect inserted order data can be rollback, actual result is the data has been inserted into db.

BTW, the error occurs at

    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; }

it will throw exception like "{document}{_id}.ToString() is not supported." How to avoid this

mickl
  • 48,568
  • 9
  • 60
  • 89
Yu Taylor
  • 63
  • 4
  • As Wan points out below, you need to pass the session to each operating. If you don't feel like doing that, JohnKnoop.MongoRepository provides an abstraction that automatically enlists with the current transaction: https://github.com/johnknoop/MongoRepository#transactions – John Knoop Aug 07 '20 at 13:16

1 Answers1

2

I can see it executed 'session.AbortTransaction();', but data was still inserted.

The reason why the operations are executed (i.e. data was still inserted after abort), because the operations are not contained within a transactional session.

All of the CRUD operations should have an overloaded methods that specifies IClientSessionHandle as its first argument. For example:

Task InsertOneAsync(IClientSessionHandle session, TDocument document, InsertOneOptions options = null, CancellationToken cancellationToken = default(CancellationToken));

See: InsertOneAsync and ReplaceOneAsync in MongoDB .NET/C# driver v2.8.1.

To ensure the operations are contained within the transactional session, pass the session as an argument to the CRUD operations. Any operations that don't have the session object, will be executed outside of the session.

Wan B.
  • 18,367
  • 4
  • 54
  • 71