Service Layer Architecture

1.Implement Generic Repository and Unit of Work Patterns with Service layer in an ASP.NET MVC. Ninject, MS Unit Test, Moq

Download Source

1.     Implementing Generic Repository

You need to create IGenericRepository and GenericRepository as shown bellow

Generic Repository Interface
 //IGenericRepository.cs
public interface IGenericRepository<T> where T : class
    {


        List<T> GetAll();
        List<T> FindBy(Expression<Func<T, bool>> predicate);
        bool Add(T entity);
        bool Delete(T entity);
        bool Edit(T entity);
        T FindById(int id);
    }

Service Layer Interface Snippet(sib)

Generic Repository Implementation
//GenericRepository.cs

public  class GenericRepository<T> :
   IGenericRepository<T>
        where T : class
    {
        public GenericRepository(MyStoreContext context)
        {
            _entities = context;
        }
        private MyStoreContext _entities;
        public MyStoreContext db
        {
            get { return _entities; }
            set { _entities = value; }
        }

        public virtual List<T> GetAll()
        {
            IQueryable<T> query = _entities.Set<T>();
            return query.ToList();
        }


        public List<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            IQueryable<T> query = _entities.Set<T>().Where(predicate);
            return query.ToList();
        }
        public virtual void Attach(T entity)
        {
            _entities.Set<T>().Attach(entity);
        }


        public virtual bool Add(T entity)
        {
            _entities.Set<T>().Add(entity);
            return true;
        }


        public virtual bool Delete(T entity)
        {
            _entities.Set<T>().Remove(entity);
            return true;
        }

        public virtual bool Edit(T entity)
        {
            _entities.Entry(entity).State = EntityState.Modified;
            return true;
        }




        public virtual T FindById(int id)
        {
           return  _entities.Set<T>().Find(id);
        }


    }

 

Service Layer Code Snippet (srvb)

2.     Implementing Unit of Work

DbContext contains unit of work by default , if you make series of changes to entities and call SaveChanges method , either all the changes make will be persisted to the database or if any of the change is failed to be persisted then all changes will be rolled back to the state prior to calling savechanges.

We created the unit of work to make our transactional change to be testable at the service layer and to get some flexibility.

 

Unit of Work Interface
//IUnitOfWork.cs
public interface IUnitOfWork:IDisposable
    {
        IGenericRepository<Product> ProductRepository { get;  }
        IGenericRepository<Order> OrderRepository { get;  }
        void Save();
    }
Unit of Work Implementation
//UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
    {
        private readonly MyStoreContext _context;
        private IGenericRepository<Product> productRepository;
        private IGenericRepository<Order> orderRepository;

        public UnitOfWork()
        {
            this._context = new MyStoreContext();
        }

        public IGenericRepository<Product> ProductRepository
        {
            get { return this.productRepository ?? (this.productRepository = new GenericRepository<Product>(_context)); }
        }

        public IGenericRepository<Order> OrderRepository
        {
            get { return this.orderRepository ?? (this.orderRepository = new GenericRepository<Order>(_context)); }
        }
      
        public void Save()
        {
            _context.SaveChanges();
        }

        private bool disposed = false;


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


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


    }

Code Snippet Unit Of Work Property(uprop)

Here we have prepared our unit of work, if you want to make our unit of work to be testable you can factor our DbContext and inject to our unit of work.

 

3.     Introducing Service Layer

 

The service layers the one the controller is going to interact, this service layer can be used for various purposes, and the first one will be to implement CRUD operation.  Since service layer is consuming unit of work we can also make changes to the group of objects and call unit of work save method. The  will be transactional because unit of work will indirectly call to dbcontext savechanges melthod which of course is transactional.

NOTE: The service layer will be used for transactional execution only if  your transaction target only single dbcontext object , if you are targeting multiple dbcontext objects you need to address this issue independently and not covered in this article.

Service Layer Interface
//IProductService.cs
public interface IProductService:IDisposable
   {
        void AddProduct(Product product);
        void UpdateProduct(Product product);
        void DeleteProduct(int id);
        List<Product> GetAllProduct();
        Product GetProduct(int productId);
    }
