Saturday, November 30, 2019

CRUD using the Repository Pattern in MVC

This article introduces the repository pattern in an MVC applications. We are developing an application for a Book entity on which we can perform Create, Read, Update and Delete operations. To keep the article simple and make it easy to understand the repository pattern, we use a single book entity in the application.
 
Overview of Repository Pattern
 
The repository pattern is intended to create an abstraction layer between the data access layer and the business logic layer of an application. It is a data access pattern that prompts a more loosely coupled approach to data access. We create the data access logic in a separate class, or set of classes, called a repository with the responsibility of persisting the application's business model.
 
In this article we will implement a "One-per business model" approach to design a repository in which there is a repository class for each entity type. For the Book entity type we'll create a repository interface and a repository class. When we instantiate the repository in our controller, we'll use the interface so that the controller will accept a reference to any object that implements the repository interface. When the controller runs under a web server, it receives a repository that works with the Entity Framework.
 
MVC controllers interact with repositories to load and persist an application business model. By taking advantage of dependency injection (DI), repositories can be injected into a controller's constructor. the following diagram shows the relationship between the repository and Entity Framework data context, in which MVC controllers interact with the repository rather than directly with Entity Framework.

Repository Pattern in MVC
Overview of Entity Framework
 
The ADO.NET Entity Framework is an Object Relational Mapper (ORM) included with the .NET framework. It basically generates business objects and entities according to the database tables. It provides basic CRUD operations, easily managing relationships among entities with the ability to have an inheritance relationship among entities.
 
When using EF, we interact with an entity model instead of the application's relational database model. This abstraction allows us to focus on business behavior and the relationships among entities. We use the Entity Framework data context to perform queries. When one of the CRUD operations is invoked, the Entity Framework will generate the necessary SQL to perform the operation.
 
Work with Data in Entity Framework
 
The ADO.NET Entity Framework allows developers to choose any one approach among three possible approaches: Database First, Model First and Code First.
 
Database First: It is a more data-centric design that is based on an existing database. The Entity Framework is able to generate a business model based on the tables and columns in a relational database. The information about our database structure (store schema), our data model (conceptual model), and the mapping among them is stored in XML in an .edmx file.
 
Database First 
 
Model First: In this approach, we don't have an existing database and the Entity Framework offers a designer that can create a conceptual data model. It also uses an .edmx file to store the model and mapping information. When the model has been created then the Entity Framework designer can generate the database schema that can be used to create the database.
 
Model First 
 
Code First: Whether you have an existing database or not, you can code your own classes and properties that correspond to tables and columns and use them with the Entity Framework without an .edmx file. In this approach the Entity Framework does not leverage any kind of configuration file (.edmx file) to store the database schema, because the mapping API uses these conventions to generate the database schema dynamically at runtime.
 
Code First 
 
Currently, the Entity Framework Code First approach does not support mapping to Stored Procedures. The ExecuteSqlCommand() and SqlQuery() methods can be used to execute Stored Procedures.
 
In this article we use the Code First approach of Entity Framework to develop a data access layer in an MVC application. The driving force behind the Code First approach is the ability to use POCO (Plain Old CLR Objects) classes. Code First uses a set of conventions to map POCO classes but that can be changed using code first data annotation:
  1. Primary Key is based on property name Id or ClassNameId. In other words, suppose we have a Book entity that has property Id or BookId that will be the primary key in the generated Books table.
  2. Table names are defined using the pluralized form of the entity class name. In other words, suppose we have an entity Book and that entity would generate a table in the database, that table name will be Books.
  3. The column names of the table are derived from the property names of the entity. Column names can be changed using Code First data annotation.
  4. The default connection string matches the name of the DataContext class.
Code First data annotation
 
The Entity Framework includes several data annotation attributes we can use to control how the framework handles mapping entities. Here we have a basic data annotation that will be used for the Book entity.
 
Sr.NoPropertyDescription
1.TableUsed to define the table name to use for an entity.
2.ColumnThe database table column name, ordinal position, and data type to map the property to
3.KeyOne or more properties used to uniquely identify an entity.
4.RequiredMarks a property as being required (non-nullable).
5.MaxLengthThe maximum length for the property (column).
6.MinLengthThe minimum length for the property (column).
7.StringLengthDefine the minimum and maximum length of a field.
 
An MVC Application Using the Repository Pattern
 
We now have sufficient theory. Let's now start the real fun of implementing it in an MVC application. We create an MVC application (BookStore Application) using Visual Studio 2010, MVC 4 and Entity Framework 5.
 
Step 1 - From the Visual Studio Start Page, click "New Project"
 
Step 2 - Choose "MVC 4 Project Template"
 
