Working With Web Services Through ASP.NET Core And DotVVM - A Step By Step Guide

Introduction 

 
Today, there are many organizations that need to communicate and exchange data with external applications in order to acquire web services that they do not have to meet the needs of their potential customers.
 
In more technical terms, the first goal is to implement the backend of a given system that accesses the database and provides services for the manipulation of this data. From this, the second objective is to use these services, either from a website or from a mobile application, in order to worry only about the design of the frontend in this second instance.
 

Overall Idea of the Solution

 
For this article, we have two objectives. The first is about building a Rest API that will make available web services for the handling of data from a database with MySQL. The second objective is to use these services in a second project through a web page implemented with DotVVM.
 
Working With Web Services Through ASP.NET Core And DotVVM
Note
The source code used in this article is available on GitHub in the following repositories: REST API with ASP.NET Core and MySQL and Consuming Web API in DotVVM with ASP.NET Core.
 

Activities

 
The article will have two important parts:
  • Part 1: Deploying a Web API with ASP.NET Core
  • Part 2: Consuming HTTP Web Services with DotVVM
Resources Needed
 
To follow this article step by step or run the included demo, it is necessary to have the following tools in operation:
  • MySQL
  • .NET Core SDK
  • Visual Studio 2019
  • The Web Development and ASP.NET workload for Visual Studio 2019
  • The DotVVM extension for Visual Studio 2019

Deploying a Web API with ASP.NET Core

 
In this first part, we will have three important sections:
  • Setting the database.
  • Setting database access from ASP.NET Core through Entity Framework.
  • Setting controllers and their functionalities. These methods will be responsible for providing web services for later use.
The case study for this tutorial will handle user information through the CRUD operations: create, read, update, and delete.
 
Database for the Application Domain
 
The database consists of a single table named User, with attributes: Int, FirstName, LastName, Username, Password, and EnrrollmentDate.
 
Working With Web Services Through ASP.NET Core And DotVVM
 
The SQL statement for creating the User table is as follows:
  1. CREATE TABLE `user` (  
  2.   `Id` INT NOT NULL PRIMARY KEY,  
  3.   `FirstName` VARCHAR(45) NOT NULL,  
  4.   `LastName` VARCHAR(45) NOT NULL,  
  5.   `Username` VARCHAR(45) NOT NULL,  
  6.   `Password` VARCHAR(45) NOT NULL,  
  7.   `EnrollmentDate` datetime NOT NULL   
  8. );   
All right, with the database ready we can start with the implementation of the first project for the development of API Rest services.
 
ASP.NET Core Project with Web API Type
 
In Visual Studio 2019, the first thing we'll do is create a new project of type ASP.NET Core Web Application (within the .NET Core - C# category),
 
Working With Web Services Through ASP.NET Core And DotVVM
 
After specifying the project name, we will select the type template: API, for the ASP.NET Core Web project to create:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
With this project, we will create access to the database and implement the corresponding driver, in this case, for the User class.
 
Access to the Database with Entity Framework
 
To establish entities through classes and the database connection, we can use the Entity Framework Database First approach, which allows you to scaffold from the database to the project, that is, generate classes automatically according to the entities established in the database and the connection in the project.
 
For this purpose, we need to install three NuGet packages:
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.Tools
  • MySql.Data.EntityFrameworkCore
In case you are working with SQL Server, the NuGet package to install will be Microsoft.EntityFrameworkCore.SQLServer.
 
Note
To find the NuGet Package Admin Center we can go to the Options Menu -> Project -> Handle NuGet packages.
 
Working With Web Services Through ASP.NET Core And DotVVM
 
With the installation of these NuGet packages, we will now open the Package Management Console to enter a command that will allow you to scaffold from the database:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
Command
  1. Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir Entities -f  
