n-layered Architecture Using Entity Framework, Generic Repository And Unit-Of-Work

Introduction

This article covers the things given below.

  • Implementation of n-layered architecture with following layers,

    • Domain layer
      It holds Entity Framework Context and Entities created using Code First from database approach.

    • Repository layer
      It holds code for CRUD operations, using Generic Repository and Unit-of-Work patterns.

    • Application layer
      It holds the Application logic. Application layer call methods of Repository layer for CRUD operations. Repository layer returns the data in Domain layer Entities. Application layer performs an additional logic and returns the data in Model objects to the Web layer.

    • Model layer
      Model is shared between Application and Web layer. Application layer returns the data in Model objects to the Web layer.

    • Web layer
      This uses MVC (Model-View-Controller) pattern and holds Presentation layer logic. For Models, it uses Model layer objects.

  • Creating Database for the tutorial.
  • Using Entity Framework’s Code First from the database approach.
  • Using Generic Repository and Unit-of-Work patterns.

Basic Solution and Project creation

  • Create a new .NET Framework 4.5 Blank Solution (FrameworkOne).


  • Add a .NET Framework 4.5 Class Library project (FrameworkOne.Repository) in the solution.


  • Add a .NET Framework 4.5 Class Library project (FrameworkOne.Domain) in the solution.


  • Add a .NET Framework 4.5 Class Library project (FrameworkOne.Application) in the solution.


  • Add a .NET Framework 4.5 Class Library project (FrameworkOne.Model) in the solution.


  • Add a .NET Framework 4.5 ASP.NET Web Application project (FrameworkOne.Web) in the solution.


  • Select MVC template.

Adding NuGet Packages

  • Using NuGet Package Manager, add an Entity Framework in FrameworkOne.Domain project.


  • Using NuGet Package Manager, add Entity Framework in FrameworkOne.Repository project.


  • Using NuGet Package Manager, add Unity.Mvc in FrameworkOne.Web project. This is added for Dependency Injection.


Adding Project References

  • Add Framework.Domain project reference in FrameworkOne.Repository project.


  • Add FrameworkOne.Domain, FrameworkOne.Model, Framework.Repository project reference in FrameworkOne.Application project.


  • Add FrameworkOne.Application, FrameworkOne.Domain, FrameworkOne.Model, Framework.Repository project reference in FrameworkOne.Web project.


