Problem
I’m building WPF/MVVM application and I’m struggling implementing Unit of work pattern.
The part, about which I’m not sure is UnitOfWork
class. Here is my thought:
It’s coupled with my repositories and MyDatabaseContext
. At first impression, it doesn’t sound good. But, I think, UnitOfWork
should be coupled with DbContext
. Technically it should be named MyDatabaseUnitOfWork
. What do you think?
Unit of work:
public interface IUnitOfWork : IDisposable
{
int Complete();
ICustomerRepository Customers { get; }
IPhoneRepository Phones { get; }
IAddressRepository Address { get; }
}
public class UnitOfWork<TContext> : IUnitOfWork
where TContext : MyDatabaseContext, new()
{
public ICustomerRepository Customers { get; }
public IPhoneRepository Phones { get; }
public IAddressRepository Address { get; }
public UnitOfWork()
{
context = new TContext();
Customers = new CustomerRepository(context);
Phones = new PhoneRepository(context);
Address = new AddressRepository(context);
}
public int Complete()
{
return context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
public IUnitOfWork Create()
{
return new UnitOfWork<MyDatabaseContext>();
}
}
Useage:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
try
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<IUnitOfWorkFactory, UnitOfWorkFactory>();
unityContainer.RegisterType<MainWindowViewModel>();
var window = new MainWindow()
{
DataContext = unityContainer.Resolve<MainWindowViewModel>()
};
window.Show();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
public class MainWindowViewModel : ObservableObject
{
private readonly IUnitOfWorkFactory unitOfWorkFactory;
public MainWindowViewModel(IUnitOfWorkFactory uowFactory)
{
unitOfWorkFactory = uowFactory;
}
private int DoSomething()
{
using (var unitOfWork = unitOfWorkFactory.Create())
{
}
}
}
Sample repository:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(int id);
IEnumerable<TEntity> Get(Func<TEntity, bool> predicate);
IEnumerable<TEntity> GetAll();
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
}
public interface ICustomerRepository : IRepository<Customer>
{
IEnumerable<Customer> GetTopCustomers(int count);
}
internal class CustomerRepository : Repository<Customer>, ICustomerRepository
{
private MyDatabaseContext MyDatabaseContext => Context as MyDatabaseContext;
internal CustomerRepository(MyDatabaseContext context)
: base(context)
{
}
public IEnumerable<Customer> GetTopCustomers(int count)
{
return MyDatabaseContext.Customers.ToList();
}
}
Here is my second option: Is it necessary?
public interface IUnitOfWork : IDisposable
{
int Complete();
}
public interface IMyDatabaseUnitOfWork : IUnitOfWork
{
int Complete();
ICustomerRepository Customers { get; }
IPhoneRepository Phones { get; }
IAddressRepository Address { get; }
}
public class MyDatabaseUnitOfWork<TContext> : IMyDatabaseUnitOfWork
where TContext : MyDatabaseContext, new()
{
public ICustomerRepository Customers { get; }
public IPhoneRepository Phones { get; }
public IAddressRepository Address { get; }
public MyDatabaseUnitOfWork()
{
context = new TContext();
Customers = new CustomerRepository(context);
Phones = new PhoneRepository(context);
Address = new AddressRepository(context);
}
public int Complete()
{
return context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
Solution
You should consider using loosely coupled code. IoC / DI are a great way to go. Example:
public class ImmutableUnitOfWork
{
private readonly ICustomerRepository _customers;
private readonly IPhoneRepository _phones;
private readonly IAddressRepository _address;
public ImmutableUnitOfWork(
ICustomerRepository customers,
IPhoneRepository phones,
IAddressRepository address)
{
_customers = customers;
_phones = phones;
_address = address;
}
}
using StructureMap your dependency resolution should look something in the lines of:
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.AssemblyContainingType<ICustomerRepository>(); // Core
scan.AssemblyContainingType<CustomerRepository>(); // Infrastructure
scan.AssemblyContainingType<MyDatabaseContext>(); // Just in case
});
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
// WithDefaultConventions basically says if you name interfaces with
// same name, but with "I" prefix you do not need following:
x.For(typeof(IMyDatabaseContext)).Use(typeof(MyDatabaseContext));
x.For(typeof(ICustomerRepository)).Use(typeof(CustomerRepository));
});
but I understand if it is for learning purposes.
From code perspective :
ICustomerRepository is a service, not a repository. IRepository<TEntity>
– is leaky, especially IEnumerable<TEntity> Get(Func<TEntity, bool> predicate);
. If you feel that you really need it, try using specification pattern instead. This is just bad on many levels:
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}