We get the New Project window in which we choose "MVC 4 Project Template" and provide an appropriate name to both the Project and Solution then click on the "Ok" button.
 
We then get another window to choose a MVC application template. We choose "Internet Application" from the templates and "Razor" as the view engine.
 
ASP.NET MVC 4 Project 
 
Click on "Ok" and our default application is ready.
 
We are developing a MVC application using MVC 4 with razor view engine so our default MVC internet application includes an EntityFramework reference so there is no need to add a reference or install a Nuget package for Entity Framework.
 
Step 3 - Create Model
 
We create a model for Book under the Models folder. This model is actually a class that uses an entity and entity set. We create the Book class under Models and implements Code First data annotation for database table that will be created by it.
  1. using System.ComponentModel.DataAnnotations;  
  2. using System.ComponentModel.DataAnnotations.Schema;  
  1. namespace BookStore.Models  
  2. {  
  3.     public class Book  
  4.     {  
  5.         [Key]  
  6.         public int Id { getset; }  
  7.         [Required]  
  8.         [MaxLength(30)]  
  9.         public string Title { getset; }  
  10.         public string Authers { getset; }  
  11.   
  12.         [Column("Year")]  
  13.         [Display(Name = "Publish Year")]  
  14.         public string publishYear { getset; }  
  15.   
  16.         [Column("Price")]  
  17.         [Display(Name = "Price")]  
  18.         public decimal BasePrice { getset; }  
  19.     }  
  20. }  
Our Book model is ready and now we proceed to the Data Context.
 
Step 4 - Create Data Context class
 
The ADO.NET Entity Framework Code First data access approach requires us to create a data access context class that inherits from the DbContext class. This class must contain properties for each of the entities in the domain model.
 
Here is an Entity Framework Code First data context that contains one entity, Book. We create this context class (BookContext) under the new folder DAL. Below the definition of the data context class that has a constructor to pass a connection string that is defined in web.config file. By default the connection string name is the same name as the data context class but we can use a different name for the connection string so that all the data contexts can use a single connection string.
  1. using System.Data.Entity;  
  2. using BookStore.Models;  
  3.   
  4. namespace BookStore.DAL  
  5. {  
  6.     public class BookContext : DbContext  
  7.     {  
  8.         public BookContext()  
  9.             : base("name=BookStoreConnectionString")  
  10.         {  
  11.         }  
  12.         public DbSet<Book> Books { getset; }  
  13.     }  
  14. }  
The connection string in the web.config file is:
  1. <connectionStrings>  
  2.   <add name="BookStoreConnectionString" connectionString="Data Source=sandeepss-PC;Initial Catalog=BookStore;User ID=shekhawat; Password=******" providerName="System.Data.SqlClient" />  
  3. </connectionStrings>  
Step 5 - Create Repository
 
In the DAL folder create an IBookRepository interface that has the filename IBookRepository.cs. This interface code declares a typical set of CRUD methods, including two read methods; one that returns all Book entity sets, and one that finds a single Book entity by ID.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using BookStore.Models;  
  4.   
  5. namespace BookStore.DAL  
  6. {  
  7.     public interface IBookRepository : IDisposable  
  8.     {  
  9.         IEnumerable<Book> GetBooks();  
  10.         Book GetBookByID(int bookId);  
  11.         void InsertBook(Book book);  
  12.         void DeleteBook(int bookID);  
  13.         void UpdateBook(Book book);  
  14.         void Save();  
  15.     }  
  16. }  
In the DAL folder, create a class file named "BookRepository.cs" The class file implements the "IBookRepository" interface and the "IBookRepository" inherits the IDisposable interface so the IDisposable interface is indirectly implemented by the BookRespository class. The database context is defined in a class variable, and the constructor expects the calling object to pass in an instance of the context.
 
Here we are passing the BookContext instance to the constructor.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using BookStore.Models;  
  5. using System.Data;  
  6.   
  7. namespace BookStore.DAL  
  8. {  
  9.     public class BookRepository : IBookRepository  
  10.     {  
  11.         private BookContext _context;  
  12.         public BookRepository(BookContext bookContext)  
  13.         {  
  14.             this._context = bookContext;  
  15.         }  
  16.         public IEnumerable<Book> GetBooks()  
  17.         {  
  18.             return _context.Books.ToList();  
  19.         }  
  20.         public Book GetBookByID(int id)  
  21.         {  
  22.             return _context.Books.Find(id);  
  23.         }  
  24.         public void InsertBook(Book book)  
  25.         {  
  26.             _context.Books.Add(book);  
  27.         }  
  28.         public void DeleteBook(int bookID)  
  29.         {  
  30.             Book book = _context.Books.Find(bookID);  
  31.             _context.Books.Remove(book);  
  32.         }  
  33.         public void UpdateBook(Book book)  
  34.         {  
  35.             _context.Entry(book).State = EntityState.Modified;  
  36.         }  
  37.         public void Save()  
  38.         {  
  39.             _context.SaveChanges();  
  40.         }  
  41.         private bool disposed = false;  
  42.         protected virtual void Dispose(bool disposing)  
  43.         {  
  44.             if (!this.disposed)  
  45.             {  
  46.                 if (disposing)  
  47.                 {  
  48.                     _context.Dispose();  
  49.                 }  
  50.             }  
  51.             this.disposed = true;  
  52.         }  
  53.         public void Dispose()  
  54.         {  
  55.             Dispose(true);  
  56.             GC.SuppressFinalize(this);  
  57.         }  
  58.     }  
  59. }  