Service layer Implementation
//ProductService.cs
public class ProductService:IProductService
   {
       private readonly  IUnitOfWork _unitOfWork;
     
      public ProductService(IUnitOfWork unitOfWork)
       {
           this._unitOfWork = unitOfWork;
       }

       public void AddProduct(Product product)
       {
           _unitOfWork.ProductRepository.Add(product);
           _unitOfWork.Save();
       }
       public void UpdateProduct(Product product)
       {
           _unitOfWork.ProductRepository.Edit(product);
           _unitOfWork.Save();
       }
       public  void DeleteProduct(int id)
       {
           var org = _unitOfWork.ProductRepository.FindById(id);
           _unitOfWork.ProductRepository.Delete(org);
           _unitOfWork.Save();
       }
       public List<Product> GetAllProduct()
       {
           return _unitOfWork.ProductRepository.GetAll();
       }
       public Product GetProduct(int productId)
       {
           return _unitOfWork.ProductRepository.FindById(productId);
       }

       public void Dispose()
       {
           _unitOfWork.Dispose();
       }
   }

 

4.     Setting Up Ninject

Since this design favours isoloation of each layer , we need dependency resolver to instantiate and supply dependent objects from one layer to another, for this purpose I have used Ninject dependency resolver.

  1. You have to install Ninject in your MVC project from nugget package

http://expertester.wordpress.com/2011/08/18/how-to-add-ninject-to-your-project-using-visual-studio-2010-sp1/

  1. Create Dependency Resolver Class  in your MVC project
Dependancy Resolver
//NinjectDependencyResolver.cs
public class NinjectDependencyResolver:IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver()
        {
            kernel = new StandardKernel();
            AddBindings();
        }
        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        private void AddBindings()
        {
            kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
            kernel.Bind<IProductService>().To<ProductService>();
        }
    }

 

 

  1. Register NinjectDependencyResolver in the global.asax
Hooking up the dependency resolver
// Global.asax.cs -Application_Start()
protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            DependencyResolver.SetResolver(new NinjectDependencyResolver());
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

 

 

 

5.     Consuming Service Layer at the controller

The ProductController receives service layer as a constructor injection

//ProductController.cs
public class ProductController : Controller
    {
        private readonly IProductService productService;
        public int PageSize = 4;
        //
        // GET: /Product/
     
        public ProductController(IProductService productServiceParam)
        {
          
            this.productService = productServiceParam;
        }
     
        public ViewResult Index(int page=1)
        {
            ProductListViewModel model = new ProductListViewModel
                                             {
                                                 Products = productService.GetAllProduct()
                                                     .OrderBy(p => p.ProductId)
                                                     .Skip((page - 1)*PageSize)
                                                     .Take(PageSize),
                                                 PagingInfo = new PagingInfo
                                                                  {
                                                                      CurrentPage = page,
                                                                      ItemsPerPage = PageSize,
                                                                      TotalItems =
                                                                          productService.GetAllProduct().Count()
                                                                  }




                                             };


            return View(model);
        }


        //
        // GET: /Product/Details/5


        public ActionResult Details(int id = 0)
        {
            Product product = productService.GetProduct(id);
            if (product == null)
            {
                return HttpNotFound();
            }
            return View(product);
        }


        //
        // GET: /Product/Create


        public ActionResult Create()
        {
            return View();
        }


        //
        // POST: /Product/Create


        [HttpPost]
        public ActionResult Create(Product product)
        {
            if (ModelState.IsValid)
            {
               productService.AddProduct(product);
              
                return RedirectToAction("Index");
            }


            return View(product);
        }


        //
        // GET: /Product/Edit/5


        public ActionResult Edit(int id = 0)
        {
            Product product = productService.GetProduct(id);
            if (product == null)
            {
                return HttpNotFound();
            }
            return View(product);
        }


        //
        // POST: /Product/Edit/5


        [HttpPost]
        public ActionResult Edit(Product product)
        {
            if (ModelState.IsValid)
            {
                productService.UpdateProduct(product);
           
                return RedirectToAction("Index");
            }
            return View(product);
        }


        //
        // GET: /Product/Delete/5


        public ActionResult Delete(int id = 0)
        {
            Product product = productService.GetProduct(id);
            if (product == null)
            {
                return HttpNotFound();
            }
            return View(product);
        }


        //
        // POST: /Product/Delete/5


        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            productService.DeleteProduct(id);
            return RedirectToAction("Index");
        }


        protected override void Dispose(bool disposing)
        {
            productService.Dispose();
            base.Dispose(disposing);
        }
    }

Implementing Unit Test using MS Test

How to implement NUnit Test in visual studio 2012

 

Download Source