Setting up the Database

  • Connect to (localdb)\MSSQLLocalDb Server from SQL Server 2014 Management Studio.


  • Create a new database (FrameworkOne) with default settings.


  • Create three tables Student, Assignment, StudentAssignment. The database diagram is given below and CREATE scripts for three tables.


    1. /****** Object:  Table [dbo].[Student] ******/  
    2. USE [FrameworkOne]  
    3. GO  
    4.   
    5. SET ANSI_NULLS ON  
    6. GO  
    7.   
    8. SET QUOTED_IDENTIFIER ON  
    9. GO  
    10.   
    11. CREATE TABLE [dbo].[Student](  
    12.     [StudentId] [intNOT NULL,  
    13.     [Name] [nvarchar](100) NOT NULL,  
    14.  CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED   
    15. (  
    16.     [StudentId] ASC  
    17. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
    18. ON [PRIMARY]  
    19.   
    20. GO  
    21.   
    22. /****** Object:  Table [dbo].[Assignment] ******/  
    23. USE [FrameworkOne]  
    24. GO  
    25.   
    26. SET ANSI_NULLS ON  
    27. GO  
    28.   
    29. SET QUOTED_IDENTIFIER ON  
    30. GO  
    31.   
    32. CREATE TABLE [dbo].[Assignment](  
    33.     [AssignmentId] [intNOT NULL,  
    34.     [Name] [nvarchar](100) NOT NULL,  
    35.     [TotalMarks] [intNOT NULL,  
    36.  CONSTRAINT [PK_Assignment] PRIMARY KEY CLUSTERED   
    37. (  
    38.     [AssignmentId] ASC  
    39. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
    40. ON [PRIMARY]  
    41.   
    42. GO  
    43.   
    44. /****** Object:  Table [dbo].[StudentAssignment] ******/  
    45. USE [FrameworkOne]  
    46. GO  
    47.   
    48. SET ANSI_NULLS ON  
    49. GO  
    50.   
    51. SET QUOTED_IDENTIFIER ON  
    52. GO  
    53.   
    54. CREATE TABLE [dbo].[StudentAssignment](  
    55.     [StudentId] [intNOT NULL,  
    56.     [AssignmentId] [intNOT NULL,  
    57.     [Marks] [floatNULL,  
    58.  CONSTRAINT [PK_EmployeeAssignment] PRIMARY KEY CLUSTERED   
    59. (  
    60.     [StudentId] ASC,  
    61.     [AssignmentId] ASC  
    62. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
    63. ON [PRIMARY]  
    64.   
    65. GO  
    66.   
    67. ALTER TABLE [dbo].[StudentAssignment]  WITH CHECK ADD  CONSTRAINT [FK_EmployeeAssignment_Assignment1] FOREIGN KEY([AssignmentId])  
    68. REFERENCES [dbo].[Assignment] ([AssignmentId])  
    69. GO  
    70.   
    71. ALTER TABLE [dbo].[StudentAssignment] CHECK CONSTRAINT [FK_EmployeeAssignment_Assignment1]  
    72. GO  
    73.   
    74. ALTER TABLE [dbo].[StudentAssignment]  WITH CHECK ADD  CONSTRAINT [FK_EmployeeAssignment_Employee1] FOREIGN KEY([StudentId])  
    75. REFERENCES [dbo].[Student] ([StudentId])  
    76. GO  
    77.   
    78. ALTER TABLE [dbo].[StudentAssignment] CHECK CONSTRAINT [FK_EmployeeAssignment_Employee1]  
    79. GO  
  • Insert some data in three table Student, Assignment, StudentAssignment

    1. SELECT [StudentId] ,[NameFROM [FrameworkOne].[dbo].[Student]  


    2. SELECT [AssignmentId], [Name], [TotalMarks] FROM [FrameworkOne].[dbo].[Assignment]  


    3. SELECT [StudentId], [AssignmentId], [Marks] FROM [FrameworkOne].[dbo].[StudentAssignment]  

Adding Entity Framework

  • In FrameworkOne.Domain project, add ADO.NET Entity Data Model (FrameworkOneContext).


  • Select Code First from database option.


  • From Choose Your Data Connection screen, create connection string to FrameworkOne database and save as FrameworkOneContext.


  • From Choose Your Database Objects and Settings, select Student, Assignment, StudentAssignment tables.


  • Domain project looks, as shown below.


Creating Generic Repository and Unit of Work

  • In FrameworkOne.Repository project, first add folder Interface. Now, add a new interface file IGenericRepository.cs.


  • IGenericRepository interface will look like this
    1. namespace FrameworkOne.Repository.Interface  
    2. {  
    3.     public interface IGenericRepository<T> where T : class  
    4.     {  
    5.     }  
    6. }  
  • Add four methods to IGenericRepository interface – GetItem, Insert, Update, Delete
    1. namespace FrameworkOne.Repository.Interface  
    2. {  
    3.     public interface IGenericRepository<T> where T : class  
    4.     {  
    5.         IQueryable<T> GetAll();  
    6.   
    7.         void Insert(T entity);  
    8.   
    9.         void Update(T entity);  
    10.   
    11.         void Delete(T entity);  
    12.     }  
    13. }  
  • In FrameworkOne.Repository project, add a class file GenericRepository.


  • Implement IGenericRepository interface in GenericRepository class
    1. namespace FrameworkOne.Repository.Interface  
    2. {  
    3.     public interface IGenericRepository<T> where T : class  
    4.     {  
    5.         IQueryable<T> GetAll();  
    6.   
    7.         void Insert(T entity);  
    8.   
    9.         void Update(T entity);  
    10.   
    11.         void Delete(T entity);  
    12.     }  
    13. }  
  • In FrameworkOne.Repository project, add a new interface file IUnitOfWork.


  • Add DbContext property and Save method to IUnitOfWork interface
    1. namespace FrameworkOne.Repository.Interface  
    2. {  
    3.     public interface IUnitOfWork : IDisposable  
    4.     {  
    5.         FrameworkOneContext DbContext { get; }  
    6.   
    7.         int Save();  
    8.     }  
    9. }  
  • In FrameworkOne.Repository project, add a class file UnitOfWork.


  • Implement IUnitOfWork interface in UnitOfWork class. UnitOfWork looks, as shown below.
    1. namespace FrameworkOne.Repository  
    2. {  
    3.     public class UnitOfWork : IUnitOfWork  
    4.     {  
    5.         private FrameworkOneContext _context;  
    6.   
    7.         public UnitOfWork(FrameworkOneContext context)  
    8.         {  
    9.             this._context = context;  
    10.             this._context.Configuration.LazyLoadingEnabled = false;  
    11.         }  
    12.   
    13.         public FrameworkOneContext DbContext  
    14.         {  
    15.             get  
    16.             {  
    17.                 return this._context;  
    18.             }  
    19.         }  
    20.   
    21.         public int Save()  
    22.         {  
    23.             return this._context.SaveChanges();  
    24.         }  
    25.   
    26.         public void Dispose(bool disposing)  
    27.         {  
    28.             if (disposing)  
    29.             {  
    30.                 if (this._context != null)  
    31.                 {  
    32.                     this._context.Dispose();  
    33.                     this._context = null;  
    34.                 }  
    35.             }  
    36.         }  
    37.   
    38.         public void Dispose()  
    39.         {  
    40.             Dispose(true);  
    41.             GC.SuppressFinalize(this);  
    42.         }  
    43.     }  
    44. }  
  • GenericRepository class will implement methods from IGenericRepository. GenericRepository looks like this. It has definition for GetAll, Insert, Update, Delete methods.
    1. namespace FrameworkOne.Repository  
    2. {  
    3.     public class GenericRepository<T> : IGenericRepository<T> where T : class  
    4.     {  
    5.         protected FrameworkOneContext _context;  
    6.   
    7.         public GenericRepository(IUnitOfWork unitOfWork)  
    8.         {  
    9.             _context = unitOfWork.DbContext;  
    10.         }  
    11.   
    12.         public IQueryable<T> GetAll()  
    13.         {  
    14.             IQueryable<T> query = _context.Set<T>();  
    15.             return query;  
    16.         }  
    17.   
    18.         public void Insert(T entity)  
    19.         {  
    20.             _context.Set<T>().Add(entity);  
    21.         }  
    22.   
    23.         public void Update(T entity)  
    24.         {  
    25.             _context.Set<T>().Attach(entity);  
    26.         }  
    27.   
    28.         public void Delete(T entity)  
    29.         {  
    30.             _context.Set<T>().Remove(entity);  
    31.         }  
    32.     }  
    33. }  
  • Rebuild the solution.


Creating Model classes

  • In FrameworkOne.Model project, add StudentModel class.


  • StudentModel class have two properties - StudentId and Name
    1. namespace FrameworkOne.Model  
    2. {  
    3.     public class StudentModel  
    4.     {  
    5.         public int StudentId { get; set; }  
    6.   
    7.         public string Name { get; set; }  
    8.     }  
    9. }  
  • In FrameworkOne.Model project, add AssignmentModel class.


  • AssignmentModel class have three properties – AssignmentId, Name, TotallMarks
    1. namespace FrameworkOne.Model  
    2. {  
    3.     public class AssignmentModel  
    4.     {  
    5.         public int AssignmentId { get; set; }  
    6.   
    7.         public string Name { get; set; }  
    8.   
    9.         public int TotalMarks { get; set; }  
    10.     }  
    11. }  

Creating Application Logic classes

  • In FrameworkOne.Application project, add an Interface folder.


  • In FrameworkOne.Application, add IStudentLogic interface.


  • IStudentLogic interface have three methods,

    • GetData method
      It is required for fetching the data from Repository in Domain classes. It loads the data in Model classes and pass to the Web layer.

    • SaveData method
      It is required for saving new data.

    • Dispose method
      It is required for disposing unitofwork object.
      1. namespace FrameworkOne.Application.Interface  
      2. {  
      3.     public interface IStudentLogic  
      4.     {  
      5.         List<StudentModel> GetData();  
      6.   
      7.         void SaveData(StudentModel studentModel);  
      8.   
      9.         void Dispose();  
      10.     }  
      11. }  
  • In FrameworkOne.Application, add StudentLogic class.


  • Implement IStudentLogic interface in StudentLogic class
    1. namespace FrameworkOne.Application  
    2. {  
    3.     public class StudentLogic : IStudentLogic  
    4.     {  
    5.         public void Dispose()  
    6.         {  
    7.             throw new NotImplementedException();  
    8.         }  
    9.   
    10.         public List<StudentModel> GetData()  
    11.         {  
    12.             throw new NotImplementedException();  
    13.         }  
    14.   
    15.         public void SaveData(StudentModel studentModel)  
    16.         {  
    17.             throw new NotImplementedException();  
    18.         }  
    19.     }  
    20. }  
  • StudentLogic class have,

    • Constructor is required for setting up object references (using DI).
    • GetData method is required to fetch the data from Repository in Domain classes. It loads the data in Model classes and pass to the Web layer.
    • SaveData method to save new data.
    • Dispose method for disposing unitofwork object.
      1. namespace FrameworkOne.Application  
      2. {  
      3.     public class StudentLogic : IStudentLogic  
      4.     {  
      5.         private IUnitOfWork unitOfWork;  
      6.         private IGenericRepository<Student> studentRepository;  
      7.   
      8.         public StudentLogic(IUnitOfWork unitOfWork, IGenericRepository<Student> studentRepository)  
      9.         {  
      10.             this.unitOfWork = unitOfWork;  
      11.             this.studentRepository = studentRepository;  
      12.         }  
      13.   
      14.         public List<StudentModel> GetData()  
      15.         {  
      16.             List<Student> students = studentRepository.GetAll().ToList();  
      17.   
      18.             List<StudentModel> studentModels = new List<StudentModel>();  
      19.             foreach (Student student in students)  
      20.             {  
      21.                 StudentModel studentModel = new StudentModel()  
      22.                 {  
      23.                     StudentId = student.StudentId,  
      24.                     Name = student.Name  
      25.                 };  
      26.   
      27.                 studentModels.Add(studentModel);  
      28.             }  
      29.   
      30.             return studentModels;  
      31.         }  
      32.   
      33.         public void SaveData(StudentModel studentModel)  
      34.         {  
      35.             Student student = new Student() { StudentId = studentModel.StudentId, Name = studentModel.Name };  
      36.             studentRepository.Insert(student);  
      37.   
      38.             this.unitOfWork.Save();  
      39.         }  
      40.   
      41.         public void Dispose()  
      42.         {  
      43.             this.unitOfWork.Dispose();  
      44.         }  
      45.     }  
      46. }  

Creating ASP.NET MVC Controller classes

  • Open FrameworkOne.Web project HomeController class.


  • HomeController class is updated to test the overall flow.

    • Constructor is required for setting up object references (using DI).
    • Index method is modified to test fetching of student data, using StudentLogic.GetData method.
    • About method is modified to test saving of new student data, using Student.SaveData method.
      1. namespace FrameworkOne.Web.Controllers  
      2. {  
      3.     public class HomeController : Controller  
      4.     {  
      5.         private IStudentLogic studentLogic;  
      6.   
      7.         public HomeController(IStudentLogic studentLogic)  
      8.         {  
      9.             this.studentLogic = studentLogic;  
      10.         }  
      11.   
      12.         public ActionResult Index()  
      13.         {  
      14.             // Fetch Data  
      15.             List<StudentModel> studentModels = studentLogic.GetData();  
      16.   
      17.             return View();  
      18.         }  
      19.   
      20.         public ActionResult About()  
      21.         {  
      22.             // Save Data  
      23.             StudentModel student = new StudentModel() { StudentId = 100004, Name = "Student Four" };  
      24.   
      25.             this.studentLogic.SaveData(student);  
      26.             this.studentLogic.Dispose();  
      27.   
      28.             ViewBag.Message = "Your application description page.";  
      29.   
      30.             return View();  
      31.         }  
      32.   
      33.         public ActionResult Contact()  
      34.         {  
      35.             ViewBag.Message = "Your contact page.";  
      36.   
      37.             return View();  
      38.         }  
      39.     }  
      40. }  
  • Rebuild the whole solution.


Configuration tasks

  • Copy FrameworkOneContext connectionstring from FrameworkOne.Domain App.config to FrameworkOne.Web web.config.




  • Register UnitOfWork, GenericRepository and StudentLogic in FrameworkOne.Web UnityConfig class
    1. public static void RegisterTypes(IUnityContainer container)  
    2.         {  
    3.             // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.  
    4.             // container.LoadConfiguration();  
    5.   
    6.             container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());  
    7.             container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));  
    8.             container.RegisterType<IStudentLogic, StudentLogic>();  
    9.         }  
  • In FrameworkOne.Domain project FrameworkOneContext class set LazyLoadingEnabled to false in the constructor
    1. public partial class FrameworkOneContext : DbContext  
    2.     {  
    3.         public FrameworkOneContext()  
    4.             : base("name=FrameworkOneContext")  
    5.         {  
    6.             this.Configuration.LazyLoadingEnabled = false;  
    7.         }          
    8.  }  

Run and Test solution

  • Put breakpoints on HomeController’s Index, About methods and run the solution.
  • Index method will be called. You can see the three rows returned in studentModel variable.
  • Click on About link at the top. It will save one new row in Student table. Check the database for the new row with StudentId – 100004.
    1. SELECT [StudentId] ,[Name] FROM [FrameworkOne].[dbo].[Student]