Step 6 - Create Controller and Views for CRUD operations
 
Now our model, data context and repository are ready, so now proceed to the Controller. So we create a controller (BookController) for Book under the Controllers folder.
 
We need the following "using" in the controller to perform CRUD operations:
  1. using System.Data;  
  2. using System.Linq;  
  3. using System.Web.Mvc;  
  4. using BookStore.DAL;  
  5. using BookStore.Models;
We create an instance of the Book repository interface in the Book Controller and initialize the book repository in the constructor of Book Controller (BookController.cs) as in the following:
  1. private IBookRepository _bookRepository;  
  2. public BookController()  
  3. {  
  4.     this._bookRepository = new BookRepository(new BookContext());  
  5. }  
We will use Scaffold templates to create a view for the CRUD operations. We use five scaffold templates, List, Create, Edit, Delete and Details. So create a controller that has post and get action results depending on the operation.
 
Operation 1 - Show List of All Books
 
Create an action in the controller named Index. The Index action returns a list of books.
  1. public ActionResult Index()  
  2. {  
  3.     var books = from book in _bookRepository.GetBooks()  
  4.     select book;  
  5.     return View(books);  
  6. }  
Now we create a view. To create the view usethe following procedure:
  1. Compile the source code successfully
  2. Right-click on Action Method Index.
  3. The View Name is already filled in so don't change it.
  4. The View Engine already selected Razor so don't change it.
  5. Check the checkbox "Create a strongly-typed-view" because we are creating a strongly typed view.
  6. Choose the Model class "Book" so it can be bound with the view.
  7. Choose "List" from the Scaffold template so rapid development can be done and we get the view with the code for showing the list of Books.
  8. Check both checkboxes "Reference script libraries" and "Use a layout or master page".
These are the common steps to follow for each operation and the only change will be the Scaffold template. The following picture shows the Index view that has a List Scaffold template.
 
Scaffold template 
 
Operation 2 - Show Details of Book
 
Create an action in the controller named Details. The Details action returns the details of the book.
  1. public ViewResult Details(int id)  
  2. {  
  3.    Book student = _bookRepository.GetBookByID(id);  
  4.    return View(student);  
  5. }  
Now we create the view. To create the view use the following procedure:
  1. Right-click on Action Method Details.
  2. The View Name is already filled in so don't change it.
  3. The View Engine already selected Razor so don't change it.
  4. Check the checkbox "Create a strongly-typed-view" because we are creating a strongly typed view.
  5. Choose the Model class "Book" so it can be bound with the view.
  6. Choose "Details" from the Scaffold template so we can do rapid development and we get the view with the code for showing the details of the book.
  7. Check both the checkboxes "Reference script libraries" and "Use a layout or master page".
Operation 3 - Create New Book
 