The result is as follows:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
The User class is defined as follows:
  1. public partial class User  
  2. {  
  3.     public int Id { getset; }  
  4.     public string FirstName { getset; }  
  5.     public string LastName { getset; }  
  6.     public string Username { getset; }  
  7.     public string Password { getset; }  
  8.     public DateTime EnrollmentDate { getset; }  
  9. }  
The DBContext, which has the configuration with the database, has a main method OnConfiguring which will look something like this:
  1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  2.         {  
  3.             if (!optionsBuilder.IsConfigured)  
  4.             {                optionsBuilder.UseMySQL("server=localhost;port=3306;user=root;password=;database=database");  
  5.             }  
  6.         }  
Now, it is not as appropriate as the database connection string is specified in this OnConfiguring method, to do this, we will specify the connection string in the appsettings.json file as follows:
  1. "AllowedHosts""*",  
  2. "ConnectionStrings": {  
  3.   "DefaultConnection""server=servername;port=portnumber;user=username;password=pass;database=databasename;"  
  4. }  
Then, in the Startup class in ConfigureServices method we add as a service to the DBContext and reference the DefaultConnection property specified in the appsettings.json file:
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     services.AddEntityFrameworkMySQL()  
  4.         .AddDbContext<DBContext>(options =>  
  5.         {  
  6.             options.UseMySQL(Configuration.GetConnectionString("DefaultConnection"));  
  7.         });  
  8.     services.AddControllers();  
  9. }  
In this case, by returning to the DBContext class, we clear the connection string specified in the OnConfiguring method. In the end, we would have the empty method,
  1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  2. {}  
And the constructor of the DBContext would be constituted as follows:
  1. public DBContext(DbContextOptions<DBContext> options)  
  2.     : base(options) {}  
With these steps, we already have the connection and configurations necessary to work with the database in ASP.NET Core with the help of Entity Framework.
 
Set DTOs - Data Transfer Objects
 
In order to transport the data between the processes for database management and the processes to work with the web services, it is advisable to establish the DTO classes for each entity of the project, in this case, a DTO for the User entity.
 
To do this we will create a new folder within the project called DTO and create a class called UserDTO, whose attributes will be the same as the User class defined in the Entities section above:
  1. public class UserDTO  
  2. {  
  3.     public int Id { getset; }  
  4.     public string FirstName { getset; }  
  5.     public string LastName { getset; }  
  6.     public string Username { getset; }  
  7.     public string Password { getset; }  
  8.     public DateTime EnrollmentDate { getset; }  
  9. }  
Web API Controllers
 
Now what we'll do is add the handlers, in this case, the driver for the user, which will allow us to set methods to perform CRUD operations on the database tables and expose them through the Web API. Above the Controllers folder, we'll add a driver called User
 
Working With Web Services Through ASP.NET Core And DotVVM
 
The definition of the class and its constructor will look like this:
  1. [ApiController]  
  2. [Route("api/[controller]")]  
  3. public class UserController : ControllerBase  
  4. {  
  5.     private readonly DBContext DBContext;  
  6.   
  7.     public UserController( DBContext DBContext)  
  8.     {  
  9.         this.DBContext = DBContext;  
  10.     }  
  11.     ...  
  12. }  
Now the goal is to implement the methods of this controller to perform CRUD operations. At this point, it is important to mention the basic methods of HTTP. These methods are:
  • POST (create): This is a method for when sending information to insert a record into the database for example. The information is sent in the body of the request, that is, the data is not visible to the user.
  • GET (read): This is a method used for a read mode, for example: when you want to list all users in a database. Parameters are sent by a URL.
  • PUT (update): This is a method for when you want to update a record.
  • DELETE: This is a method for when you want to delete a record. Delete an X user from a database.
For the implementation of CRUD operations, we will use methods to access the information (Get), to insert data (Post), to modify (Put), and to delete a record (Delete).
 
The final code for each of the methods is shown below:
 
