UnitOfWork
In the previous article, Repository And UnitOfWork Pattern - Part One, we looked at the Repository Pattern, which provides the ability to create repository class which holds the data of specific entity or entities in the form of collections. It's also used to create an abstraction layer between the data persistence (data access layer) and the business logic (business access layer) to perform operations on the data persistence.
In the previous article, we created a repository class for data table Order and OrderItem, in which each repository class created an object of DataContext which further saved the changes into the data persistence. We also discussed the problem with the repository pattern, which is that we need to have data all saved in one single transaction instead of having a separate DataContext object in every repository.
UnitOfWork (UOW) is the common pattern that is used to resolve data concurrency issues which arise when each repository implements and maintains separate Data context objects. The UnitOfWork (UOW) pattern implementation manages in-memory database operations on entities as one transaction. So, if one of the operations is failing, then the entire database operations will rollback.
According to Martin Fowler, a unit of work "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems." The "list of objects" are the repositories, the "business transactions" are the repository-specific business rules to retrieve data, and the "coordination of the writing of changes and concurrency problems" is through the DbContext.
Now, let’s have a look at the implementation of Repository pattern example using UnitOfWork Pattern.
Make the following changes to the BaseRepository class,
- First, remove the instance of OrderManagementDbContext.
- Remove Context.SaveChanges(); call from operation
- Add a parameter of type DbContext to the constructor
- public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
- {
- protected DbSet<TEntity> Entities;
- private readonly DbContext _dbContext;
- /// <summary>
- /// Initializes a new instance of the <see cref="BaseRepository{TEntity}"/> class.
- /// Note that here I've stored Context.Set<TEntity>() in the constructor and store it in a private field like _entities.
- /// This way, the implementation of our methods would be cleaner: ///
- /// _entities.ToList();
- /// _entities.Where();
- /// _entities.SingleOrDefault();
- /// </summary>
- public BaseRepository(DbContext dbContext)
- {
- _dbContext = dbContext;
- Entities = _dbContext.Set<TEntity>();
- }
- public virtual IEnumerable<TEntity> Get(
- Expression<Func<TEntity, bool>> filter = null,
- Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
- string includeProperties = "")
- {
- IQueryable<TEntity> query = Entities;
- if (filter != null)
- {
- query = query.Where(filter);
- }
- foreach (var includeProperty in includeProperties.Split
- (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
- {
- query = query.Include(includeProperty);
- }
- if (orderBy != null)
- {
- return orderBy(query).ToList();
- }
- else
- {
- return query.ToList();
- }
- }
- /// <summary>
- /// Gets the specified identifier.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns></returns>
- public virtual TEntity Get(int id)
- {
- // Here we are working with a DbContext, not specific DbContext.
- // So we don't have DbSets we need to use the generic Set() method to access them.
- return Entities.Find(id);
- }
- /// <summary>
- /// Gets all.
- /// </summary>
- /// <returns></returns>
- public IEnumerable<TEntity> GetAll()
- {
- return Entities.ToList();
- }
- /// <summary>
- /// Finds the specified predicate.
- /// </summary>
- /// <param name="predicate">The predicate.</param>
- /// <returns></returns>
- public IEnumerable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
- {
- return Entities.Where(predicate);
- }
- /// <summary>
- /// Singles the or default.
- /// </summary>
- /// <param name="predicate">The predicate.</param>
- /// <returns></returns>
- public TEntity SingleOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
- {
- return Entities.Where(predicate).SingleOrDefault();
- }
- /// <summary>
- /// First the or default.
- /// </summary>
- /// <returns></returns>
- public TEntity FirstOrDefault()
- {
- return Entities.SingleOrDefault();
- }
- /// <summary>
- /// Adds the specified entity.
- /// </summary>
- /// <param name="entity">The entity.</param>
- public void Add(TEntity entity)
- {
- Entities.Add(entity);
- }
- /// <summary>
- /// Adds the range.
- /// </summary>
- /// <param name="entities">The entities.</param>
- public void AddRange(IEnumerable<TEntity> entities)
- {
- Entities.AddRange(entities);
- }
- /// <summary>
- /// Removes the specified entity.
- /// </summary>
- /// <param name="entity">The entity.</param>
- public void Remove(TEntity entity)
- {
- Entities.Remove(entity);
- }
- /// <summary>
- /// Removes the range.
- /// </summary>
- /// <param name="entities">The entities.</param>
- public void RemoveRange(IEnumerable<TEntity> entities)
- {
- Entities.RemoveRange(entities);
- }
- /// <summary>
- /// Removes the Entity
- /// </summary>
- /// <param name="entityToDelete"></param>
- public virtual void RemoveEntity(TEntity entityToDelete)
- {
- if (_dbContext.Entry(entityToDelete).State == EntityState.Detached)
- {
- Entities.Attach(entityToDelete);
- }
- Entities.Remove(entityToDelete);
- }
- /// <summary>
- /// Update the Entity
- /// </summary>
- /// <param name="entityToUpdate"></param>
- public virtual void UpdateEntity(TEntity entityToUpdate)
- {
- Entities.Attach(entityToUpdate);
- _dbContext.Entry(entityToUpdate).State = EntityState.Modified;
- }
- }
Add UnitOfWork class comprises the property for each of the repositories and an instance of data context object that will be passed to the different repositories, so that repository will have access to the same data context object instead of creating separate instance objects within the repository itself. UnitOfWork class also implements interface IDisposable that provides an ability to release memory as soon as the execution is over.
- public interface IUnitOfWork : IDisposable
- {
- bool SaveChanges();
- }
- public class UnitOfWork : IUnitOfWork
- {
- protected readonly OrderManagementDbContext Context;
- public UnitOfWork()
- {
- Context = new OrderManagementDbContext();
- }
- public bool SaveChanges()
- {
- bool returnValue = true;
- using (var dbContextTransaction = Context.Database.BeginTransaction())
- {
- try
- {
- Context.SaveChanges();
- dbContextTransaction.Commit();
- }
- catch (Exception)
- {
- //Log Exception Handling message
- returnValue = false;
- dbContextTransaction.Rollback();
- }
- }
- return returnValue;
- }
- #region Public Properties
- private OrderRepository _orderRepository;
- public OrderRepository OrderRepoistory => _orderRepository ?? (_orderRepository = new OrderRepository(Context));
- private OrderItemRepository _orderItemRepository;
- public OrderItemRepository OrderItemRepoistory => _orderItemRepository ?? (_orderItemRepository = new OrderItemRepository(Context));
- #endregion
- #region IDisposable Support
- private bool _disposedValue = false; // To detect redundant calls
- protected virtual void Dispose(bool disposing)
- {
- if (_disposedValue) return;
- if (disposing)
- {
- //dispose managed state (managed objects).
- }
- // free unmanaged resources (unmanaged objects) and override a finalizer below.
- // set large fields to null.
- _disposedValue = true;
- }
- // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
- // ~UnitOfWork() {
- // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- // Dispose(false);
- // }
- // This code added to correctly implement the disposable pattern.
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
- // uncomment the following line if the finalizer is overridden above.
- // GC.SuppressFinalize(this);
- }
- #endregion
- }
- class Program
- {
- static void Main(string[] args)
- {
- GetOrders();
- AddOrder();
- GetOrders();
- Console.ReadLine();
- }
- private static void AddOrder()
- {
- Order order = new Order
- {
- OrderId = Guid.NewGuid().ToString(),
- OrderDate = DateTime.Now,
- OrderStatus = "In Process"
- };
- using (var unitOfWork = new UnitOfWork())
- {
- unitOfWork.OrderRepository.Add(order);
- order.OrderItems = new List<OrderItem>
- {
- new OrderItem
- {
- OrderId = order.OrderId,
- ItemName = "Item 1",
- OrderItemId = Guid.NewGuid().ToString(),
- Quantity = 1
- },
- new OrderItem
- {
- OrderId = order.OrderId,
- ItemName = "Item 2",
- OrderItemId = Guid.NewGuid().ToString(),
- Quantity = 4
- }
- };
- unitOfWork.OrderItemRepository.AddRange(order.OrderItems);
- unitOfWork.SaveChanges();
- }
- }
- private static void GetOrders()
- {
- using (var unitOfWork = new UnitOfWork())
- {
- var orders = unitOfWork.OrderRepository.GetAll();
- IEnumerable<Order> orderDatas = orders.ToList();
- if (!orderDatas.Any())
- {
- Console.WriteLine("No Order Placed");
- }
- else
- {
- foreach (var orderData in orderDatas)
- {
- Console.WriteLine("Order {0} placed and currently is {1}", orderData.OrderId,
- orderData.OrderStatus);
- var orderItems = unitOfWork.OrderItemRepository.Find(x => x.OrderId == orderData.OrderId);
- IEnumerable<OrderItem> items = orderItems as OrderItem[] ?? orderItems.ToArray();
- if (!items.Any()) continue;
- Console.WriteLine("Items included in the order:");
- foreach (var orderItem in items)
- {
- Console.WriteLine(orderItem.ItemName);
- }
- }
- }
- }
- }
- }



- order.OrderItems = new List<OrderItem>
- {
- new OrderItem
- {
- OrderId = order.OrderId,
- ItemName = "Item 1",
- OrderItemId = Guid.NewGuid().ToString(),
- Quantity = 1
- },
- new OrderItem
- {
- OrderId = order.OrderId,
- ItemName = "Item 1",
- OrderItemId = Guid.NewGuid().ToString(),
- Quantity = 4
- }
- };



As we can see, no data is inserted into either of the tables. So we can conclude that the UnitOfWork pattern allows us to handle multiple activities in a single transaction.
Summary
UnitOfWork pattern helps us in implementing the in-memory database operations and later saves in-memory updates as one transaction into the database.

DasithPosted Mar 25, 2021, 1:23 AM
This is good for learning but generic repositories like the one in your example are a code smell and should be avoided. http://codebetter.com/gregyoung/2009/01/16/ddd-the-generic-repository/
souvik settPosted Jul 24, 2019, 2:31 PM
The example is very good for really really basic learning. You carefully skipped a concept. Okay, lets take a scenario. Order id is generated after order inserted in database (most of the cases people design like this). Now how can I use the OrderId to Order Items in a single transaction?
Dmitry KorzhPosted Aug 15, 2018, 3:25 AM
Thanks for a great article! Could you please explain the benefit of having 2 different repositories for Orders and OrderItems instead of having just one that will handle Orders and OrderItems together as they are closely related? My concern is that if I had to separate commands and queries for your example it would end up with 4 repositories (Orders read repo, Orders write repo, OrderItems read repo, OrderItems write repo) and increasing complexity as a result. Now imagine that we have not only Orders and OrderItems, but also Customers, Adressess etc.
masoud talaeiiPosted Aug 15, 2018, 1:56 AM
Hi very good.