Create two actions in the controller, one for the new book to create a view (Get Action) and another for submitting new book details to the repository (Post Action). These have the same name, Create.
  1. public ActionResult Create()  
  2. {  
  3.     return View(new Book());  
  4. }   
  5. [HttpPost]  
  6. public ActionResult Create(Book book)  
  7. {  
  8.     try  
  9.     {  
  10.         if (ModelState.IsValid)  
  11.         {  
  12.             _bookRepository.InsertBook(book);  
  13.             _bookRepository.Save();  
  14.             return RedirectToAction("Index");  
  15.         }  
  16.     }  
  17.     catch (DataException)  
  18.     {                 
  19.        ModelState.AddModelError("""Unable to save changes. Try again, and if the problem persists see your system administrator.");  
  20.     }  
  21.        return View(book);  
  22. }  
Now we create a view. To create the view use the following procedure:
  1. Right-click on the Action Method Create (GET).
  2. The View Name is already filled in so don't change it.
  3. The View Engine already selected Razor so don't change it.
  4. Check the checkbox "Create a strongly-typed-view" because we are creating a strongly typed view.
  5. Choose the Model class "Book" so it can be bound with the view.
  6. Choose "Create" from the Scaffold template so we can do rapid development and we get the view for creating the new book.
  7. Check both checkboxes "Reference script libraries" and "Use a layout or master page".
Operation 4 - Update Book Details
 
Create two actions in the controller, one for an existing book edit view (Get Action) and another for submitting the updated book details to the repository (Post Action). These have the same name Create. The Get action fills in the book details on the form by the id of the book so we would pass the id to the action.
  1. public ActionResult Edit(int id)  
  2. {  
  3.     Book book = _bookRepository.GetBookByID(id);  
  4.     return View(book);  
  5. }    
  6. [HttpPost]  
  7. public ActionResult Edit(Book book)  
  8. {  
  9.     try  
  10.     {  
  11.         if (ModelState.IsValid)  
  12.         {  
  13.             _bookRepository.UpdateBook(book);  
  14.             _bookRepository.Save();  
  15.             return RedirectToAction("Index");  
  16.         }  
  17.     }  
  18.     catch (DataException)  
  19.     {                 
  20.         ModelState.AddModelError("""Unable to save changes. Try again, and if the problem persists see your system administrator.");  
  21.     }  
  22.     return View(book);  
  23. }  
Now we create the view. To create the view use the following procedure:
  1. Right-click on Action Method Edit (GET).
  2. The View Name is already filled in so don't change it.
  3. The View Engine already selected Razor so don't change it.
  4. Check the checkbox "Create a strongly-typed-view" because we are creating a strongly typed view.
  5. Choose the Model class "Book" so it can be bound with the view.
  6. Choose "Edit" from the Scaffold template so we can do rapid development and we get the view for updating an existing book.
  7. Check both checkboxes "Reference script libraries" and "Use a layout or master page".
Operation 5 - Delete Book
 
Create two actions in the controller, one to show the details of the book after clicking on the Delete link (Get Action) and another to Delete the book (Post Action). One Delete action but another overrides the Delete Action that overrides the DeleteConfirmed method. The Get action fills in book details on the form by the id of the book then the Post action is performed on it. 
  1. public ActionResult Delete(int id, bool? saveChangesError)  
  2. {  
  3.     if (saveChangesError.GetValueOrDefault())  
  4.     {  
  5.         ViewBag.ErrorMessage = "Unable to save changes. Try again, and if the problem persists see your system administrator.";  
  6.     }  
  7.     Book book = _bookRepository.GetBookByID(id);  
  8.     return View(book);  
  9. }  
  10. [HttpPost, ActionName("Delete")]  
  11. public ActionResult DeleteConfirmed(int id)  
  12. {  
  13.     try  
  14.     {  
  15.         Book book = _bookRepository.GetBookByID(id);  
  16.         _bookRepository.DeleteBook(id);  
  17.         _bookRepository.Save();  
  18.     }  
  19.     catch (DataException)  
  20.     {                
  21.         return RedirectToAction("Delete",  
  22.            new System.Web.Routing.RouteValueDictionary {  
  23.         { "id", id },  
  24.         { "saveChangesError"true } });  
  25.     }  
  26.     return RedirectToAction("Index");  
  27. }  
Now we create the view. To create the view use the following procedure:
  1. Right-click on Action Method Delete.
  2. The View Name is already filled in so don't change it.
  3. The View Engine already selected Razor so don't change it.
  4. Check the checkbox "Create a strongly-typed-view" because we are creating a strongly typed view.
  5. Choose the Model class "Book" so it can be bound with the view.
  6. Choose "Delete" from the Scaffold template so we can do rapid development and we get the view of the delete for the existing book.
  7. Check both checkboxes "Reference script libraries" and "Use a layout or master page".
Now the view and action are ready to perform CRUD operations.
 
MVC View 
 
Step 7 - Run the application

Call the Book controller http://localhost:4736/Book from the browser and we get the default empty list of books.
 
ASP.Net MVC

Check that our database is created by the data context when the application called the book entity.
 
Database

Create new books.
 
Create new book
 
Click on the "Create" button and a new book is created.
 
Create new book 
 
Now click on the "Edit" link button of a row and show the book details in the edited form.
 
Edit book 
 
Click on the "Save" button and see the updated book list.
 
Updated book list 
 
Now click on the "Details" button and we get the details of the book.
 
Book Details 
 
Click on back to list and now click on the delete link button then we get the delete confirmation window.
 
Delete a book 
 
Click on the delete button and the book is deleted.
 
We perform all CRUD operations successfully. I didn't use many basic screenshots here to create the controller, model and views because I know that you are already familiar with these. 

No comments:

Post a Comment

Lab 09: Publish and subscribe to Event Grid events

  Microsoft Azure user interface Given the dynamic nature of Microsoft cloud tools, you might experience Azure UI changes that occur after t...