Transactions

The document storage does not assume supporting transactions in terms of ACID but rely that a write operation is atomic on the level of a single document, even if the operation modifies multiple embedded documents within a single document. When a single write operation modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic and other operations may interleave. It is common behavior most of NoSQL databases.

Two Phase Commit

Lack of transactions is known problem and you can solve it using the Two Phase Commit, for example, in a way which is offered by MondoDB. But there is no silver bullet and if you really need ACID transactions you should use appropriate database.

Unit of Work

Yet another solution of transactions absence can be the Unit of Work pattern which is presented as the IUnitOfWork interface. You can accumulate changes in the memory of the web server and then commit them. Until the commit, all changes of the documents remain in the memory and nobody see them. Thus if during the changes an exception appears, the documents will not be changed. To some degree it works like transactions but if there is some functional relation between the changes, the unit of work can be unsuitable solution.

To create instance of the IUnitOfWork use the IUnitOfWorkFactory:

IUnitOfWorkFactory factory;

// ...

IUnitOfWork unitOfWork = factory.Create();

Note

The IUnitOfWork can be nested during the HTTP request handling.

The IUnitOfWork implements the IDisposable interface so it is better to use using operator:

IUnitOfWorkFactory factory;

// ...

using (IUnitOfWork unitOfWork = factory.Create())
{
    // Inserts, updates, deletes...

    unitOfWork.Commit();
}

After all changes are applied, invoke Commit() or CommitAsync().

Now let’s consider full example of using the IUnitOfWork:

IUnitOfWorkFactory factory;

// ...

using (IUnitOfWork unitOfWork = factory.Create())
{
    unitOfWork.InsertOne(...);
    unitOfWork.InsertMany(...);

    unitOfWork.UpdateOne(...);
    unitOfWork.UpdateMany(...);

    unitOfWork.DeleteOne(...);
    unitOfWork.DeleteMany(...);

    unitOfWork.Commit();
}