A. Get the list of all registered users 
  1. [HttpGet("GetUsers")]  
  2. public async Task<ActionResult<List<UserDTO>>> Get()  
  3. {  
  4.     var List = await DBContext.User.Select(  
  5.         s => new UserDTO  
  6.         {  
  7.             Id = s.Id,  
  8.             FirstName = s.FirstName,  
  9.             LastName = s.LastName,  
  10.             Username = s.Username,  
  11.             Password = s.Password,  
  12.             EnrollmentDate = s.EnrollmentDate  
  13.         }  
  14.     ).ToListAsync();  
  15.   
  16.     if (List.Count < 0)  
  17.     {  
  18.         return NotFound();  
  19.     }  
  20.     else  
  21.     {  
  22.         return List;  
  23.     }  
  24. }  
B. Get the data of a specific user according to their ID 
  1. [HttpGet("GetUserById")]  
  2. public async Task<ActionResult<UserDTO>> GetUserById(int Id)  
  3. {  
  4.     UserDTO User = await DBContext.User.Select(  
  5.             s => new UserDTO  
  6.             {  
  7.                 Id = s.Id,  
  8.                 FirstName = s.FirstName,  
  9.                 LastName = s.LastName,  
  10.                 Username = s.Username,  
  11.                 Password = s.Password,  
  12.                 EnrollmentDate = s.EnrollmentDate  
  13.             })  
  14.         .FirstOrDefaultAsync(s => s.Id == Id);  
  15.   
  16.     if (User == null)  
  17.     {  
  18.         return NotFound();  
  19.     }  
  20.     else  
  21.     {  
  22.         return User;  
  23.     }  
  24. }  
C. Insert a new user 
  1. [HttpPost("InsertUser")]  
  2. public async Task<HttpStatusCode> InsertUser(UserDTO User)  
  3. {  
  4.     var entity = new User()  
  5.     {  
  6.         FirstName = User.FirstName,  
  7.         LastName = User.LastName,  
  8.         Username = User.Username,  
  9.         Password = User.Password,  
  10.         EnrollmentDate = User.EnrollmentDate  
  11.     };  
  12.   
  13.     DBContext.User.Add(entity);  
  14.     await DBContext.SaveChangesAsync();  
  15.   
  16.     return HttpStatusCode.Created;  
  17. }  
D. Update the data of a specific user 
  1. [HttpPut ("UpdateUser")]  
  2. public async Task<HttpStatusCode> UpdateUser(UserDTO User)  
  3. {  
  4.     var entity = await DBContext.User.FirstOrDefaultAsync(s => s.Id == User.Id);  
  5.   
  6.     entity.FirstName = User.FirstName;  
  7.     entity.LastName = User.LastName;  
  8.     entity.Username = User.Username;  
  9.     entity.Password = User.Password;  
  10.     entity.EnrollmentDate = User.EnrollmentDate;  
  11.   
  12.     await DBContext.SaveChangesAsync();  
  13.     return HttpStatusCode.OK;  
  14. }  
E. Delete a user based on their ID 
  1. [HttpDelete("DeleteUser/{Id}")]  
  2. public async Task<HttpStatusCode> DeleteUser(int Id)  
  3. {  
  4.     var entity = new User()  
  5.     {  
  6.         Id = Id  
  7.     };  
  8.     DBContext.User.Attach(entity);  
  9.     DBContext.User.Remove(entity);  
  10.     await DBContext.SaveChangesAsync();  
  11.     return HttpStatusCode.OK;  
  12. }  
With these methods and with the steps followed up to this point, the services are ready to run.
 
Test the developed Web API
 
To test the deployed Web API we will make use of Postman, a cross-platform tool that allows you to do REST API testing. The installer can be found here.
 
Build and run our application from Visual Studio 2019:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
Once Postman is started, we can perform the first test. Below, we can see the use of the service to recover all registered users:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
As we can see in the image, it is necessary to specify the type of operation, in this case, it is of type GET, as specified in the user driver. Also, we can see that the status of the result is 200 OK – that is, everything went well. And finally, we can see in the response the JSON with the results.
 
