Dot.Conductor
1.2.19
See the version list below for details.
dotnet add package Dot.Conductor --version 1.2.19
NuGet\Install-Package Dot.Conductor -Version 1.2.19
<PackageReference Include="Dot.Conductor" Version="1.2.19" />
paket add Dot.Conductor --version 1.2.19
#r "nuget: Dot.Conductor, 1.2.19"
// Install Dot.Conductor as a Cake Addin #addin nuget:?package=Dot.Conductor&version=1.2.19 // Install Dot.Conductor as a Cake Tool #tool nuget:?package=Dot.Conductor&version=1.2.19
Unit of Work and Repository Pattern with Entity Framework Core
This project provides an implementation of the Unit of Work (UoW) and the Repository pattern using Entity Framework (EF) Core.
Setting Up
In your Startup.cs - ConfigureServices method, register the UnitOfWork and repositories using provided extension method:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddUnitOfWorkAndRepositories<MyDbContext>(
Configuration.GetConnectionString("DefaultConnection")
);
// ...
}
Using Unit of Work
You can use it in your Controller by dependency injection in the constructor.
public class UserController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public UserController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
}
Use it in your methods for querying data or performing operations:
public async Task<IActionResult> GetUser(int id)
{
var userRepository = _unitOfWork.GetRepository<User>();
var user = await userRepository.GetByIdAsync(id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
Perform multiple operations in a single transaction:
public async Task<IActionResult> UpdateUser(User user)
{
var userRepository = _unitOfWork.GetRepository<User>();
userRepository.UpdateAsync(user);
var changesCount = await _unitOfWork.CommitAsync();
return Ok(changesCount);
}
If you want to catch exceptions:
public async Task<IActionResult> UpdateUser(User user)
{
try
{
var userRepository = _unitOfWork.GetRepository<User>();
userRepository.UpdateAsync(user);
var changesCount = await _unitOfWork.CommitAsync();
return Ok(changesCount);
}
catch (Exception e)
{
return BadRequest($"An error occurred when updating the data: {e.Message}");
}
}
Using Unit of Work outside of scope
To use UnitOfWork outside of Startup file, you can use the extension method provided for IServiceScopeFactory:
public async Task<bool> PerformUnitOfWorkAsync(IServiceScopeFactory scopeFactory)
{
var result = await scopeFactory.UseUnitOfWork<MyDbContext, bool>(connectionString, async uow =>
{
var userRepository = uow.GetRepository<User>();
var user = new User { Name = "John Doe", Email = "john@example.com" };
await userRepository.AddAsync(user);
await uow.CommitAsync();
return true;
});
return result;
}
This code will execute actions related to User repository inside a separate UnitOfWork scope.
Unit of Work with Custom Connection String On-The-Fly
First, inject UnitOfWorkFactory into the class where you want to use a UnitOfWork with a custom connection string:
public class UserController : ControllerBase
{
private readonly UnitOfWorkFactory<MyDbContext> _unitOfWorkFactory;
public UserController(UnitOfWorkFactory<MyDbContext> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
}
Then, you can create a UnitOfWork with your custom connection string:
public async Task<ActionResult> Create(User user)
{
// Use a custom connection string
string connectionString = "YourConnectionStringHere";
using(var unitOfWork = _unitOfWorkFactory.CreateUnitOfWork(connectionString))
{
var userRepository = unitOfWork.GetRepository<User>();
await userRepository.AddAsync(user);
await unitOfWork.CommitAsync();
}
return CreatedAtAction("GetUser", new { id = user.UserId }, user);
}
This will create a new UnitOfWork that is connected to the database specified in YourConnectionStringHere, independent of other UnitOfWork instances. Please remember to dispose of the UnitOfWork when you've finished using it.
Using Transactions with Unit of Work
If you need to set a specific isolation level for a transaction:
public async Task<IActionResult> UpdateUser(User user)
{
try
{
await _unitOfWork.BeginTransactionAsync(System.Data.IsolationLevel.Serializable);
var userRepository = _unitOfWork.GetRepository<User>();
await userRepository.UpdateAsync(user);
var changesCount = await _unitOfWork.CommitAsync();
return Ok(changesCount);
}
catch (Exception e)
{
await _unitOfWork.RollbackAsync();
return BadRequest($"An error occurred when updating the data: {e.Message}");
}
}
In this example, a transaction is started with Serializable isolation level. This isolation level offers high data consistency by preventing other users from updating or inserting rows into the dataset until the transaction is finished.
TransactionService
The TransactionService
class provides a method named ExecuteInTransactionAsync
. This method serves as a wrapper for executing a Func<Task<TResult>>
within a database transaction.
The transaction begins before executing the provided operation and is committed immediately after its successful execution. However, if an exception occurs during the operation, the transaction is rolled back.
You can then use TransactionService within a controller or service by injecting it in your constructor:
public class SomeController : ControllerBase
{
private readonly TransactionService _transactionService;
public SomeController(TransactionService transactionService)
{
_transactionService = transactionService;
}
public async Task<IActionResult> SomeAction()
{
var result = await _transactionService.ExecuteInTransactionAsync(async () =>
{
var repository1 = _uow.GetRepository<EntityOne>();
var entity1 = await repository1.GetQueryable().FirstOrDefaultAsync(); // retrieving data
entity1.SomeProperty = "newValue";
var repository2 = _uow.GetRepository<EntityTwo>();
var entity2 = new EntityTwo { SomeProperty = "example" }; // creating new entity
repository2.Add(entity2);
await _uow.CommitAsync(); // saving changes
return entity2.EntityId; // return some result
});
return Ok(result);
}
}
Notes
- Exceptions are handled by re-throwing them after a rollback. Consider wrapping them in a custom exception class to add context and improve handling downstream.
- The implementation of UoW here is tightly coupled with Entity Framework Core. Consider abstracting DbContext away for a more flexible design.
- Multiple threads may affect UnitOfWork. Consider synchronization for multithreaded operations.
- The CommitAsync() function needs proper exception handling to avoid memory leaks.
Use this pattern to make your code cleaner, more organized, and easier to maintain. Happy coding!
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.0)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.4.0 | 164 | 10/17/2024 |
1.3.0 | 538 | 6/23/2024 |
1.2.23 | 351 | 3/11/2024 |
1.2.22 | 365 | 12/29/2023 |
1.2.21 | 132 | 12/29/2023 |
1.2.20 | 139 | 12/28/2023 |
1.2.19 | 141 | 12/19/2023 |
1.2.18 | 111 | 12/19/2023 |
1.2.17 | 107 | 12/19/2023 |
1.2.16 | 257 | 11/15/2023 |
1.2.15 | 157 | 11/5/2023 |
1.2.14 | 137 | 11/4/2023 |
1.2.13 | 116 | 11/4/2023 |
1.2.12 | 107 | 11/4/2023 |
1.2.11 | 116 | 11/4/2023 |
1.2.10 | 115 | 11/4/2023 |
1.2.9 | 120 | 11/4/2023 |
1.2.8 | 127 | 11/4/2023 |
1.2.7 | 122 | 11/2/2023 |
1.2.6 | 120 | 11/2/2023 |
1.2.5 | 143 | 11/2/2023 |
1.2.4 | 133 | 11/2/2023 |
1.2.3 | 130 | 11/1/2023 |
1.2.2 | 119 | 11/1/2023 |
1.2.1 | 133 | 10/27/2023 |
1.2.0 | 132 | 10/27/2023 |
1.1.1 | 121 | 10/25/2023 |
1.1.0 | 129 | 10/19/2023 |