lundi 22 octobre 2018

Disposing of context in the Unit Of Work / Repository pattern

Hello I implemented UoW/Repository pattern in my application, following inter alia MSDN tutorials. I am confused however when it comes to disposing of the context (which admittedly is because of the fact that I yet have a lot to learn about memory management in C#).

Anyway, I have a context which is passed to:

  • unit of work
  • generic repositories
  • specific repositories

My question: when exactly I should dispose of that context and which of the interfaces I have should derive from IDisposable / which classes should implement IDisposable?

Currently, I derive from IDisposable in IGenericRepository and IUnitOfWork and then implement the Dispose method in GenericRepository and UnitOfWork. But in the MSDN tutorials the implementation of the Dispose method is in the specific repositories rather then in the generic repository which is the reason for my confusion. If I am working with the same instance of context passing from base class (generic repository) to a specific repository which gets the context using base constructor, should it not be enough if I dispose it in the generic repository?

Interfaces:

public interface IUnitOfWork : IDisposable
{
    IAccountsRepository Accounts { get; }
    ITransactionsRepository Transactions { get; }
    IAccountGroupsRepository AccountGroups { get; }

    void Complete();
}

public interface IGenericRepository<TEntity> : IDisposable where TEntity : class
{
    void Add(TEntity entity);
    void Edit(TEntity entity);
    IEnumerable<TEntity> GetAll();
    TEntity GetById(object id);
    void Remove(object id);
    void Remove(TEntity entity);
}

public interface IAccountsRepository : IGenericRepository<Account>
{
    IEnumerable<Account> GetForUser(string applicationUserId);
    string GetAccountName(int accountId);
}

Implementation:

public class UnitOfWork : IUnitOfWork
{
    private readonly TinyBooksDbContext _context;
    private bool _disposed;

    public IAccountsRepository Accounts { get; }
    public ITransactionsRepository Transactions { get; }
    public IAccountGroupsRepository AccountGroups { get; set; }


    public UnitOfWork(TinyBooksDbContext context)
    {
        _context = context;
        Accounts = new AccountsRepository(_context);
        Transactions = new TransactionsRepository(_context);
        AccountGroups = new AccountGroupsRepository(_context);
    }

    public void Complete()
    {
        _context.SaveChanges();
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    private readonly TinyBooksDbContext _context;
    private readonly DbSet<TEntity> _dbSet;
    private bool _disposed;

    public GenericRepository(TinyBooksDbContext context)
    {
        _context = context;
        _dbSet = _context.Set<TEntity>();

    }

    // C
    public virtual void Add(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    public virtual IEnumerable<TEntity> GetAll()
    {
        return _dbSet.ToList();
    }

    // R
    public virtual TEntity GetById(object id)
    {
        return _dbSet.Find(id);
    }

    // U
    public virtual void Edit(TEntity entity)
    {
        _dbSet.Attach(entity);
        _context.Entry(entity).CurrentValues.SetValues(entity);
    }


    // D
    public virtual void Remove(object id)
    {
        var entity = _dbSet.Find(id);
        Remove(entity);
    }

    public virtual void Remove(TEntity entity)
    {
        if (_context.Entry(entity).State == EntityState.Detached)
            _dbSet.Attach(entity);

        _dbSet.Remove(entity);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class AccountsRepository : GenericRepository<Account>, IAccountsRepository
{
    private readonly TinyBooksDbContext _context;
    private bool _disposed;

    public AccountsRepository(TinyBooksDbContext context) : base(context)
    {
        _context = context;
    }

    public IEnumerable<Account> GetForUser(string applicationUserId) =>
        _context.Accounts.Where(a => a.ApplicationUserId == applicationUserId).ToList();

    public string GetAccountName(int accountId) =>
        _context.Accounts.SingleOrDefault(a => a.Id == accountId).Name;
}

Aucun commentaire:

Enregistrer un commentaire