ASP.NET Core Entity Framework Core Code First: CRUD Operations
Introduction
This article introduces how to perform the Create, Read, Update, and Delete (CRUD) operations in ASP.NET Core, using Entity Framework Core. This walk through will use the "Code First" development approach and create a database from model using migration. We can view this article's sample on TechNet Gallery. I would like to recommend the following wiki article Overview Of ASP.NET Core so that development environment be prepare for this sample application in ASP.NET Core. We will create a single entity Customer to perform the CRUD operations.
Create Database
First, let's install the Entity Framework Core in our application. As we use SQL Server, install the package for SQL Server database provider. To install database provider, follow the below steps.
- Tools - NuGet Package Manager - Package Manager Console
- Run PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
When we install a package in ASP.NET Core application, then the package installation occurs in the background. We see "(Restoring...)" appeared next to References in Solution Explorer while the installation occurs.
We also use Entity Framework Core as an Object Relationship Mapper (ORM) to perform the CRUD operations. Let's install the Entity Framework Core Tool to maintain the database, using the following procedure.
- Run PM> Install-Package Microsoft.EntityFrameworkCore.Tools -Pre.
- Open project.json file.
- Locate the tools section and add the ef command as shown below.
"tools": {
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"BundlerMinifier.Core": "2.0.238",
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
}
Now, create two entities - the BaseEntity class that has common properties that will be inherited by each entity, and the Customer. Let's see each entity. The following is a code snippet for the BaseEntity class.
using System;
namespace CRUDApplication.DbEntities
{
public class BaseEntity
{
public Int64 Id { get; set; }
public DateTime AddedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string IPAddress { get; set; }
}
}
Now, create a Customer entity under the DbEntities folder which inherits from the BaseEntity class. The following is a code for Customer entity.
namespace CRUDApplication.DbEntities
{
public class Customer:BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string MobileNo { get; set; }
}
}
Now, let's define the configuration for the Customer entity that will be used when the database table will be created by the entity. The following is the code for Customer mapping entity (CustomerMap.cs).
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CRUDApplication.DbEntities
{
public class CustomerMap
{
public CustomerMap(EntityTypeBuilder<Customer> entityBuilder)
{
entityBuilder.HasKey(t => t.Id);
entityBuilder.Property(t => t.FirstName).IsRequired();
entityBuilder.Property(t => t.LastName).IsRequired();
entityBuilder.Property(t => t.Email).IsRequired();
entityBuilder.Property(t => t.MobileNo).IsRequired();
}
}
}
The EntityTypeBuilder is an important class that allows configuration to be performed for an entity type in a model. This is done using the modelbuilder in an override of the OnModelCreating method. The Constructor of the CustomerMap class uses the Fluent API to map and configure properties in the table. So let's see each method used in the constructor one-by-one.
- HasKey(): The Haskey() method configures a primary key on the table.
- Property(): The Property method configures attributes for each property belonging to an entity or complex type. It is used to obtain a configuration object for a given property. The options on the configuration object are specific to the type being configured.
Now, it's time to define context class. The ADO.NET Entity Framework Code First development approach requires us to create a data access context class that inherits from the DbContext class so we create a context class CRUDContext (CRUDContext.cs) class. In this class, we override the OnModelCreating() method. This method is called when the model for a context class (CRUDContext) 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 CRUDApplication.DbEntities
{
public class CRUDContext:DbContext
{
public CRUDContext(DbContextOptions<CRUDContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
new CustomerMap(modelBuilder.Entity<Customer>());
}
}
}
As the concept of dependency injection is central to the ASP.NET Core application, so we register our context to dependency injection during the application start up. Once we register CRUDContext context as a service to dependency injection, then provide it via constructor to MVC controller. In order for our MVC controllers to make use of CRUDContext, we are going to register it as a service.
Open the appsettings.json file and define connection string here.
{ "ConnectionStrings": { "DefaultConnection": "Data Source=ADMIN\\SQLEXPRESS;Initial Catalog=ECommerceDb;User ID=sa; Password=" }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } }
It stores application level settings such as connection string, SMTP etc. It's similar to web.config file in ASP.NET.
Open the Startup.cs file and following using statements at the start of the file.
using Microsoft.EntityFrameworkCore; using CRUDApplication.DbEntities;
Now, we can use the AddDbContext method to register it as a service. Locate the ConfigureServices method and add the lines of code for register it to dependency injection as per following code snippet.
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDbContext<CRUDContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); }
Now we have created a model so time to create a database using migration.
- 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. Create a CustomerController under the Controllers folder of the application. This controller has all ActionResult methods for each user interface of a CRUD operation. We first create a CRUDContext class instance then we inject it in the controller's constructor to get its object. The following is a code snippet for the CustomerController.
using CRUDApplication.DbEntities;
using CRUDApplication.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CRUDApplication.Controllers
{
public class CustomerController : Controller
{
private CRUDContext context;
public CustomerController(CRUDContext context)
{
this.context = context;
}
[HttpGet]
public IActionResult Index()
{
IEnumerable<CustomerViewModel> model = context.Set<Customer>().ToList().Select(s => new CustomerViewModel
{
Id= s.Id,
Name = $"{s.FirstName} {s.LastName}",
MobileNo = s.MobileNo,
Email = s.Email
});
return View("Index", model);
}
[HttpGet]
public IActionResult AddEditCustomer(long? id)
{
CustomerViewModel model = new CustomerViewModel();
if (id.HasValue)
{
Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id.Value);
if (customer != null)
{
model.Id = customer.Id;
model.FirstName = customer.FirstName;
model.LastName = customer.LastName;
model.MobileNo = customer.MobileNo;
model.Email = customer.Email;
}
}
return PartialView("~/Views/Customer/_AddEditCustomer.cshtml", model);
}
[HttpPost]
public ActionResult AddEditCustomer(long? id, CustomerViewModel model)
{
try
{
if (ModelState.IsValid)
{
bool isNew = !id.HasValue;
Customer customer = isNew ? new Customer
{
AddedDate = DateTime.UtcNow
} : context.Set<Customer>().SingleOrDefault(s => s.Id == id.Value);
customer.FirstName = model.FirstName;
customer.LastName = model.LastName;
customer.MobileNo = model.MobileNo;
customer.Email = model.Email;
customer.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
customer.ModifiedDate = DateTime.UtcNow;
if (isNew)
{
context.Add(customer);
}
context.SaveChanges();
}
}
catch (Exception ex)
{
throw ex;
}
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult DeleteCustomer(long id)
{
Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id);
CustomerViewModel model = new CustomerViewModel
{
Name = $"{customer.FirstName} {customer.LastName}"
};
return PartialView("~/Views/Customer/_DeleteCustomer.cshtml", model);
}
[HttpPost]
public IActionResult DeleteCustomer(long id, FormCollection form)
{
Customer customer = context.Set<Customer>().SingleOrDefault(c => c.Id == id);
context.Entry(customer).State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
context.SaveChanges();
return RedirectToAction("Index");
}
}
}
We can notice that the Controller takes a CRUDContext as a constructor parameter. ASP.NET dependency injection will take care of passing an instance of CRUDContext into our controller. The controller is developed to handle CURD operation requests for a Customer entity. Now, let's develop the user interface for the CRUD operations. We develop it for the views for adding and editing a customer, a customer listing, customer deletion. Let's see each one by one.
Customer List View
This is the first view when the application is accessed or the entry point of the application is executed. It shows the customer listing as in Figure 1. We display customer data in tabular format and on this view we create links to add a new customer, edit a customer and delete a customer. This view is an index view and the following is a code snippet for index.cshtml under the Customer folder of Views.
@model IEnumerable<CRUDApplication.Models.CustomerViewModel>
@using CRUDApplication.Models
@using CRUDApplication.Code
<div class="top-buffer"></div>
<div class="panel panel-primary">
<div class="panel-heading panel-head">Customers</div>
<div class="panel-body">
<div class="btn-group">
<a id="createEditCustomerModal" data-toggle="modal" asp-action="AddEditCustomer" data-target="#modal-action-customer" class="btn btn-primary">
<i class="glyphicon glyphicon-plus"></i> Add Customer
</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>Mobile No</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.MobileNo)</td>
<td>
<a id="editCustomerModal" data-toggle="modal" asp-action="AddEditCustomer" asp-route-id= "@item.Id" data-target="#modal-action-customer"
class="btn btn-info">
<i class="glyphicon glyphicon-pencil"></i> Edit
</a>
<a id="deleteCustomerModal" data-toggle="modal" asp-action="DeleteCustomer" asp-route-id= "@item.Id" data-target="#modal-action-customer" 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-customer", AreaLabeledId = "modal-action-customer-label", Size = ModalSize.Small })
@section scripts
{
<script src="~/js/customer-index.js" asp-append-version="true"></script>
}
When we run the application and call the index() action with a HttpGet request, then we get all the customers listed in the UI as in Figure 1. This UI has options for CRUD operations.
Figure 1: Customer Listing UI
Create / Edit Customer View
We create a common view to create and edit a customer so we create a single customer view model. The following code snippet for CustomerViewModel.cs.
using System.ComponentModel.DataAnnotations;
namespace CRUDApplication.Models
{
public class CustomerViewModel
{
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 Name { get; set; }
public string Email { get; set; }
[Display(Name = "Mobile No")]
public string MobileNo { get; set; }
}
}
We show form in the bootstrap modal popup and submit using ajax post; that's why we create a javascript file which contains a method for removing loaded data.
(function ($) {
function Customer() {
var $this = this;
function initilizeModel() {
$("#modal-action-customer").on('loaded.bs.modal', function (e) {
}).on('hidden.bs.modal', function (e) {
$(this).removeData('bs.modal');
});
}
$this.init = function () {
initilizeModel();
}
}
$(function () {
var self = new Customer();
self.init();
})
}(jQuery))
Now, define a create/edit customer partial view. The following is the code snippet for _AddEditCustomer.cshtml.
@model CRUDApplication.Models.CustomerViewModel
@using CRUDApplication.Models
<form asp-action="AddEditCustomer" role="form">
@await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = String.Format("{0} Customer", @Model.Id == 0 ? "Add" : "Edit") })
<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 class="form-group">
<label asp-for="MobileNo" class="col-lg-3 col-sm-3 control-label"></label>
<div class="col-lg-6">
<input asp-for="MobileNo" class="form-control" />
</div>
</div>
</div>
@await Html.PartialAsync("_ModalFooter", new ModalFooter { })
</form>
Now, run the application and click on Edit button of listing which calls AddEditCustomer action method, then we get the UI as in Figure 2 to edit a customer.
Figure 2: Edit a Customer UI
Delete A Customer
To delete a customer, we follow the process of clicking on the Delete button that exists in the Customer listing data then a modal popup shows to ask "Are you want to delete xxx?" after clicking on the Delete button that exists in the popup view such as in Figure 3 then it makes a HttpPost request that calls the DeleteCustomer() action method and deletes the customer. The following is a code snippet for _DeleteCustomer.cshtml.
@model CRUDApplication.Models.CustomerViewModel
@using CRUDApplication.Models
@using (Html.BeginForm())
{
@Html.Partial("_ModalHeader", new ModalHeader { Heading = "Delete Customer" })
<div class="modal-body form-horizontal">
Are you want to delete @Model.Name?
</div>
@Html.Partial("_ModalFooter", new ModalFooter { SubmitButtonText = "Delete" })
}
Now, run the application and click on Delete button of listing which calls DeleteCustomer action method, then we get the UI as in Figure 3 to delete a customer.
Figure 3: Delete Confirmation
Conclusion
This article introduced CRUD operations in ASP.NET Core using Entity Framework Core with "code first" development approach. We used bootstrap CSS and JavaScript for the user interface design in this application.
Download
You can download the complete source code from TechNet Gallery using following links.
- Rating Star Application in ASP.NET Core
- CRUD Operations in ASP.NET Core and Entity Framework Core
- Repository Pattern In ASP.NET Core
- Generic Repository Pattern in ASP.NET Core
- Onion Architecture In ASP.NET Core MVC
- ASP.NET Core MVC: Authentication and Role Based Authorisation with Identity
- ASP.NET Core MVC: Authentication and Claim Based authorization with Identity
See Also
I would like to recommend more articles which help to build the application in ASP.NET Core.
- Overview Of ASP.NET Core
- ASP.NET Core With Visual Studio 2017 RC
- Repository Pattern In ASP.NET Core
- ASP.NET Core: Generic Repository Pattern
- Onion Architecture In ASP.NET Core MVC
- ASP.NET Core MVC: Authentication And Role Based Authorization With ASP.NET Core Identity
- ASP.NET Core MVC: Authentication And Claim Based Authorisation With ASP.NET Identity Core
- ASP.NET Core : Overview Of Dependency Injection
- ASP.NET Core: In-Memory Caching