As an additional example, we can see the case to get a specific user:
 
Working With Web Services Through ASP.NET Core And DotVVM
 

Consuming HTTP services with DotVVM

 
In this second part, we will have three important sections:
  1. Set methods for communication with HTTP services defined above.
  2. Implement the views and models of these views for the development of the corresponding web page.
The goal with web pages is to consume CRUD operations: create, read, update, and delete set in HTTP services.
 
DotVVM project with ASP.NET Core
 
Working With Web Services Through ASP.NET Core And DotVVM
 
In DotVVM, communication between HTML (web pages) and C-code (source code) is done through the MVVM design pattern (Model, View, ViewModel). The purpose of these elements are as follows:
  • Model. — is responsible for all application data and related business logic.
  • The view. — Representations for the end-user of the application model. The view is responsible for displaying the data to the user and allowing manipulation of the application data.
  • Model-View or View-Model. — one or more per view; the model-view is responsible for implementing view behavior to respond to user actions and for easily exposing model data.
Models and Logic of the Application
 
In this first part, we will define the models and services to consume HTTP services and set the logic of our application. In this case, what you are looking for is to have a general list of users and specific information on each of them.
 
To do this, as the first point we will define the models:
 
UserList
  1. public class UserListModel  
  2. {  
  3.     public int Id {getset;}  
  4.     public string FirstName {getset;}  
  5.     public string LastName {getset;}  
  6. }  
UserDetailModel
  1. public class UserDetailModel  
  2. {  
  3.     public int Id { getset; }  
  4.     [Required]  
  5.     public string FirstName { getset; }  
  6.     [Required]  
  7.     public string LastName { getset; }  
  8.     [Required]  
  9.     public string Username { getset; }  
  10.     [Required]  
  11.     public string Password { getset; }  
  12.     [Required]  
  13.     public DateTime EnrollmentDate { getset; }  
  14. }   
And then the services of our application. In this case, we have the user service that will allow us to set the CRUD operations according to the HTTP methods.
 
