One of the benefits of using ASP.NET Web API over WCF services is that the client does need to consume a proxy in order to make a call to Web API service. This facilitates decoupling between the client and the Web API service.
Web APIs are very useful when we use client-side JavaScript frameworks or libraries to make an Ajax call to the Web API. But the scope of Web API is not only limited to JavaScript Ajax calls. Sometimes they can be called from managed clients too e.g. Desktop Applications created using WPF or even in an ASP.NET client or ASP.NET MVC client applications. In this case, we need to make sure that calls to Web API can be handled asynchronously.
The following diagram explains the architecture of calling Web API using a Client Application.
The above diagram explains the basic arrangement of a Web API Service and the client application. The API Service communicates with Database for writing and reading data and uses ADO.NET EntityFramework to communicate with the database. The Data Access Repository, decouples the ADO.NET EntityFramework from the ApiController class, using the simple implementation of the repository pattern, implemented using Unity.WebApi framework. The WEB API Controller class calls the Data Access Repository for performing Read/Write operations. The Managed Client Application, uses HttpClient class for performing an asynchronous calls to the Web API.
HttpClient
This is the base class used for making an asynchronous calls to the remote service repository using its URL. This class is present under the System.Net.Http namespace and provide methods like GetAsync (), PostAsync (), PutAsync () and DeleteAsync ().
The Implementation
Step 1: Open the free Visual Studio 2013 Community Edition and create a Blank solution, name this solution as ‘MVC_ClientWebAPI’. In this solution, add a new ASP.NET Web API Project of the name APIService and click OK.
This will create a Web API project with the necessary folders for Models, Controllers, App_Start, App_Data, etc.
Step 2: In the App_Data folder, add a new Sql Server database of name ‘Application.mdf’. In this database add the EmployeeInfo table using the following script.
CREATE TABLE [dbo].[EmployeeInfo] (
[EmpNo] INT IDENTITY (1, 1) NOT NULL,
[EmpName] VARCHAR (50) NOT NULL,
[DeptName] VARCHAR (50) NOT NULL,
[Designation] VARCHAR (50) NOT NULL,
[Salary] DECIMAL (18) NOT NULL,
PRIMARY KEY CLUSTERED ([EmpNo] ASC)
);
In this table add the sample data as shown in the following script
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (1, N'MS', N'MS IT Services', N'Director', CAST(780000 AS Decimal(18, 0)))
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (2, N'LS', N'Administration', N'Manager', CAST(1556000 AS Decimal(18, 0)))
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (3, N'TS', N'Administration', N'Dy. Manager', CAST(45000 AS Decimal(18, 0)))
Step 3: In the Models folder add a new ADO.NET EF. Name this EF as DataAccess. This will start a Wizard. In this wizard, select the EF Designer from Database, (Note: Since the article uses VS2013 with Update 4, the EF wizard shows EF Designer from Database, else the option may be Generate from Database). The wizard selects Application.mdf, as shown in the following image:
In the next step of the wizard, select EmployeeInfo table as shown in the following image:
After completion of this wizard, the table mapping will be generated. This creates EmployeeInfo class and ApplicationEntities class in the Models folder.
Build the project.
Step 4: Since we will be using a simple Repository pattern to decouple the Web API Controller class from the ADO.NET EF, we need Unity framework for dependency injection for the Web API using constructor injection. To implement this, in the project add Unity.WebApi.5.1 (or whatever version is latest) NuGet package using the Package Manager window. To implement this, right-click on the project and select Manage NuGet Packages. From the NuGet Packages window, select Unity.WebApi.5.1 as shown in the following image:
Click on the install button. This will add Unity.WebApi reference in the project references. The App_Start folder will be added with the UnityConfig.cs file with UnityConfig class in it. We will modify this class in the coming steps.
Step 5: We need to create a separate class to implement the data access logic. In the project, add a new folder of name ‘DataAccessRepository’. In this folder, add a new interface file with the code as shown as following:
public interface IDataAccessRepository<TEntity, in TPrimaryKey> where TEntity : class
{
IEnumerable<TEntity> Get();
TEntity Get(TPrimaryKey id);
void Post(TEntity entity);
void Put(TPrimaryKey id,TEntity entity);
void Delete(TPrimaryKey id);
}
This generic interface contains methods for Get, Post, Put and Delete operations. This interface is typed to the following parameters:
- TEntity: This is the class type, represents the entity class used during Read/Write operations.
- TPrimaryKey: This is an input parameter used during Reading of specific record and deleting it.
Step 5: In the DataAccessRepository folder, add a new class of clsDataAccessRepository with the following code:
using System.Collections.Generic;
using System.Linq;
using APIService.Models;
using Microsoft.Practices.Unity;
namespace APIService.DataAccessRepository
{
public class clsDataAccessRepository : IDataAccessRepository<EmployeeInfo, int>
{
//The dendency for the DbContext specified the current class.
[Dependency]
public ApplicationEntities ctx { get; set; }
//Get all Employees
public IEnumerable<EmployeeInfo> Get()
{
return ctx.EmployeeInfoes.ToList();
}
//Get Specific Employee based on Id
public EmployeeInfo Get(int id)
{
return ctx.EmployeeInfoes.Find(id);
}
//Create a new Employee
public void Post(EmployeeInfo entity)
{
ctx.EmployeeInfoes.Add(entity);
ctx.SaveChanges();
}
//Update Exisitng Employee
public void Put(int id, EmployeeInfo entity)
{
var Emp = ctx.EmployeeInfoes.Find(id);
if (Emp != null)
{
Emp.EmpName = entity.EmpName;
Emp.Salary = entity.Salary;
Emp.DeptName = entity.DeptName;
Emp.Designation = entity.Designation;
ctx.SaveChanges();
}
}
//Delete an Employee based on Id
public void Delete(int id)
{
var Emp = ctx.EmployeeInfoes.Find(id);
if (Emp != null)
{
ctx.EmployeeInfoes.Remove(Emp);
ctx.SaveChanges();
}
}
}
}
The above class code is specified with the ApplicationEntities DbContext injection using [Dependency] attribute on it. This eliminates need of creating instance using new keyword. The class contains methods for performing Read/Write operations using ADO.NET EF methods.
Step 6: To add the above code class in the Unity Container, we need to change the UnityConfig class from the App_Start folder as shown in the following code:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IDataAccessRepository<EmployeeInfo,int>, clsDataAccessRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
The RegisterType () method accepts the interface and class for registering in the Unity container. The UnityDependencyResolver wraps the container object of the type UnityContainer in it. This resolves the dependencies registered inside the container. In our case, we have registered IDataAccessRepository interface and clsDataAccessRepository class.
Open Global.asax, and add the following line in it:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
The above line of code calls the RegisterComponents () method in the Application_Start event so that all dependency must be registered in the beginning of the application.
Step 7: In the Controllers,add a new Empty Web API Controller of the name EmployeeInfoAPIController. In this controller, add the following code:
using System.Collections.Generic;
using System.Net;
using System.Web.Http;
using APIService.Models;
using APIService.DataAccessRepository;
using System.Web.Http.Description;
namespace APIService.Controllers
{
public class EmployeeInfoAPIController : ApiController
{
private IDataAccessRepository<EmployeeInfo, int> _repository;
//Inject the DataAccessRepository using Construction Injection
public EmployeeInfoAPIController(IDataAccessRepository<EmployeeInfo, int> r)
{
_repository = r;
}
public IEnumerable<EmployeeInfo> Get()
{
return _repository.Get();
}
[ResponseType(typeof(EmployeeInfo))]
public IHttpActionResult Get(int id)
{
return Ok (_repository.Get(id));
}
[ResponseType(typeof(EmployeeInfo))]
public IHttpActionResult Post(EmployeeInfo emp)
{
_repository.Post(emp);
return Ok(emp);
}
[ResponseType(typeof(void))]
public IHttpActionResult Put(int id ,EmployeeInfo emp)
{
_repository.Put(id,emp);
return StatusCode(HttpStatusCode.NoContent);
}
[ResponseType(typeof(void))]
public IHttpActionResult Delete(int id)
{
_repository.Delete(id);
return StatusCode(HttpStatusCode.NoContent);
}
}
}
In the above controller, the clsDataAccessRepository object is injected using Constructor Injection. The controller contains methods for performing Http GET, POST, PUT and DELETE operations.
Implementing the ASP.NET MVC Client Applciation
Step 1: In the same solution, add a new ASP.NET MVC application of name ‘MVC_Client’. You can also select Empty MVC application. If you select an empty mvc app, then in that case reference for Json.NET must be added explicitly using NuGet Package Manager. This is for JSON Serialization and Deserialization of the Entity object during Read/Write operation. To make Http calls using C#, we need to add references for System.Net Http.
Note: The NuGet Package for Json.Net and references for System.Net.Http is not required if an ASP.NET MVC ready template is chosen.
Step 2: In this project in the Models folder add the following EmployeeInfo class.
public class EmployeeInfo
{
public int EmpNo { get; set; }
public string EmpName { get; set; }
public decimal Salary { get; set; }
public string DeptName { get; set; }
public string Designation { get; set; }
}
This is the same class available on the API Service side. We need this for the Serialization and Deserialization during Read/Write operations.
Step 3: In the Controllers folder, add a new Empty MVC Controller of the name EmployeeInfoController. In this controller, add the following code:
using Newtonsoft.Json;
namespace MVC_Client.Controllers
{
public class EmployeeInfoController : Controller
{
HttpClient client;
//The URL of the WEB API Service
string url = "http://localhost:60143/api/EmployeeInfoAPI";
//The HttpClient Class, this will be used for performing
//HTTP Operations, GET, POST, PUT, DELETE
//Set the base address and the Header Formatter
public EmployeeInfoController()
{
client = new HttpClient();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
// GET: EmployeeInfo
public async Task<ActionResult> Index()
{
HttpResponseMessage responseMessage = await client.GetAsync(url);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result ;
var Employees = JsonConvert.DeserializeObject<List<EmployeeInfo>>(responseData);
return View(Employees);
}
return View("Error");
}
public ActionResult Create()
{
return View(new EmployeeInfo());
}
//The Post method
[HttpPost]
public async Task<ActionResult> Create(EmployeeInfo Emp)
{
HttpResponseMessage responseMessage = await client.PostAsJsonAsync(url,Emp);
if (responseMessage.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
return RedirectToAction("Error");
}
public async Task<ActionResult> Edit(int id)
{
HttpResponseMessage responseMessage = await client.GetAsync(url+"/"+id);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var Employee = JsonConvert.DeserializeObject<EmployeeInfo>(responseData);
return View(Employee);
}
return View("Error");
}
//The PUT Method
[HttpPost]
public async Task<ActionResult> Edit(int id,EmployeeInfo Emp)
{
HttpResponseMessage responseMessage = await client.PutAsJsonAsync(url+"/" +id, Emp);
if (responseMessage.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
return RedirectToAction("Error");
}
public async Task<ActionResult> Delete(int id)
{
HttpResponseMessage responseMessage = await client.GetAsync(url + "/" + id);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var Employee = JsonConvert.DeserializeObject<EmployeeInfo>(responseData);
return View(Employee);
}
return View("Error");
}
//The DELETE method
[HttpPost]
public async Task<ActionResult> Delete(int id, EmployeeInfo Emp)
{
HttpResponseMessage responseMessage =await client.DeleteAsync(url + "/" + id);
if (responseMessage.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
return RedirectToAction("Error");
}
}
}
The above controller class has the following features:
- The constructor of the class instantiates the HttpClient class. This instance is set with the properties like BaseAddress, this represents the remote Web API URL with which the Read/Write operations are performed. The DefaultRequestHeaders is set to accept the media type formatter for json data.
- The Index () method uses the GetAsyc() method to receive the HttpResponseMessage. Since the response message contains the Json string contents, this content can be read using ReadAsStringAsync() function. The string contents are DeSerialized to the List of Employees using DeserializeObject () method of JsonConvert class. Since this call is asynchronous, the Index() method returns Task<T> object where T is ActionResult.
- Create() function with HttpPost uses PostJsonAsAsync() method to make the asynchronous post of the EmployeeInfo object to the Web API Service.
- Similarly Edit() method uses PutAsJsonAsync() method and Delete method uses DeleteAsync() methods for performing respective operations using Web API.
Step 4: To generate UI for the application, scaffold views from the Index, Create, Edit, Delete (all HttpGet) methods. This adds Index.cshtml, Create.cshtml, Edit.cshtml and Delete.cshtml in the EmployeeInfo folder of the Views folder.
Step 5: To run the Index.cshtml, change the RouteConfig class from RouteConfig.cs file of the App_Start folder.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "EmployeeInfo", action = "Index", id = UrlParameter.Optional }
);
}
}
Step 6: To run the API Service and MVC Client project, right-click on the solution and set the multiple startup project as shown in the following image:
Run the application, the Index view of the Employee will be displayed as shown below:
Click on the Create New link, and the Create View will be displayed. Enter the Employee data in it as shown in the following image:
Click on the ‘Create’ button, the control will be redirected to the Index view with the record added as shown in the following image.
Conclusion: We saw how the HTTPClient class provides useful methods to call an ASP.NET Web API service asynchronously from the managed clients like WPF or ASP.NET MVC.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn