如果事务中止(MongoDB.Driver和.NETCore),则在数据库中创建记录

时间:2018-07-12 05:49:23

标签: mongodb transactions

有人尝试过在.NetCore中使用事务吗?我尝试过,但无法正常工作。

我的设置:

  1. Mongo4(3节点副本集)
  2. Visual Studio 2017
  3. MongoDB.Driver 2.7.0
  4. .Net Core 2.0。控制台应用程序

我正在按照说明进行操作:https://docs.mongodb.com/manual/core/transactions/

问题是每次都会在数据库中创建新文档(如果我中止事务,如果我提交事务,...)

我也尝试过直接在数据库上使用事务,它们也可以工作,我也可以在NodeJS上进行尝试,它们也可以工作。也许驱动程序存在错误,我不知道我在做什么错。

代码:

using System;
using MongoDB.Bson;
using MongoDB.Driver;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var connString = "mongodb://user:password@localhost:27017";
            var client = new MongoClient(connString);

            using (var session = client.StartSession())
            {
                try
                {
                    RunTransactionWithRetry(UpdateEmployeeInfo, client, session);
                }
                catch (Exception exception)
                {
                    // do something with error
                    Console.WriteLine($"Non transient exception caught during transaction: ${exception.Message}.");
                }
            }

        }

        public static void RunTransactionWithRetry(Action<IMongoClient, IClientSessionHandle> txnFunc, IMongoClient client, IClientSessionHandle session)
        {
            while (true)
            {
                try
                {
                    txnFunc(client, session); // performs transaction
                    break;
                }
                catch (MongoException exception)
                {
                    // if transient error, retry the whole transaction
                    if (exception.HasErrorLabel("TransientTransactionError"))
                    {
                        Console.WriteLine("TransientTransactionError, retrying transaction.");
                        continue;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }

        public static void CommitWithRetry(IClientSessionHandle session)
        {
            while (true)
            {
                try
                {
                    session.CommitTransaction();
                    Console.WriteLine("Transaction committed.");
                    break;
                }
                catch (MongoException exception)
                {
                    // can retry commit
                    if (exception.HasErrorLabel("UnknownTransactionCommitResult"))
                    {
                        Console.WriteLine("UnknwonTransactionCommiResult, retrying commit operation");
                        continue;
                    }
                    else
                    {
                        Console.WriteLine($"Error during commit: {exception.Message}.");
                        throw;
                    }
                }
            }
        }

        // updates two collections in a transaction
        public static void UpdateEmployeeInfo(IMongoClient client, IClientSessionHandle session)
        {
            var employeesCollection = client.GetDatabase("testdatabase").GetCollection<BsonDocument>("employees");
            var eventsCollection = client.GetDatabase("testdatabase").GetCollection<BsonDocument>("events");

            session.StartTransaction(new TransactionOptions(
                readConcern: ReadConcern.Snapshot,
                writeConcern: WriteConcern.WMajority));

            try
            {
                employeesCollection.UpdateOne(
                    Builders<BsonDocument>.Filter.Eq("employee", 3),
                    Builders<BsonDocument>.Update.Set("status", "Inactive"));
                eventsCollection.InsertOne(
                    new BsonDocument
                    {
                { "employee", 3 },
                { "status", new BsonDocument { { "new", "Inactive" }, { "old", "Active" } } }
                    });
            }
            catch (Exception exception)
            {
                Console.WriteLine($"Caught exception during transaction, aborting: {exception.Message}.");
                session.AbortTransaction();
                throw;
            }

            //I WANT TO ABORT TRANSACTION - BUT THE RECORD "employee:3...." IS STILL IN DATABASE "events"
            session.AbortTransaction();
        }

        public static void UpdateEmployeeInfoWithTransactionRetry(IMongoClient client)
        {
            // start a session
            using (var session = client.StartSession())
            {
                try
                {
                    RunTransactionWithRetry(UpdateEmployeeInfo, client, session);
                }
                catch (Exception exception)
                {
                    // do something with error
                    Console.WriteLine($"Non transient exception caught during transaction: ${exception.Message}.");
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:3)

您需要将session传递到操作中,以将其包括在交易会话中。即InsertOne方法接受IClientSessionHandle作为第一个参数。

否则,这些操作将在会话之外作为单独的操作进行操作。因此,中止实际上不会中止它们。

修改示例:

var database = client.GetDatabase("testdatabase");
var employeesCollection = database.GetCollection<BsonDocument>("employees");
var eventsCollection = database.GetCollection<BsonDocument>("events");

session.StartTransaction(new TransactionOptions(
                             readConcern: ReadConcern.Snapshot,
                             writeConcern: WriteConcern.WMajority));

try
{
    employeesCollection.UpdateOne(
                session,
                Builders<BsonDocument>.Filter.Eq("employee", 3),
                Builders<BsonDocument>.Update.Set("status", "Inactive"));
    eventsCollection.InsertOne(
                session,
                new BsonDocument
                {
                    { "employee", 3 },
                    { "status", new BsonDocument { { "new", "Inactive" }, { "old", "Active" } } }
                });
}
catch (Exception exception)
{
     Console.WriteLine($"Caught exception during transaction, aborting: {exception.Message}.");
     session.AbortTransaction();
     throw;
}

// OR session.CommitTransaction(); 
session.AbortTransaction(); 

上面的示例是使用MongoDB .Net driver v2.7和MongoDB 4.0编写的。

请注意,MongoDB Multi-Document Transactions要求集合名称空间存在。