I'm in the process of refactoring legacy code. Unfortunately it is a WebForms application with very tight coupling and smelly code. Currently access to database is inside models which looks something like this:
var fooModel = new FooModel();
fooModel.Select(sqlConnection); // Accesses db and populates model's data.
I would like to move this logic into Data Access Layer and in the process make it easy to test, so in the future I can migrate to MVC without much of a hassle. Currently the code does not use any ORM, so I have to stick to SqlConnection
.
I figured out a way to abstract database access, but I don't know how to make it easy to test. I decided to make this hierarchy:
Controller(currently codebehind) -> Services -> UnitOfWork -> Repositories -> SqlConnection to db
.
Basically Controllers
can only see Services
which use UnitOfWork
to access Repositories
to make use of transactions and retrieve data.
I created a sample to show you what I mean and what the current problem is. I used simple List
instead of actually connecting to database, because it is just a sample to show what I'm looking for and I'm open to changing and rewriting this completely if someone has more knowledge of this kind of problems.
Example service:
public class CustomerService : ICustomerService
{
public int AddCustomer(Customer customer)
{
using (var uof = new UnitOfWork())
{
var customerId = uof.CustomerRepository.Add(customer);
var customerAddressId = uof.CustomerAddressRepository.Add(customer.Address);
var customerAddress = uof.CustomerAddressRepository.Get(customerAddressId);
customer.CustomerAddressId = customerAddressId;
customer.Address = customerAddress;
uof.CustomerRepository.Update(customer);
uof.Commit();
return customerId;
}
}
public void UpdateCustomer(Customer customer)
{
using (var uof = new UnitOfWork())
{
uof.CustomerRepository.Update(customer);
uof.Commit();
}
}
public Customer GetCustomerById(int customerId)
{
using (var uof = new UnitOfWork())
{
return uof.CustomerRepository.Get(customerId);
}
}
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork, IDisposable
{
public ICustomerRepository CustomerRepository { get; }
public ICustomerAddressRepository CustomerAddressRepository { get; }
private SqlConnection _sqlConnection;
private SqlTransaction _transaction;
public UnitOfWork()
{
_sqlConnection = new SqlConnection();
_transaction = _sqlConnection.BeginTransaction();
CustomerRepository = new CustomerRepository(_sqlConnection);
CustomerAddressRepository = new CustomerAddressRepository(_sqlConnection);
}
public void Commit()
{
_transaction.Commit();
}
public void Dispose()
{
_sqlConnection.Dispose();
}
}
Example repository:
public class CustomerRepository : ICustomerRepository
{
private readonly SqlConnection _connection;
private readonly List<Customer> _customers;
public CustomerRepository(SqlConnection connection)
{
_connection = connection;
_customers = new List<Customer>();
}
public int Add(Customer customer)
{
var id = _customers.Count + 1;
customer.CustomerId = id;
_customers.Add(customer);
return id;
}
public void Update(Customer newCustomer)
{
var index = _customers.FindIndex(f => f.CustomerId == newCustomer.CustomerId);
_customers.RemoveAt(index);
_customers.Add(newCustomer);
}
public Customer Get(int id)
{
return _customers.FirstOrDefault(f => f.CustomerId == id);
}
}
How controllers(current codebehind) use this:
var customer = _customerService.GetCustomerById(1);
var specificCustomers = _customerService.GetCustomersByIds(new int[] {5, 63, 75});
With the current approach I cannot mock ICustomerRepository
in my UnitOfWork
class. Also I kind of mixed patterns and what I need together, so I'm not sure if this is the way to go here. What can I do to make repositories in UnitOfWork
testable?