Skip to content

Synnotech-AG/Synnotech.DatabaseAbstractions

Repository files navigation

Synnotech.DatabaseAbstractions

Provides common abstractions for database access in .NET.

Synnotech Logo

License NuGet

How to Install

Synnotech.DatabaseAbstractions is compiled against .NET Standard 2.0 and 2.1 and thus supports all major plattforms like .NET 5, .NET Core, .NET Framework 4.6.1 or newer, Mono, Xamarin, UWP, or Unity.

Synnotech.DatabaseAbstractions is available as a NuGet package and can be installed via:

  • Package Reference in csproj: <PackageReference Include="Synnotech.DatabaseAbstractions" Version="3.0.0" />
  • dotnet CLI: dotnet add package Synnotech.DatabaseAbstractions
  • Visual Studio Package Manager Console: Install-Package Synnotech.DatabaseAbstractions

What does Synnotech.DatabaseAbstractions offer you?

IAsyncSession - supporting the Unit-of-Work pattern

This package offers interfaces and abstract base classes for accessing databases. Both the IAsyncSession and ISession interfaces represent the Unit-of-Work Design Pattern. We strongly recommend to use IAsyncSession by default as all database I/O should be executed in an asynchronous fashion to avoid threads being blocked during database queries. This is especially important when you try to scale service apps. Incoming requests will usually be handled by executing code on the .NET Thread Pool (e.g. in ASP.NET Core) which in turn will create new threads when it sees that its threads are blocked. With a high number of concurrent requests, you might end up in a situation where your service app responds really slowly because of all the overhead of new threads being created and the constant context switches between them (thread starvation).

However, some data access libraries do not support asynchronous queries. As of August 2021, e.g. Oracle and Firebird did not override the asynchronous methods of ADO.NET - all calls will always be executed synchronously (even when you call the async APIs, like DbConnection.OpenAsync). You can resort to ISession in these circumstances.

There is also an IAsyncReadOnlySession interface that derives from both IDisposable and IAsyncDisposable. It can be used to create abstractions for sessions that only read data and do not require a transaction.

Sessions with several transactions

If you need to support individual transactions during a database session, then use the IAsyncTransactionalSession (or ITransactionalSession) interfaces. Instead of a SaveChangesAsync method, you can use this session type to manually begin transactions by calling BeginTransactionAsync. You can then save your changes by committing the transaction. Please be aware that you should not nest transaction, i.e. you should not call BeginTransactionAsync again while you still have an existing transaction in your current scope.

ISessionFactory for asynchronously creating and opening sessions

Some data access libraries, especially Micro-ORMs and ADO.NET, require client code to explicitly open a transaction asychronously and start a transaction before data-manipulating commands can be executed. If you want to do this asynchronously while hiding the transaction behind an IAsyncSession, you can use the ISessionFactory<T> interface to create and open session instances asynchronously. The client code might look like this:

public sealed class SomeService
{
    public SomeService(ISessionFactory<IMySession> sessionFactory) =>
        SessionFactory = sessionFactory ?? throw new ArgumentNullException(nameof(sessionFactory));

    // IMySession is a session that is customized for your use case and derives from IAsyncSession (or IAsyncReadOnlySession)
    private ISessionFactory<IMySession> SessionFactory { get; }

    public async Task DoSomethingAsync()
    {
        // The next line will create a new session instance,
        // open the connection to the target database asynchronously.
        // Dependending on the implementation, a transaction might
        // be started asynchronously.
        await using var session = await SessionFactory.OpenSessionAsync();

        // Do something useful here with your session
    }
}

Please note that you usually need to use this interface with Micro-ORMs or ADO.NET. If your underlying data access library already implements a SaveChangesAsync mechanism that involves an implicit transaction (like e.g. RavenDB's IAsyncDocumentSession or Entity Framework's DbContext), you usually do not need to use the ISessionFactory interface.

Cancellation Tokens

As of version 2.0.0, all async APIs of this package support CancellationToken instances to abort async operations. Please be aware that the result of aborting an async operation might lead to different results (e.g. an OperationCanceledException or the task returning a result of -1) which depends on the implementation of the async methods.