Unit of Work (UoW) pattern | design pattern | Database

Unit of Work (UoW)

The Unit of Work (UoW) pattern is a design pattern used in software development to manage transactions and the interactions with a data store, typically a database. It helps ensure that all database operations within a certain scope are performed atomically (all succeed or all fail), and it abstracts the complexity of managing database connections and transactions.


Here's a high-level explanation of how the Unit of Work pattern works:


1. Define a Unit of Work Interface: Create an interface for the Unit of Work that includes methods to begin, commit, and rollback transactions. This interface should be responsible for managing data transactions across multiple repositories.


2. Implement a Concrete Unit of Work: Create a concrete implementation of the Unit of Work interface. This implementation will be responsible for starting, committing, and rolling back transactions.


3. Create Repository Interfaces: Define interfaces for your data repositories (e.g., `IPersonRepository`, `IOrderRepository`) that include methods for data access operations.


4. Implement Repository Classes: Implement concrete repository classes that implement the repository interfaces. These classes should have methods for CRUD operations and use the Unit of Work to manage transactions.


5. UnitOfWork-Repository Interaction: In your repository methods, use the Unit of Work to begin a transaction, perform the necessary database operations (e.g., inserts, updates, deletes), and then commit or rollback the transaction as needed.


6. Usage in Services or Controllers: In your application's services or controllers, use the repository classes and the Unit of Work to orchestrate data access and transaction management. Transactions can span multiple repository operations.


Code example c#


using System;

using System.Data.Entity;

using System.Linq;


// Define your data model (e.g., Person)

public class Person

{

    public int ID { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

}


// Define a context that inherits from DbContext

public class YourDbContext : DbContext

{

    public YourDbContext() : base("YourConnectionStringName")

    {

    }


    public DbSet<Person> People { get; set; }

}


// Define a generic repository interface

public interface IRepository<T> where T : class

{

    IQueryable<T> GetAll();

    T GetById(int id);

    void Insert(T entity);

    void Update(T entity);

    void Delete(int id);

}


// Implement a generic repository

public class Repository<T> : IRepository<T> where T : class

{

    private readonly YourDbContext _context;


    public Repository(YourDbContext context)

    {

        _context = context;

    }


    public IQueryable<T> GetAll()

    {

        return _context.Set<T>();

    }


    public T GetById(int id)

    {

        return _context.Set<T>().Find(id);

    }


    public void Insert(T entity)

    {

        _context.Set<T>().Add(entity);

    }


    public void Update(T entity)

    {

        _context.Entry(entity).State = EntityState.Modified;

    }


    public void Delete(int id)

    {

        var entity = _context.Set<T>().Find(id);

        if (entity != null)

        {

            _context.Set<T>().Remove(entity);

        }

    }

}


// Define a Unit of Work interface

public interface IUnitOfWork : IDisposable

{

    void SaveChanges();

    IRepository<T> GetRepository<T>() where T : class;

}


// Implement a Unit of Work

public class UnitOfWork : IUnitOfWork

{

    private readonly YourDbContext _context;

    private bool disposed = false;


    public UnitOfWork()

    {

        _context = new YourDbContext();

    }


    public void SaveChanges()

    {

        _context.SaveChanges();

    }


    public IRepository<T> GetRepository<T>() where T : class

    {

        return new Repository<T>(_context);

    }


    protected virtual void Dispose(bool disposing)

    {

        if (!disposed)

        {

            if (disposing)

            {

                _context.Dispose();

            }

            disposed = true;

        }

    }


    public void Dispose()

    {

        Dispose(true);

        GC.SuppressFinalize(this);

    }

}


public class MyService

{

    private readonly IUnitOfWork _unitOfWork;


    public MyService(IUnitOfWork unitOfWork)

    {

        _unitOfWork = unitOfWork;

    }


    public void CreateNewPerson(Person newPerson)

    {

        try

        {

            // Get the repository for the Person entity

            var personRepository = _unitOfWork.GetRepository<Person>();


            // Perform data operations

            personRepository.Insert(newPerson);


            // Commit changes to the database

            _unitOfWork.SaveChanges();

        }

        catch (Exception ex)

        {

            // Handle exceptions, log errors, etc.

            // You can also choose to rollback the transaction here if needed.

        }

    }


    // Other methods for data operations

}


Comments