For User service initialization, we can start with a definition like this:
  1. public class UserService  
  2. {  
  3.     private readonly string URLbase = "https://localhost:5003/api/User";  
  4.     …  
From the base address for HTTP service consumption, the following are the corresponding methods:
 
A. Get the list of all registered users
  1. public async Task<List<UserListModel>> GetAllUsersAsync()  
  2. {  
  3.     List<UserListModel> usersList = new List<UserListModel>();  
  4.     using (var httpClient = new HttpClient())  
  5.     {  
  6.         string URL = URLbase + "/GetUsers";  
  7.         HttpResponseMessage response = await httpClient.GetAsync(URL);  
  8.         string apiResponse = await response.Content.ReadAsStringAsync();  
  9.         usersList = JsonConvert.DeserializeObject<List<UserListModel>>(apiResponse).Select(  
  10.             s => new UserListModel  
  11.             {  
  12.                 Id = s.Id,  
  13.                 FirstName = s.FirstName,  
  14.                 LastName = s.LastName  
  15.             }  
  16.             ).ToList();  
  17.     }  
  18.     return usersList;  
  19. }  
B. Get the data of a specific user according to their ID 
  1. public async Task<UserDetailModel> GetUserByIdAsync(int Id)  
  2. {  
  3.     string URL = URLbase + "/GetUserById?id=" + Id;  
  4.     UserDetailModel User = new UserDetailModel();  
  5.   
  6.     using (var httpClient = new HttpClient())  
  7.     {  
  8.         HttpResponseMessage response = await httpClient.GetAsync(URL);  
  9.         string apiResponse = await response.Content.ReadAsStringAsync();  
  10.         User = JsonConvert.DeserializeObject<UserDetailModel>(apiResponse);  
  11.     }  
  12.     return User;  
  13. }  
C. Insert a new user 
  1. public async Task InsertUserAsync(UserDetailModel user)  
  2. {  
  3.     string URL = URLbase + "/InsertUser";  
  4.   
  5.     using (var httpClient = new HttpClient())  
  6.     {  
  7.         StringContent content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");  
  8.         HttpResponseMessage response = await httpClient.PostAsync(URL, content);  
  9.         string apiResponse = await response.Content.ReadAsStringAsync();         
  10.     }  
  11. }  
D. Update a specific user's data 
  1. public async Task UpdateUserAsync(UserDetailModel user)  
  2. {  
  3.     string URL = URLbase + "/UpdateUser";  
  4.   
  5.     using (var httpClient = new HttpClient())  
  6.     {  
  7.         StringContent content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");  
  8.   
  9.         HttpResponseMessage response = await httpClient.PutAsync(URL, content);  
  10.         string apiResponse = await response.Content.ReadAsStringAsync();  
  11.     }  
  12. }  
E. Delete a user based on their ID 
  1. public async Task DeleteUserAsync(int Id)  
  2. {  
  3.     string URL = URLbase + "/DeleteUser/" + Id;  
  4.   
  5.     using (var httpClient = new HttpClient())  
  6.     {  
  7.         var response = await httpClient.DeleteAsync(URL);  
  8.         string apiResponse = await response.Content.ReadAsStringAsync();  
  9.     }  
  10. }  
Views and viewmodels
 
Now that the methods consuming HTTP services have been defined, we now only have to design the web page so that the user can interact with it and in this case, perform CRUD operations for user handling.
 
This is the part where DotVVM comes into action. Each page in DotVVM consists of two files:
  • A view, which is based on HTML syntax, and describes what the page will look like.
  • A model of the view, that is, a class in CTM that describes the state of the page (for example, values in form fields) and handles user interactions (for example, button clicks).
For our case, we will have four views and four models associated with these views:
  • Default: it will be the main page of the application where the list of registered users will be displayed.
  • Create: A page made up of a form to create new users.
  • Detail: To see a user's information in detail.
  • Edit: to modify a user's information or delete it.
Considering the Views and Viewmodels files, in Visual Studio we'll visualize something like this:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
Next, let's take a closer look at the View and Viewmodel of Default and its components.
 
Viewmodel del Default
  1. public class DefaultViewModel : MasterPageViewModel  
  2. {  
  3.     private readonly UserService userService;  
  4.   
  5.     public DefaultViewModel(UserService userService)  
  6.     {  
  7.         this.userService = userService;  
  8.     }  
  9.   
  10.     [Bind(Direction.ServerToClient)]  
  11.     public List<UserListModel> Users { getset; }  
  12.   
  13.     public override async Task PreRender()  
  14.     {  
  15.         Users = await userService.GetAllUsersAsync();  
  16.         await base.PreRender();  
  17.     }  
  18. }  
As the first point, we have the instance of UserService that will allow us to access the methods to handle the operations defined in the User service implemented previously.
 
Then we have the definition List<UserListModel> Users of type UserListModel defined in the model classes, which will have the list of users (Id, FirstName and LastName) to load them into a table on the main page of the web application.
 
A very important feature to mention is the declaration [Bind(Direction.ServerToClient)]. This type of property allows us to specify which information to be transferred from the server to the client or from the client to the server when using Binding Directions. Considering the case of the user list, it is often not necessary to transfer the entire view model in both directions. From the server to the view will suffice in this case.
 
Learn more about Binding Directions here.
 
Finally, in Default Viewmodel we have the method PreRender(), which allows us to perform certain kinds of operations that will be performed at the time of loading the View. In this case, the database will be queried through the service method call userService.GetAllUsersAsync(), then the results will be assigned to the Users collection of type UserListModel and then the page will be loaded along with the other design components.
 
Default View
  1. @viewModel DotVVM_APIConsume.ViewModels.DefaultViewModel, DotVVM_APIConsume  
  2. @masterPage Views/MasterPage.dotmaster  
  3. <dot:Content ContentPlaceHolderID="MainContent">  
  4.     <div class="page-center">  
  5.         <div class="page-grid-top">  
  6.         <div class="student-image"></div>  
  7.             <h1>User List</h1>  
  8.   
  9.                 <dot:RouteLink Text="New User" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>  
  10.   
  11.         </div>  
  12.         <dot:GridView DataSource="{value: Users}" class="page-grid">  
  13.             <Columns>  
  14.                 <dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />  
  15.                 <dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="Lastname" />  
  16.                 <dot:GridViewTemplateColumn>  
  17.                     <dot:RouteLink Text="Detail" RouteName="CRUD_Detail" Param-Id="{{value: Id}}" />  
  18.                 </dot:GridViewTemplateColumn>  
  19.                 <dot:GridViewTemplateColumn>  
  20.                     <dot:RouteLink Text="Edit" RouteName="CRUD_Edit" Param-Id="{{value: Id}}" />  
  21.                 </dot:GridViewTemplateColumn>  
  22.             </Columns>  
  23.              <EmptyDataTemplate>  
  24.                 There are no registered users.   
  25.             </EmptyDataTemplate>  
  26.         </dot:GridView>  
  27.     </div>  
  28. </dot:Content>  
As we can see the View of Default, the page layout becomes the handling of HTML and CSS statements. For our case study, there are some interesting statements and features that we can analyze,
 
GridView
 
<dot:GridView ... >, a DotVVM control that allows us to create a table or grid to display a certain list of information. In HTML we would be talking about the tag <table>. One of its attributes is DataSource: DataSource="{value: Users}" which allows us to specify the data source, in this case, we refer to the list of users: 'Users', which was defined in the ViewModel as we saw earlier.
 
In addition to tables, DotVVM also has other custom control components, for example, for text boxes, ComboBox, file handling, among others that allow us to maintain communication between the View and the information sources defined in the Viewmodels.
 
Continuing with our analysis, in the GridView we have the columns Id, FirstName, and LastName of the users, but additionally, we can also add columns to perform operations on some specific record. In this case, with RouteLink, we can define a hyperlink that constructs a URL from route names and parameter values to redirect us to other pages or perform additional operations, for example, view detail or modify a particular student's record based on their Id:
  1. <dot:RouteLink RouteName="Edit" Param-Id="{{value: Id}}" />  
These routes and their corresponding parameters are defined in the DotvvmStartup.cs file in the ConfigureRoutes method as follows: 
  1. config.RouteTable.Add("Edit""edit/{Id}""Views/Edit.dothtml");  
The Create, View Detail, and Modify pages follow the same logic for the View and Viewmodel components.
 
Execution of solutions
 
Up to this point, we have implemented two solutions, the first to provide HTTP services over ASP.NET Core, and the second, to consume these services through a web application with DotVVM. To test locally, both Visual Studio solutions must run in order to enable HTTP services and consume them in turn. Below we can see some screenshots about the deployed web application
 
Create a new record
 
Working With Web Services Through ASP.NET Core And DotVVM
 
Get the detail of a specific record:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
General list of users:
 
Working With Web Services Through ASP.NET Core And DotVVM
 
What's next?
 
With this article, we have learned step by step how to implement HTTP services that handle database information with ASP.NET Core and how to consume them through a project with DotVVM.
 
The next activity is to publish these cloud services so that they can be accessed from anywhere. In Azure, for example, the process for uploading a Web API project or a project made up of web pages is the same. The steps to carry out this process can be found in the following article - Deploy DotVVM and .NET Core web applications to Azure (Everything you need to know).
 
The source code of the two projects used in this article is available in the following GitHub repositories - REST API with ASP.NET Core and MySQL and Consuming Web API in DotVVM with ASP.NET Core.
 
Thank you for reading,
 
I hope you liked the article. To stay on top of future contributions or if you have any concerns, you can follow me on Twitter: twitter.com/esDanielGomez.
 
See you soon!