Unit Of Work Core Save

Unit of Work component that automatically handles transactions (with multiple DbContext support). Created as a .NET standard library with filters and configuration for ASP.NET Core

Project README

UnitOfWorkCore

A standalone Unit of Work component for Entity Framework Core 2.0 and ASP.NET Core 2.0 which handles transactions automatically (in ASP.NET request scope) by committing (and rolling back in case of an error) them at the end of HTTP request. Supports multiple DbContexts.

Repository contents

This repository contains several C# projects:

  • UnitOfWorkCore - core library with Unit of Work pattern implementation
  • UnitOfWorkCore.AspNetCore - UnitOfWorkCore configuration (transaction filters and isolation level attributes) for ASP.NET Core 2.0
  • Sample projects:
    • UnitOfWorkCore.Samples.ReleasesDatabase - sample database migration scripts (console app using DbUp)
    • UnitOfWorkCore.Samples.IssuesDatabase - sample database migration scripts (console app using DbUp)
    • UnitOfWorkCore.Samples.SingleContextApi - sample ASP.NET Core 2.0 project using UnitOfWorkCore with only one DbContext
    • UnitOfWorkCore.Samples.MultiContextApi - sample ASP.NET Core 2.0 project using UnitOfWorkCore with two DbContexts

Nuget packages

UnitOfWorkCore component is distributed as two separate NuGet packages

To install UnitOfWorkCore and UnitOfWorkCore.AspNetCore in your project(s), use the Visual Studio's built-in NuGet GUI (Manage NuGet packages option in project's context menu) or run the following commands in Package Manager Console:

Install-Package UnitOfWorkCore    
Install-Package UnitOfWorkCore.AspNetCore

Single DbContext usage

Register your DbContext in ConfigureServices method in Startup.cs

services.AddDbContext<ReleasesDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ReleasesDb")));

Register UnitOfWorkCore for your DbContext in services

services.AddUnitOfWork<ReleasesDbContext>();

Add UnitOfWorkTransactionFilter to global filters

services.AddMvcCore(options =>
{      
    options.Filters.Add(typeof(UnitOfWorkTransactionFilter));
});

Inject IUnitOfWork into your services where you need to access DbSets or save database changes (like EF's DbContext.SaveChanges) to obtain inserted entity's id etc.

public ReleasesService(IUnitOfWork uow) 
{ 
    this._uow = uow;
}

Use the IUnitOfWork in your service

//add entity to the database
_uow.Set<ReleaseEntity>().Add(entity);

//save changes to obtain the entity id
_uow.SaveChanges();

PRO TIP: If you don't want to write _uow.Set<MyEntity>() with the exact name of the entity's class because you never remember it, or would like Intellisense to help you with selecting the right entities set, you can either:

A) Use helper extension methods:

public static DbSet<ReleaseEntity> Releases(this IUnitOfWork uow)
{
    return uow.Set<ReleaseEntity>();
}

//and then:
_uow.Releases().Add(entity);

B) Utilize decorator pattern described in the next section with Multiple DbContexts usage (this is a more elegant solution for multiple DbContexts, but it also works for a single one)


Multiple DbContexts usage

Register your DbContexts in ConfigureServices method in Startup.cs

services.AddDbContext<ReleasesDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ReleasesDb")));

services.AddDbContext<IssuesDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("IssuesDb")));

Register multiple UoWs, providing a unique key (string) for each of them

services.AddUnitOfWorkPool(optionsBuilder =>
{
    optionsBuilder.AddUnitOfWork<ReleasesDbContext>("Releases");
    optionsBuilder.AddUnitOfWork<IssuesDbContext>("Issues");
});

Add UnitOfWorkPoolTransactionFilter (please notice the Pool word) to global filters

services.AddMvcCore(options =>
{      
    options.Filters.Add(typeof(UnitOfWorkPoolTransactionFilter));
});

Inject IUnitOfWorkPool into your services and retrieve IUnitOfWork for a specific DbContext by its key

private readonly IUnitOfWork _releasesUow;
private readonly IUnitOfWork _issuesUow;

public MyService(IUnitOfWorkPool uowPool)
{
    _releasesUow = uowPool.Get("Releases");
    _issuesUow = uowPool.Get("Issues");
}

PRO TIP: Injecting IUnitOfWorkPool into your services and retrieving the required IUnitOfWork by key may be a bit tedious and unelegant - it would be much easier if you had a separate interface for each of your UoWs, e.g IReleasesUoW and IIssuesUoW, which you could inject instead of the whole IUnitOfWorkPool. You can easily do this by using the decorator pattern that will also allow you to extend UoW with additional methods and properties (e.g. DbSet<TEntity> properties for easier access to data with Intellisense, methods for bulk insert, delete etc.) See the sample code in UnitOfWorkCore.Samples.MultiContextApi project.

The general steps for the adapter are:

  1. Create an interface that inherits from IUnitOfWork<TDbContext> and add your own methods and properties
public interface IReleasesUoW : IUnitOfWork<ReleasesDbContext>
{
    DbSet<ReleaseEntity> Releases { get; }
}
  1. Implement your custom interface, inject IUnitOfWorkPool and use the IUnitOfWork retrieved by key in your methods
public class ReleasesUoW : IReleasesUoW
{
    private readonly IUnitOfWork _uow;

    public DbSet<ReleaseEntity> Releases => _uow.Set<ReleaseEntity>();

    public ReleasesUoW(IUnitOfWorkPool uowPool)
    {
        _uow = uowPool.Get("Releases");
    }

    //IUnitOfWork<ReleasesDbContext> methods
    public void CommitTransaction()
    {
        _uow.CommitTransaction();
    }

    //(...)
}
  1. Register your custom UoW in Startup.ConfigureServices and inject it into your other classes
//register in DI container
services.AddScoped<IReleasesUoW, ReleasesUoW>();

//use as a dependency in other classes instead of IUnitOfWorkPool
public MyService(IReleasesUoW uow) {}

Open Source Agenda is not affiliated with "Unit Of Work Core" Project. README Source: piotrekh/unit-of-work-core
Stars
36
Open Issues
3
Last Commit
1 year ago
License
MIT

Open Source Agenda Badge

Open Source Agenda Rating