In this post, we will see how to create a simple Web API with all CRUD operations and will connect with an existing MS SQL database. After that, we will create an MVC application and consume this Web API for CRUD actions.
Create "Employees" table in MSSQL database
In this post, we will see how to create an Employee data entry application. So, we need to create an “Employees” table first. If you have any existing database, please create this table under that database. Otherwise, create a new database as well.
Create a Visual Studio project with MVC and Web API templates
We can create a new project in VS 2015/2017. I am using a free 2017 community edition.
Please choose MVC template and choose the Web API option so, that we can get the bootstrapping for Web API in our project. No need to change any other default parameters.
Our new project will be ready in a few moments.
As I mentioned earlier, we are creating an Employee data entry application. Hence, please create an Employee class under “Models” folder.
Please note, I am creating all business classes inside this “Models” folder for simplicity. If you are following other design patterns, you can keep the files accordingly.
Employee.cs
- namespace MVCwithWebAPI.Models
- {
- public class Employee
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public string Address { get; set; }
- public string Gender { get; set; }
- public string Company { get; set; }
- public string Designation { get; set; }
- }
- }
We can create a “DbContext” class for database connectivity.
The class that derives DbContext is called context class in entity framework. DbContext is an important class in Entity Framework API. It is a bridge between domain or entity classes and the database. DbContext is the primary class that is responsible for interacting with the database.
Before creating our DbContext class, we must install “EntityFramework” NuGet package in our project.
We can create “SqlDbContext” class and derives DbContext class insides this class.
- using System.Data.Entity;
-
- namespace MVCwithWebAPI.Models
- {
- public class SqlDbContext : DbContext
- {
- public SqlDbContext() : base("name=SqlConn")
- {
- }
- public DbSet<Employee> Employees { get; set; }
- }
- }
Please note, we have used a connection “SqlConn” in above DbContext class. Hence, we can create the connection string in Web.Config file.
- <connectionStrings>
- <add name="SqlConn"
- connectionString="Data Source=SARATHLALS\SQL2016; Initial Catalog=SarathlalDB; Integrated Security=True; MultipleActiveResultSets=True;"
- providerName="System.Data.SqlClient" />
- </connectionStrings>
We are following the repository pattern in this application. We can create a “IEmployeeRepository” interface and define all the functions there.
- using System.Collections.Generic;
- using System.Threading.Tasks;
-
- namespace MVCwithWebAPI.Models
- {
- public interface IEmployeeRepository
- {
- Task Add(Employee employee);
- Task Update(Employee employee);
- Task Delete(string id);
- Task<Employee> GetEmployee(string id);
- Task<IEnumerable<Employee>> GetEmployees();
- }
- }
We can implement the exact logic for CRUD actions in “EmployeeRepository” class. We will implement IEmployeeRepository interface in this class.
- using System;
- using System.Collections.Generic;
- using System.Data.Entity;
- using System.Linq;
- using System.Threading.Tasks;
- using System.Web;
-
- namespace MVCwithWebAPI.Models
- {
- public class EmployeeRepository : IEmployeeRepository
- {
- private readonly SqlDbContext db = new SqlDbContext();
- public async Task Add(Employee employee)
- {
- employee.Id = Guid.NewGuid().ToString();
- db.Employees.Add(employee);
- try
- {
- await db.SaveChangesAsync();
- }
- catch
- {
- throw;
- }
- }
- public async Task<Employee> GetEmployee(string id)
- {
- try
- {
- Employee employee = await db.Employees.FindAsync(id);
- if (employee == null)
- {
- return null;
- }
- return employee;
- }
- catch
- {
- throw;
- }
- }
- public async Task<IEnumerable<Employee>> GetEmployees()
- {
- try
- {
- var employees = await db.Employees.ToListAsync();
- return employees.AsQueryable();
- }
- catch
- {
- throw;
- }
- }
- public async Task Update(Employee employee)
- {
- try
- {
- db.Entry(employee).State = EntityState.Modified;
- await db.SaveChangesAsync();
- }
- catch
- {
- throw;
- }
- }
- public async Task Delete(string id)
- {
- try
- {
- Employee employee = await db.Employees.FindAsync(id);
- db.Employees.Remove(employee);
- await db.SaveChangesAsync();
- }
- catch
- {
- throw;
- }
- }
-
- private bool EmployeeExists(string id)
- {
- return db.Employees.Count(e => e.Id == id) > 0;
- }
-
- }
- }
I have implemented all 5 methods (for CRUD) in this class. All are self-explanatory. If you need further clarification on any terms, please feel free to contact me.
We can create our API Controller now.
EmployeesApiController.cs
- using MVCwithWebAPI.Models;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- using System.Web.Http;
-
- namespace MVCwithWebAPI.Controllers
- {
- public class EmployeesApiController : ApiController
- {
- private readonly IEmployeeRepository _iEmployeeRepository = new EmployeeRepository();
-
- [HttpGet]
- [Route("api/Employees/Get")]
- public async Task<IEnumerable<Employee>> Get()
- {
- return await _iEmployeeRepository.GetEmployees();
- }
-
- [HttpPost]
- [Route("api/Employees/Create")]
- public async Task CreateAsync([FromBody]Employee employee)
- {
- if (ModelState.IsValid)
- {
- await _iEmployeeRepository.Add(employee);
- }
- }
-
- [HttpGet]
- [Route("api/Employees/Details/{id}")]
- public async Task<Employee> Details(string id)
- {
- var result = await _iEmployeeRepository.GetEmployee(id);
- return result;
- }
-
- [HttpPut]
- [Route("api/Employees/Edit")]
- public async Task EditAsync([FromBody]Employee employee)
- {
- if (ModelState.IsValid)
- {
- await _iEmployeeRepository.Update(employee);
- }
- }
-
- [HttpDelete]
- [Route("api/Employees/Delete/{id}")]
- public async Task DeleteConfirmedAsync(string id)
- {
- await _iEmployeeRepository.Delete(id);
- }
- }
- }
All the CRUD actions are derived in this API class. We have created an instance for EmployeeRepository class and with the help of this instance, we have accessed all the methods from EmployeeRepository class in our API class.
We have completed our Web API design. If needed, you can check the Web API using Postman or any other tool. Please note down the URL of the application. We need to add this base URL in our Web.Config file because we will use this base URL in MVC controllers.
We can create a new key-value pair in Web.Config file under “appSettings” section.
We have named the key as “apiBaseAddress” and gave the project URL as value.
Create the MVC Controller using Scaffolding
We can create the MVC Controller using scaffolding template so that we will get all the views for CRUD actions easily. We will modify these views later.
We can right-click the “Controller” folder and click “Add” and choose “New Scaffolded Item” to create a new MVC controller.
You can choose, “MVC 5 Controller with views, using Entity Framework” option. This will create all views for CRUD operations.
We can choose the model class, data context class and give controller name as well. Please note, the system will automatically give a name for a controller. If needed, you can change it.
You can click the “Add” button to proceed further. After a few moments, our Employees controller is created with all views.
You can see the views under “Views” -> “Employees” folder.
We will modify some of the view files later. Let us modify the “EmployeesController” class now. Please note, by default MVC controller does not create actions as async. We have converted all these actions asynchronously. You can copy the below code and paste it inside the controller class.
- using MVCwithWebAPI.Models;
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Threading.Tasks;
- using System.Web.Mvc;
-
- namespace MVCwithWebAPI.Controllers
- {
- public class EmployeesController : Controller
- {
- readonly string apiBaseAddress = ConfigurationManager.AppSettings["apiBaseAddress"];
- public async Task<ActionResult> Index()
- {
- IEnumerable<Employee> employees = null;
-
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var result = await client.GetAsync("employees/get");
-
- if (result.IsSuccessStatusCode)
- {
- employees = await result.Content.ReadAsAsync<IList<Employee>>();
- }
- else
- {
- employees = Enumerable.Empty<Employee>();
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
- return View(employees);
- }
-
- public async Task<ActionResult> Details(string id)
- {
- if (id == null)
- {
- return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
- }
-
- Employee employee = null;
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var result = await client.GetAsync($"employees/details/{id}");
-
- if (result.IsSuccessStatusCode)
- {
- employee = await result.Content.ReadAsAsync<Employee>();
- }
- else
- {
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
-
- if (employee == null)
- {
- return HttpNotFound();
- }
- return View(employee);
- }
-
- public ActionResult Create()
- {
- return View();
- }
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public async Task<ActionResult> Create([Bind(Include = "Name,Address,Gender,Company,Designation")] Employee employee)
- {
- if (ModelState.IsValid)
- {
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var response = await client.PostAsJsonAsync("employees/Create", employee);
- if (response.IsSuccessStatusCode)
- {
- return RedirectToAction("Index");
- }
- else
- {
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
- }
- return View(employee);
- }
-
- public async Task<ActionResult> Edit(string id)
- {
- if (id == null)
- {
- return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
- }
- Employee employee = null;
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var result = await client.GetAsync($"employees/details/{id}");
-
- if (result.IsSuccessStatusCode)
- {
- employee = await result.Content.ReadAsAsync<Employee>();
- }
- else
- {
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
- if (employee == null)
- {
- return HttpNotFound();
- }
- return View(employee);
- }
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public async Task<ActionResult> Edit([Bind(Include = "Id,Name,Address,Gender,Company,Designation")] Employee employee)
- {
- if (ModelState.IsValid)
- {
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
- var response = await client.PutAsJsonAsync("employees/edit", employee);
- if (response.IsSuccessStatusCode)
- {
- return RedirectToAction("Index");
- }
- else
- {
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
- return RedirectToAction("Index");
- }
- return View(employee);
- }
-
- public async Task<ActionResult> Delete(string id)
- {
- if (id == null)
- {
- return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
- }
- Employee employee = null;
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var result = await client.GetAsync($"employees/details/{id}");
-
- if (result.IsSuccessStatusCode)
- {
- employee = await result.Content.ReadAsAsync<Employee>();
- }
- else
- {
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
-
- if (employee == null)
- {
- return HttpNotFound();
- }
- return View(employee);
- }
-
- [HttpPost, ActionName("Delete")]
- [ValidateAntiForgeryToken]
- public async Task<ActionResult> DeleteConfirmed(string id)
- {
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var response = await client.DeleteAsync($"employees/delete/{id}");
- if (response.IsSuccessStatusCode)
- {
- return RedirectToAction("Index");
- }
- else
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- return View();
- }
-
- }
- }
You can see, we have defined an “apiBaseAddress” variable globally and got the value for apiBaseAddress from Web.Config file. We will use this value in all our controller actions.
- public async Task<ActionResult> Index()
- {
- IEnumerable<Employee> employees = null;
-
- using (var client = new HttpClient())
- {
- client.BaseAddress = new Uri(apiBaseAddress);
-
- var result = await client.GetAsync("employees/get");
-
- if (result.IsSuccessStatusCode)
- {
- employees = await result.Content.ReadAsAsync<IList<Employee>>();
- }
- else
- {
- employees = Enumerable.Empty<Employee>();
- ModelState.AddModelError(string.Empty, "Server error try after some time.");
- }
- }
- return View(employees);
- }
If you look at the index action, you can see, we have declared a “HttpClient” variable and using client.GetAsync method, we have got the employee data result from Web API and store in a “result” variable. We have again read the employee data from this result variable using “ReadAsync” method.
We have used the same approach in other action methods also. All the methods are self-explanatory. If you have any queries, please feel free to contact me.
We can now modify the Index view. Please copy the below and paste to Index view file.
Index.cshtml
- @model IEnumerable<MVCwithWebAPI.Models.Employee>
-
- @{
- ViewBag.Title = "Employee List";
- }
-
- <h3>Employee List</h3>
-
- <p>
- @Html.ActionLink("New Employee", "Create")
- </p>
- <table class="table">
- <tr>
- <th>
- @Html.DisplayNameFor(model => model.Name)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Address)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Gender)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Company)
- </th>
- <th>
- @Html.DisplayNameFor(model => model.Designation)
- </th>
- <th></th>
- </tr>
-
- @foreach (var item in Model)
- {
- <tr>
- <td>
- @Html.ActionLink(item.Name, "Details", new { id = item.Id })
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Address)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Gender)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Company)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Designation)
- </td>
- <td>
- @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
- @Html.ActionLink("Delete", "Delete", new { id = item.Id })
- </td>
- </tr>
- }
-
- </table>
We have modified the existing “Index” view. We have removed the “Details” link from this view and instead, we have given a hyperlink in the employee name itself for details.
We can modify the “Create” view by removing the Id field. Because for us, employee Id will be created automatically while inserting new data. We have used system GUID for this.
- @model MVCwithWebAPI.Models.Employee
-
- @{
- ViewBag.Title = "Create Employee";
- }
-
- <h3>Create Employee</h3>
-
-
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
-
- <div class="form-horizontal">
- <hr />
- @Html.ValidationSummary(true, "", new { @class = "text-danger" })
-
- <div class="form-group">
- @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
- </div>
- </div>
-
- <div class="form-group">
- @Html.LabelFor(model => model.Address, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Address, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Address, "", new { @class = "text-danger" })
- </div>
- </div>
-
- <div class="form-group">
- @Html.LabelFor(model => model.Gender, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Gender, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Gender, "", new { @class = "text-danger" })
- </div>
- </div>
-
- <div class="form-group">
- @Html.LabelFor(model => model.Company, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Company, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Company, "", new { @class = "text-danger" })
- </div>
- </div>
-
- <div class="form-group">
- @Html.LabelFor(model => model.Designation, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Designation, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Designation, "", new { @class = "text-danger" })
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-md-offset-2 col-md-10">
- <input type="submit" value="Create" class="btn btn-default" />
- </div>
- </div>
- </div>
- }
-
- <div>
- @Html.ActionLink("Back to List", "Index")
- </div>
-
- @section Scripts {
- @Scripts.Render("~/bundles/jqueryval")
- }
We can run the application now. The landing page looks like the below screenshot.
We can click the “Employees” link and click the “New Employee” link to create a new employee.
I have given my own details in the above screen.
We can create one more employee data and display two employee details as shown below.
As I mentioned earlier in this post, I have removed the default “Details” link from this view and added the hyperlink in employee name itself.
You can click the employee name to show the details.
We can click “Edit” link to edit the employee details.
We can use “Delete” link to delete the record as well.
We have successfully seen all the CRUD actions with this application.
In this post, we have created a Web API using Entity Framework and repository patterns and later we created an MVC controller and views using scaffolding template and consumed the Web API in MVC controller. We have seen all CRUD actions with this employee application.