Introduction
This article introduces how to implement a generic repository pattern in ASP.NET Core, using Entity Framework Core. The generic repository pattern implements in a separate class library project. It uses the "Code First" development approach and creates a database from a model, using migration. This article demonstrates a sample Application, which has one too many relationships in ASP.NET Core with Entity Framework Core. The Application source code is available for download on the MSDN sample https://code.msdn.microsoft.com/Generic-Repository-Pattern-f133bca4).
The repository pattern is intended to create an Abstraction layer between the Data Access layer and Business Logic layer of an Application. It is a data access pattern that prompts a more loosely coupled approach to data access. We create a generic repository, which queries the data source for the data, maps the data from the data source to a business entity, and persists changes in the business entity to the data source.
Implement Generic Repository Pattern
To implement generic repository pattern, create two projects - one is an ASP.NET Core Web Application and another is a class library project, which are GR.Web and GR.Data respectively in the solution. The class library is named as SA.Data project, which has data access logic with a generic repository, entities, and context, so we install Entity Framework Core in this project.
There is an unsupported issue of EF Core 1.0.0-preview2-final with "NETStandard.Library": "1.6.0". Thus, we have changed the target framework to netstandard1.6 > netcoreapp1.0. We modify the project.json file of GR.Data project to implement Entity Framework Core in this class library project. Thus, the code snippet, mentioned below for the project.json file after modification.
- {
- "dependencies": {
- "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
- "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
- },
- "frameworks": {
- "netcoreapp1.0": {
- "imports": ["dotnet5.6", "portable-net45+win8"]
- }
- },
- "tools": {
- "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
- },
- "version": "1.0.0-*"
- }
This Application uses the Entity Framework Code First approach, so the project GR.Data contains entities that are required in the Application's database. The GR.Data project holds three entities, one is the BaseEntity class that has common properties that will be inherited by each entity. The code snippet mentioned below is for the BaseEntity class.
- using System;
- namespace GR.Data {
- public class BaseEntity {
- public Int64 Id {
- get;
- set;
- }
- public DateTime AddedDate {
- get;
- set;
- }
- public DateTime ModifiedDate {
- get;
- set;
- }
- public string IPAddress {
- get;
- set;
- }
- }
- }
The Author and Book entities have one to many relationships, as shown below.
Author-Book Relationship
Now, we create an Author entity, which is inherited from BaseEntity class. The code snippet, mentioned below is for the Author entity.
- using System.Collections.Generic;
- namespace GR.Data {
- public class Author: BaseEntity {
- public string FirstName {
- get;
- set;
- }
- public string LastName {
- get;
- set;
- }
- public string Email {
- get;
- set;
- }
- public virtual ICollection < Book > Books {
- get;
- set;
- }
- }
- }
Now, we define the configuration for the Author entity that will be used when the database table will be created by the entity. The following is a code snippet for the Author mapping entity (AuthorMap.cs).
- using Microsoft.EntityFrameworkCore.Metadata.Builders;
- namespace GR.Data {
- public class AuthorMap {
- public AuthorMap(EntityTypeBuilder < Author > entityBuilder) {
- entityBuilder.HasKey(t => t.Id);
- entityBuilder.Property(t => t.FirstName).IsRequired();
- entityBuilder.Property(t => t.LastName).IsRequired();
- entityBuilder.Property(t => t.Email).IsRequired();
- }
- }
- }
Now, we create a Book entity, which inherits from the BaseEntity class. The code snippet, mentioned below is for the Book entity.
- using System;
- namespace GR.Data {
- public class Book: BaseEntity {
- public Int64 AuthorId {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public string ISBN {
- get;
- set;
- }
- public string Publisher {
- get;
- set;
- }
- public virtual Author Author {
- get;
- set;
- }
- }
- }
Now, we define the configuration for the Book entity that will be used when the database table will be created by the entity. The code snippet is mentioned below for the Book mapping entity (BookMap.cs).
- using Microsoft.EntityFrameworkCore.Metadata.Builders;
- namespace GR.Data {
- public class BookMap {
- public BookMap(EntityTypeBuilder < Book > entityBuilder) {
- entityBuilder.HasKey(t => t.Id);
- entityBuilder.Property(t => t.Name).IsRequired();
- entityBuilder.Property(t => t.ISBN).IsRequired();
- entityBuilder.Property(t => t.Publisher).IsRequired();
- entityBuilder.HasOne(e => e.Author).WithMany(e => e.Books).HasForeignKey(e => e.AuthorId);
- }
- }
- }
The GR.Data project also contains DataContext. The ADO.NET Entity Framework Code First data access approach needs to create a data access context class that inherits from the DbContext class, so we create a context class ApplicationContext (ApplicationContext.cs) class.
In this class, we override the OnModelCreating() method. This method is called when the model for a context class (ApplicationContext) has been initialized, but before the model has been locked down and used to initialize the context such that the model can be further configured before it is locked down. The following is the code snippet for the context class.
- using Microsoft.EntityFrameworkCore;
- namespace GR.Data {
- public class ApplicationContext: DbContext {
- public ApplicationContext(DbContextOptions < ApplicationContext > options): base(options) {}
- protected override void OnModelCreating(ModelBuilder modelBuilder) {
- base.OnModelCreating(modelBuilder);
- new AuthorMap(modelBuilder.Entity < Author > ());
- new BookMap(modelBuilder.Entity < Book > ());
- }
- }
- }
The DbContext must have an instance of DbContextOptions in order to execute. We will use dependency injection, so we pass options via constructor dependency injection.
ASP.NET Core is designed from the ground to support and leverage dependency injection. Thus, we create generic repository interface for the entity operations, so that we can develop loosely coupled Applications. The code snippet, mentioned below is for the IRepository interface.
- using System.Collections.Generic;
- namespace GR.Data {
- public interface IRepository < T > where T: BaseEntity {
- IEnumerable < T > GetAll();
- T Get(long id);
- void Insert(T entity);
- void Update(T entity);
- void Delete(T entity);
- }
- }
Now, let's create a repository class to perform CRUD operations on the entity, which implements IRepository. This repository contains a parameterized constructor with a parameter as Context, so when we create an instance of the repository, we pass a context so that the entity has the same context. The code snippet is mentioned below for the Repository class under GR.Data project.
- using Microsoft.EntityFrameworkCore;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace GR.Data {
- public class Repository < T > : IRepository < T > where T: BaseEntity {
- private readonly ApplicationContext context;
- private DbSet < T > entities;
- string errorMessage = string.Empty;
- public Repository(ApplicationContext context) {
- this.context = context;
- entities = context.Set < T > ();
- }
- public IEnumerable < T > GetAll() {
- return entities.AsEnumerable();
- }
- public T Get(long id) {
- return entities.SingleOrDefault(s => s.Id == id);
- }
- public void Insert(T entity) {
- if (entity == null) {
- throw new ArgumentNullException("entity");
- }
- entities.Add(entity);
- context.SaveChanges();
- }
- public void Update(T entity) {
- if (entity == null) {
- throw new ArgumentNullException("entity");
- }
- context.SaveChanges();
- }
- public void Delete(T entity) {
- if (entity == null) {
- throw new ArgumentNullException("entity");
- }
- entities.Remove(entity);
- context.SaveChanges();
- }
- }
- }
We developed entity and context which are required to create a database but we will come back to this after creating the Web Application project.
A Web Application Using the Generic Repository Pattern
Now, we create an MVC Application (GR.Web). This is our second project of the Application. This project contains a user interface for both author and book entities database operations and the controller to do these operations.
As the concept of dependency injection is central to the ASP.NET Core Application, we register both context and repository to the dependency injection during the Application start up. Thus, we register these as a Service in the ConfigureServices method in the StartUp class.
- public void ConfigureServices(IServiceCollection services) {
-
- services.AddApplicationInsightsTelemetry(Configuration);
- services.AddMvc();
- services.AddDbContext < ApplicationContext > (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
- services.AddScoped(typeof(IRepository < > ), typeof(Repository < > ));
- }
-
Here, the DefaultConnection is a connection string that defined inappsettings.json file as per the following code snippet.
- {
- "ConnectionStrings": {
- "DefaultConnection": "Data Source=DESKTOP-RG33QHE;Initial Catalog=GRepoDb;User ID=sa; Password=*****"
- },
- "ApplicationInsights": {
- "InstrumentationKey": ""
- },
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- }
- }
Now, we have configured settings to create a database, so we have time to create a database, using migration. We must choose the GR.Data project in the Package Manager console during the performance of the steps mentioned below.
- Tools –> NuGet Package Manager –> Package Manager Console
- Run PM> Add-Migration MyFirstMigration to scaffold a migration to create the initial set of tables for our model. If we receive an error stating the term ‘add-migration’ is not recognized as the name of a cmdlet, then close and reopen Visual Studio.
- Run PM> Update-Database to apply the new migration to the database. Because our database doesn’t exist yet, it will be created for us before the migration is applied.
Create Application User Interface
Now, we proceed to the controller. We create two controllers, where one is AuthorController and another is BookController under the Controllers folder of the Application.
These controllers have all ActionResult methods for each user interface of an operation. We create an IRepository interface instance, then we inject it in the controller's constructor to get its object. The following is a partial code snippet for the AuthorController in which the repository is injected, using constructor dependency injection.
- using GR.Data;
- using GR.Web.Models;
- using Microsoft.AspNetCore.Mvc;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace GR.Web.Controllers {
- public class AuthorController: Controller {
- private IRepository < Author > repoAuthor;
- private IRepository < Book > repoBook;
- public AuthorController(IRepository < Author > repoAuthor, IRepository < Book > repoBook) {
- this.repoAuthor = repoAuthor;
- this.repoBook = repoBook;
- }
- }
- }
We can notice that Controller takes the IRepository as a constructor parameter. ASP.NET dependency injection will take care of passing an instance of IRepository into Author controller. The controller is developed to handle operations requests for both Author and Book entities. Now, let's develop the user interface for the Author Listing, Add Author with Book, Edit Author and Add Book. Let's see each one by one.
Author List View
This is the first view when the Application is accessed or the entry point of the application is executed. It shows the author listing as in Figure 2. The author data is displayed in a tabular format and on this view, it has linked to add a new author with his/her book, edit an author, and add a book.
To pass data from controller to view, create named AuthorListingViewModel view model, as per code snippet, mentioned below.
- namespace GR.Web.Models {
- public class AuthorListingViewModel {
- public long Id {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public string Email {
- get;
- set;
- }
- public int TotalBooks {
- get;
- set;
- }
- }
- }
Now, we create action method, which returns an index view with the data. The code snippet of Index action method in AuthorController is mentioned below.
- [HttpGet]
- public IActionResult Index() {
- List < AuthorListingViewModel > model = new List < AuthorListingViewModel > ();
- repoAuthor.GetAll().ToList().ForEach(a => {
- AuthorListingViewModel author = new AuthorListingViewModel {
- Id = a.Id,
- Name = $ "{a.FirstName} {a.LastName}",
- Email = a.Email
- };
- author.TotalBooks = repoBook.GetAll().Count(x => x.AuthorId == a.Id);
- model.Add(author);
- });
- return View("Index", model);
- }
Now, we create an index view, as per the code snippet, mentioned below under the Author folder of views.
- @model IEnumerable < AuthorListingViewModel > @using GR.Web.Models
- @using GR.Web.Code < div class = "top-buffer" > < /div> < div class = "panel panel-primary" > < div class = "panel-heading panel-head" > Authors < /div> < div class = "panel-body" > < div class = "btn-group" > < a id = "createEditAuthorModal"
- data - toggle = "modal"
- asp - action = "AddAuthor"
- data - target = "#modal-action-author"
- class = "btn btn-primary" > < i class = "glyphicon glyphicon-plus" > < /i> Add Author < /a> < /div> < div class = "top-buffer" > < /div> < table class = "table table-bordered table-striped table-condensed" > < thead > < tr > < th > Name < /th> < th > Email < /th> < th > Total Books < /th> < th > Action < /th> < /tr> < /thead> < tbody > @foreach(var item in Model) { < tr > < td > @Html.DisplayFor(modelItem => item.Name) < /td> < td > @Html.DisplayFor(modelItem => item.Email) < /td> < td > @Html.DisplayFor(modelItem => item.TotalBooks) < /td> < td > < a id = "editAuthorModal"
- data - toggle = "modal"
- asp - action = "EditAuthor"
- asp - route - id = "@item.Id"
- data - target = "#modal-action-author"
- class = "btn btn-info" > < i class = "glyphicon glyphicon-pencil" > < /i> Edit < /a> < a id = "addBookModal"
- data - toggle = "modal"
- asp - action = "AddBook"
- asp - route - id = "@item.Id"
- data - target = "#modal-action-author"
- class = "btn btn-success" > < i class = "glyphicon glyphicon-book" > < /i> Book < /a> < /td> < /tr>
- } < /tbody> < /table> < /div> < /div>
- @Html.Partial("_Modal", new BootstrapModel {
- ID = "modal-action-author", AreaLabeledId = "modal-action-author-label", Size = ModalSize.Large
- })
- @section scripts { < script src = "~/js/author-index.js"
- asp - append - version = "true" > < /script>
- }
- It shows all forms in bootstrap model popup so create the author - index.js file as per following code snippet.
- (function($) {
- function Author() {
- var $this = this;
-
- function initilizeModel() {
- $("#modal-action-author").on('loaded.bs.modal', function(e) {}).on('hidden.bs.modal', function(e) {
- $(this).removeData('bs.modal');
- });
- }
- $this.init = function() {
- initilizeModel();
- }
- }
- $(function() {
- var self = new Author();
- self.init();
- })
- }(jQuery))
When we run the Application and call the index() action method from AuthorController with a HttpGet request, we get all the authors listed in the UI, as shown in Figure 2.
Author Listing
Add Author
To pass the data from UI to the controller to add an author with his/her book, define view model named AuthorBookViewModel, as per the code snippet, mentioned below.
- using System.ComponentModel.DataAnnotations;
- namespace GR.Web.Models {
- public class AuthorBookViewModel {
- public long Id {
- get;
- set;
- }
- [Display(Name = "First Name")]
- public string FirstName {
- get;
- set;
- }
- [Display(Name = "Last Name")]
- public string LastName {
- get;
- set;
- }
- public string Email {
- get;
- set;
- }
- [Display(Name = "Book Name")]
- public string BookName {
- get;
- set;
- }
- public string ISBN {
- get;
- set;
- }
- public string Publisher {
- get;
- set;
- }
- }
- }
The AuthorController has an action method named AddAuthor, which returns view to add an author. The code snippet is mentioned below is for same action method for both GET and Post requests.
- [HttpGet]
- public PartialViewResult AddAuthor() {
- AuthorBookViewModel model = new AuthorBookViewModel();
- return PartialView("_AddAuthor", model);
- }
- [HttpPost]
- public ActionResult AddAuthor(AuthorBookViewModel model) {
- Author author = new Author {
- FirstName = model.FirstName,
- LastName = model.LastName,
- Email = model.Email,
- AddedDate = DateTime.UtcNow,
- IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
- ModifiedDate = DateTime.UtcNow,
- Books = new List < Book > {
- new Book {
- Name = model.BookName,
- ISBN = model.ISBN,
- Publisher = model.Publisher,
- IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
- AddedDate = DateTime.UtcNow,
- ModifiedDate = DateTime.UtcNow
- }
- }
- };
- repoAuthor.Insert(author);
- return RedirectToAction("Index");
- }
The GET request for the AddAuthor action method returns _AddAuthor partial view, which code snippet is following under the Author folder of views.
- @model AuthorBookViewModel
- @using GR.Web.Models < form asp - action = "AddAuthor"
- role = "form" > @await Html.PartialAsync("_ModalHeader", new ModalHeader {
- Heading = "Add Author"
- }) < div class = "modal-body form-horizontal" > < div class = "row" > < div class = "col-lg-6" > < div class = "form-group" > < label asp -
- for = "FirstName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "FirstName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "LastName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "LastName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "Email"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "Email"
- class = "form-control" / > < /div> < /div> < /div> < div class = "col-lg-6" > < div class = "form-group" > < label asp -
- for = "BookName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "BookName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "ISBN"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "ISBN"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "Publisher"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "Publisher"
- class = "form-control" / > < /div> < /div> < /div> < /div> < /div>
- @await Html.PartialAsync("_ModalFooter", new ModalFooter {}) < /form>
When the Application runs and clicks on the Add Author button, it makes a GET request for the AddAuthor() action, add an author screen, as shown in Figure 3.
Add Author and Book Screen
Edit Author
To pass the data from UI to a controller to edit an author, define view model named AuthorViewModel, as per the code snippet, mentioned below.
- using System.ComponentModel.DataAnnotations;
- namespace GR.Web.Models {
- public class AuthorViewModel {
- [Display(Name = "First Name")]
- public string FirstName {
- get;
- set;
- }
- [Display(Name = "Last Name")]
- public string LastName {
- get;
- set;
- }
- public string Email {
- get;
- set;
- }
- }
- }
The AuthorController has an action method named EditAuthor, which returns view to edit an author. The code snippet is mentioned below for same action method for both GET and Post requests.
- [HttpGet]
- public IActionResult EditAuthor(long id) {
- AuthorViewModel model = new AuthorViewModel();
- Author author = repoAuthor.Get(id);
- if (author != null) {
- model.FirstName = author.FirstName;
- model.LastName = author.LastName;
- model.Email = author.Email;
- }
- return PartialView("_EditAuthor", model);
- }
- [HttpPost]
- public IActionResult EditAuthor(long id, AuthorViewModel model) {
- Author author = repoAuthor.Get(id);
- if (author != null) {
- author.FirstName = model.FirstName;
- author.LastName = model.LastName;
- author.Email = model.Email;
- author.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
- author.ModifiedDate = DateTime.UtcNow;
- repoAuthor.Update(author);
- }
- return RedirectToAction("Index");
- }
GET request for the EditAuthor action method returns _EditAuthor partial view, where code snippet is following under the Author folder of views.
- @model AuthorViewModel
- @using GR.Web.Models < form asp - action = "EditAuthor"
- role = "form" > @await Html.PartialAsync("_ModalHeader", new ModalHeader {
- Heading = "Edit Author"
- }) < div class = "modal-body form-horizontal" > < div class = "form-group" > < label asp -
- for = "FirstName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "FirstName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "LastName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "LastName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "Email"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "Email"
- class = "form-control" / > < /div> < /div> < /div>
- @await Html.PartialAsync("_ModalFooter", new ModalFooter {}) < /form>
When an Application runs and clicks on the Edit button in the Author listing, it makes a GET request for the EditAuthor() action, then the edit author screen is shown in Figure 4.
Edit Author
Add Book
To pass the data from UI to the controller to add a book, define view model named BookViewModel, as per the code snippet, mentioned below.
- using System.ComponentModel.DataAnnotations;
- namespace GR.Web.Models {
- public class BookViewModel {
- [Display(Name = "Book Name")]
- public string BookName {
- get;
- set;
- }
- public string ISBN {
- get;
- set;
- }
- public string Publisher {
- get;
- set;
- }
- }
- }
The AuthorController has an action method named AddBook, which returns a view to add a book. The code snippet is mentioned below for same action method for both GET and Post requests.
- [HttpGet]
- public PartialViewResult AddBook(long id) {
- BookViewModel model = new BookViewModel();
- return PartialView("_AddBook", model);
- }
- [HttpPost]
- public IActionResult AddBook(long id, BookViewModel model) {
- Book book = new Book {
- AuthorId = id,
- Name = model.BookName,
- ISBN = model.ISBN,
- Publisher = model.Publisher,
- IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
- AddedDate = DateTime.UtcNow,
- ModifiedDate = DateTime.UtcNow
- };
- repoBook.Insert(book);
- return RedirectToAction("Index");
- }
GET request for the AddBook action method returns _AddBook partial view, where the code snippet is following under the Author folder of the views.
- @model BookViewModel
- @using GR.Web.Models < form asp - action = "AddBook"
- role = "form" > @await Html.PartialAsync("_ModalHeader", new ModalHeader {
- Heading = "Add Book"
- }) < div class = "modal-body form-horizontal" > < div class = "form-group" > < label asp -
- for = "BookName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "BookName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "ISBN"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "ISBN"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "Publisher"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "Publisher"
- class = "form-control" / > < /div> < /div> < /div>
- @await Html.PartialAsync("_ModalFooter", new ModalFooter {}) < /form>
When an Application runs and clicks on the Book button in the Author listing, it makes a GET request for the AddBook() action, then the add book screen is shows below in Figure 5.
Add Book
These operations are about the author controller. As another controller BookController has some more operations such as Book listing, Edit Book, and Delete book. The BookController’s constructor injects repository for both Author and Book entities. The code snippet is mentioned below for the same in the BookController.
- sing System;
- using System.Collections.Generic;
- using System.Linq;
- using Microsoft.AspNetCore.Mvc;
- using GR.Data;
- using GR.Web.Models;
- using Microsoft.AspNetCore.Mvc.Rendering;
- using Microsoft.AspNetCore.Http;
- namespace GR.Web.Controllers {
- public class BookController: Controller {
- private IRepository < Author > repoAuthor;
- private IRepository < Book > repoBook;
- public BookController(IRepository < Author > repoAuthor, IRepository < Book > repoBook) {
- this.repoAuthor = repoAuthor;
- this.repoBook = repoBook;
- }
- }
- }
Now, click on top menu of the book. It shows the book listing, as shown in Figure 6. The book data is displayed in a tabular format and on this view. These book listing has options to edit a book and delete a book. To pass the data from controller to view, create named BookListingViewModel view model, as shown below.
- namespace GR.Web.Models {
- public class BookListingViewModel {
- public long Id {
- get;
- set;
- }
- public string BookName {
- get;
- set;
- }
- public string AuthorName {
- get;
- set;
- }
- public string ISBN {
- get;
- set;
- }
- public string Publisher {
- get;
- set;
- }
- }
- }
Now, we create an action method, which returns an index view with the data. The code snippet, mentioned below is of Index action method in BookController.
- public IActionResult Index() {
- List < BookListingViewModel > model = new List < BookListingViewModel > ();
- repoBook.GetAll().ToList().ForEach(b => {
- BookListingViewModel book = new BookListingViewModel {
- Id = b.Id,
- BookName = b.Name,
- Publisher = b.Publisher,
- ISBN = b.ISBN
- };
- Author author = repoAuthor.Get(b.AuthorId);
- book.AuthorName = $ "{author.FirstName} {author.LastName}";
- model.Add(book);
- });
- return View("Index", model);
- }
Now, we create an index view, as per the code snippet, mentioned below under the Book folder of Views.
- @model IEnumerable < BookListingViewModel > @using GR.Web.Models
- @using GR.Web.Code < div class = "top-buffer" > < /div> < div class = "panel panel-primary" > < div class = "panel-heading panel-head" > Books < /div> < div class = "panel-body" > < div class = "top-buffer" > < /div> < table class = "table table-bordered table-striped table-condensed" > < thead > < tr > < th > Name < /th> < th > Author Name < /th> < th > ISBN < /th> < th > Publisher < /th> < th > Action < /th> < /tr> < /thead> < tbody > @foreach(var item in Model) { < tr > < td > @Html.DisplayFor(modelItem => item.BookName) < /td> < td > @Html.DisplayFor(modelItem => item.AuthorName) < /td> < td > @Html.DisplayFor(modelItem => item.ISBN) < /td> < td > @Html.DisplayFor(modelItem => item.Publisher) < /td> < td > < a id = "editBookModal"
- data - toggle = "modal"
- asp - action = "EditBook"
- asp - route - id = "@item.Id"
- data - target = "#modal-action-book"
- class = "btn btn-info" > < i class = "glyphicon glyphicon-pencil" > < /i> Edit < /a> < a id = "deleteBookModal"
- data - toggle = "modal"
- asp - action = "DeleteBook"
- asp - route - id = "@item.Id"
- data - target = "#modal-action-book"
- class = "btn btn-danger" > < i class = "glyphicon glyphicon-trash" > < /i> Delete < /a> < /td> < /tr>
- } < /tbody> < /table> < /div> < /div>
- @Html.Partial("_Modal", new BootstrapModel {
- ID = "modal-action-book", AreaLabeledId = "modal-action-book-label", Size = ModalSize.Medium
- })
- @section scripts { < script src = "~/js/book-index.js"
- asp - append - version = "true" > < /script>
- }
It shows all forms in bootstrap model popup, so create the book-index.js file, as per the code snippet, mentioned below.
- (function($) {
- function Book() {
- var $this = this;
-
- function initilizeModel() {
- $("#modal-action-book").on('loaded.bs.modal', function(e) {}).on('hidden.bs.modal', function(e) {
- $(this).removeData('bs.modal');
- });
- }
- $this.init = function() {
- initilizeModel();
- }
- }
- $(function() {
- var self = new Book();
- self.init();
- })
- }(jQuery))
When we run the Application and click on top menu Book, which calls index() action method with a HttpGet request from BookController, then we get all the book listed in the UI, as shown below.
Book Listing
Edit Book
To pass the data from UI to a controller to edit a book, define view model named EditBookViewModel, as shown below.
- using Microsoft.AspNetCore.Mvc.Rendering;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
- namespace GR.Web.Models {
- public class EditBookViewModel {
- [Display(Name = "Book Name")]
- public string BookName {
- get;
- set;
- }
- public string ISBN {
- get;
- set;
- }
- public string Publisher {
- get;
- set;
- }
- public List < SelectListItem > Authors {
- get;
- set;
- } = new List < SelectListItem > ();
- [Display(Name = "Author")]
- public long AuthorId {
- get;
- set;
- }
- }
- }
The BookController has an action method named EditBook, which returns view for editing a book. The code snippet is mentioned below for same action method for both GET and Post requests.
- public PartialViewResult EditBook(long id) {
- EditBookViewModel model = new EditBookViewModel();
- model.Authors = repoAuthor.GetAll().Select(a => new SelectListItem {
- Text = $ "{a.FirstName} {a.LastName}",
- Value = a.Id.ToString()
- }).ToList();
- Book book = repoBook.Get(id);
- if (book != null) {
- model.BookName = book.Name;
- model.ISBN = book.ISBN;
- model.Publisher = book.Publisher;
- model.AuthorId = book.AuthorId;
- }
- return PartialView("_EditBook", model);
- }
- [HttpPost]
- public ActionResult EditBook(long id, EditBookViewModel model) {
- Book book = repoBook.Get(id);
- if (book != null) {
- book.Name = model.BookName;
- book.ISBN = model.ISBN;
- book.Publisher = model.Publisher;
- book.AuthorId = model.AuthorId;
- book.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
- book.ModifiedDate = DateTime.UtcNow;
- repoBook.Update(book);
- }
- return RedirectToAction("Index");
- }
The GET request for the EditBook action method returns _EditBook partial view, where code snippet is mentioned below under the Book folder of views.
- @model EditBookViewModel
- @using GR.Web.Models < form asp - action = "EditBook"
- role = "form" > @await Html.PartialAsync("_ModalHeader", new ModalHeader {
- Heading = "Edit Book"
- }) < div class = "modal-body form-horizontal" > < div class = "form-group" > < label asp -
- for = "BookName"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "BookName"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "ISBN"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "ISBN"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "Publisher"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < input asp -
- for = "Publisher"
- class = "form-control" / > < /div> < /div> < div class = "form-group" > < label asp -
- for = "AuthorId"
- class = "col-lg-3 col-sm-3 control-label" > < /label> < div class = "col-lg-6" > < select asp -
- for = "AuthorId"
- asp - items = "@Model.Authors"
- class = "form-control" > < option > Please select < /option> < /select> < /div> < /div> < /div>
- @await Html.PartialAsync("_ModalFooter", new ModalFooter {}) < /form>
When an Application runs and clicks the Edit button in the listed books, it makes a GET request for the EditBook() action, then the edit book screen is shown below.
Edit Book
Delete Book
The BookController has an action method named DeleteBook, which returns the view to delete a book. The code snippet is mentioned below for the same action method for both GET and Post requests.
- [HttpGet]
- public PartialViewResult DeleteBook(long id) {
- Book book = repoBook.Get(id);
- return PartialView("_DeleteBook", book ? .Name);
- }
- [HttpPost]
- public ActionResult DeleteBook(long id, FormCollection form) {
- Book book = repoBook.Get(id);
- if (book != null) {
- repoBook.Delete(book);
- }
- return RedirectToAction("Index");
- }
- The GET request
- for the DeleteBook action method returns _DeleteBook partial view which code snippet is following under the Book folder of Views.
- @model string
- @using GR.Web.Models < form asp - action = "DeleteBook"
- role = "form" > @Html.Partial("_ModalHeader", new ModalHeader {
- Heading = "Delete Book"
- }) < div class = "modal-body form-horizontal" > Are you want to delete @Model ? < /div>
- @Html.Partial("_ModalFooter", new ModalFooter {
- SubmitButtonText = "Delete"
- }) < /form>
When an Application runs and you click on the Delete button in the listed books, it makes a GET request for the DeleteBook() action, then the delete book screen is shown below.
Delete Book
Download
You can download complete source code from TechNet Gallery, using the links, mentioned below.
- Generic Repository Pattern in ASP.NET Core
- Repository Pattern in ASP.NET Core
- CRUD Operations in ASP.NET Core and Entity Framework Core
- Rating Star Application in ASP.NET Core
See Also
Its recommended reading more articles related to ASP.NET Core.
- Overview Of ASP.NET Core
- ASP.NET Core With Visual Studio 2017 RC
- CRUD Operations In ASP.NET Core using Entity Framework Core Code First
- Repository Pattern In ASP.NET Core
- Generic Repository Pattern In ASP.NET Core
Conclusion
This article introduced the generic repository pattern in the ASP.NET Core, using Entity Framework Core with the "code first" development approach. We used Bootstrap CSS and JavaScript for the user interface design in this Application.