Today there are many organizations that need to communicate and exchange data with external applications, in order to acquire web services with which they do not have and meet the needs of their potential customers.
In more technical terms, our first objective is to implement the application logic of a particular system that accesses the database and provides web services for the manipulation of this data. From this, the second objective is to use these services, in order to worry only about the design of the corresponding web pages in this instance.
Web services with SOAP
SOAP web services, or "big" web services, are those that use XML messages for intercom, which follow the Simple Object Access Protocol (SOAP) standard, an XML language that defines the architecture and format of messages. Such systems typically contain a machine-readable description of the operations offered by the service, written in Web Services Description Language (WSDL), which is an XML-based language for defining interfaces syntactically.
The overall idea of the solution:
This article has two main objectives. The first is about building a service-oriented application with WCF – Windows Communication Foundation, which will allow us to work with data hosted in a MySQL database as asynchronous messages from one service endpoint to another. The second objective is to use these services in a second project through a web page implemented with DotVVM on ASP.NET Core.
Note: The source code used in this article is available on GitHub in the following repositories: WCF (Windows Communication Foundation) Service on ASP.NET Framework and Consuming WCF Service in DotVVM with ASP.NET Core.
Activities:
The article will have two important parts:
- Part 1: Implementing a Web Service with SOAP
- Part 2: Consuming SOAP 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:
- 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.
Part 1: Deploying a Web API with ASP.NET Core
In this first part we will have three important parts:
- 1. Set the database.
- 2. Set database access from ASP.NET Framework through Entity Framework.
- 3. Establish a service contract and its operations.
As a case study for this tutorial user data will be handled through CRUD operations: create, read, update, and delete.
The database for the application domain
The database consists of a single table named: User, with attributes: Id
, FirstName
, LastName
, Username
, Password
and EnrrollmentDate
.
The SQL statement for creating the User
table is as follows:
CREATE TABLE `user` (
`Id` INT NOT NULL PRIMARY KEY,
`FirstName` VARCHAR(45) NOT NULL,
`LastName` VARCHAR(45) NOT NULL,
`Username` VARCHAR(45) NOT NULL,
`Password` VARCHAR(45) NOT NULL,
`EnrollmentDate` datetime NOT NULL
);
All right, with the database ready, we can start with the implementation of the first project for SOAP service development.
WCF Service Application Project
In Visual Studio 2019 the first thing we'll do is create a new project of type WCF Service Application (within the C-Web category):
With this project, we will create access to the database, the definition of the service interface, and the corresponding implementation, in this case, for the User entity.
Access to the database with Entity Framework
To establish entities through classes and the database connection, you can use the Entity Framework Database First approach, which allows you to scaffold from the database to the project, that is, automatically generate classes according to the entities established in the database and the connection in the project.
For this purpose, you need to install three NuGet packages:
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
MySql.Data.EntityFrameworkCore
If you are working with SQL Server, the NuGet package to install will be: Microsoft.EntityFrameworkCore.SQLServer
.
Note: To find the NuGet Package Management Center, we can go to the options menu -> Project -> Handle NuGet Packages.
With the installation of these NuGet packages, we will now open the Package Management Console to enter a command that will allow you to perform scaffolding from the database:
Command:
Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir DAL/Entities -f
The result is as follows:
Here, the User
class is defined as follows:
public partial class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public DateTime EnrollmentDate { get; set; }
}
And the DBContext
, which has the configuration with the database and will allow us to perform the corresponding operations on it.
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 operations to work with the web services, it is advisable to establish the DTO classes for each entity in the project, in this case, a DTO for the entity User
.
To do this we will define 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:
public class UserDTO
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public DateTime EnrollmentDate { get; set; }
}
Service contracts
The fundamental thing in a SOAP web service is the contract that exists between client and service, that is, the WSDL document. Therefore, when creating web services it is advisable to start by defining that contract. This way we'll have more control over the data that is serialized during service invocation, so we can define the data structures that we consider most appropriate for the exchange. In this case, we will define the operations to implement in the web service through an interface. The result will be as follows:
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IService
{
[OperationContract]
List<UserDTO> Get();
[OperationContract]
UserDTO GetUserById(int Id);
[OperationContract]
bool InsertUser(UserDTO User);
[OperationContract]
void UpdateUser(UserDTO User);
[OperationContract]
void DeleteUser(int Id);
}
Then we must implement this interface, in which we will find the established methods, along with the operations necessary to receive and send the corresponding data. In this case, the file is in an extension of .svc and the code of the operations will be as follows:
Initial definition:
public class Service : IService
{
private readonly DBContext DBContext = new DBContext();
...
A. Get the list of all registered users
public List<UserDTO> Get()
{
return DBContext.User.Select(
s => new UserDTO
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
Username = s.Username,
Password = s.Password,
EnrollmentDate = s.EnrollmentDate
}
).ToList();
}
B. Get the data of a specific user according to their ID
public UserDTO GetUserById(int Id)
{
return DBContext.User.Select(
s => new UserDTO
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
Username = s.Username,
Password = s.Password,
EnrollmentDate = s.EnrollmentDate
})
.FirstOrDefault(s => s.Id == Id);
}
C. Insert a new user
public bool InsertUser(UserDTO User)
{
var entity = new User()
{
FirstName = User.FirstName,
LastName = User.LastName,
Username = User.Username,
Password = User.Password,
EnrollmentDate = User.EnrollmentDate
};
DBContext.User.Add(entity);
DBContext.SaveChangesAsync();
return true;
}
D. Update a specific user's data
public void UpdateUser(UserDTO User)
{
var entity = DBContext.User.FirstOrDefault(s => s.Id == User.Id);
entity.FirstName = User.FirstName;
entity.LastName = User.LastName;
entity.Username = User.Username;
entity.Password = User.Password;
entity.EnrollmentDate = User.EnrollmentDate;
DBContext.SaveChangesAsync();
}
E. Delete a user based on their ID
public void DeleteUser(int Id)
{
var entity = new User()
{
Id = Id
};
DBContext.User.Attach(entity);
DBContext.User.Remove(entity);
DBContext.SaveChangesAsync();
}
The interface (.cs) and deployment (.svc) file will look like this within the Visual Studio solution:
With these few steps followed so far, the SOAP service for handling user data is ready to run.
Test the SOAP service developed
To test the deployed service we have several ways, one of them is to place ourselves in the SVC service implementation file and run the project. In this run, the WCF Test Client will be executed and will help us test the implemented functions.
In a first example we can test the Get() method and display the list of registered users, as we can see in the following image:
Another case may be to get a specific user based on their ID, for this we will select the GetUserById() operation, and in this selection, the WCF test client will tell us the input parameters that we need to specify. The result is as follows:
As an additional example, we can analyze the case of creating a new user through the InsertUser() operation, along with its corresponding input parameters:
All right, another way we can test our implemented SOAP services is through the Postman tool. In this case, we'll run the WCF project by placing us in any file within Visual Studio 2019 other than the SVC service implementation. Performing this run will open the browser with the information on this service.
In Postman we will have to specify three things, on the one hand, the service binding, in this example will be: http://localhost:16260/Service.svc, whose selection will be in a POST method. Then we need to specify the headers according to the operation to be tested, in this case, we will exemplify the consumption with the GetUserById() operation:
Content-Type
: text/xml
SOAPAction
: http://Microsoft.ServiceModel.Samples/IService/GetUserById
Then we will have to specify the body XML with the input parameters to send, in this case, the user ID:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetUserById xmlns="http://Microsoft.ServiceModel.Samples">
<Id>1</Id>
</GetUserById>
</s:Body>
</s:Envelope>
Finally, when we make the request to the SOAP service we will get the XML response. The fields and results in Postman can be displayed in the following image:
The call of the other operations will be seen when using these services directly from a web application with DotVVM.
Part 2: Consuming WCF services with DotVVM
In this second part we will have two important parts:
- 1. Set methods for communication with SOAP services defined above.
- 2.Implement the views and models of these views for the development of the corresponding web page.
The purpose of these web pages is to publicize the resulting data, from the consumption of CRUD operations: create, read, update, and delete set out in the SOAP service contract.
DotVVM project with ASP.NET Core
For this new purpose, in Visual Studio 2019 the first thing we'll do is create a new project of type DotVVM Web Application (.NET Core):
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:
- The 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 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.
Set the reference with WCF Web Service
As the first point what we will need to do, is to establish a reference to the WCF service implemented earlier. For this purpose within the Visual Studio solution, we can go to the Connected Services
section and select Add Connected Service
:
In the Connected Service Profiles section we will select the option: Microsoft WCF Web Service Reference Provider
:
From this selection, the WCF Web Service Reference
configuration wizard will be initialized, and in this section, we will need to specify the URL with the WSDL metadata of the SOAP service, for this our WCF project must be running.
In this example, the corresponding URL is: http://localhost:16260/Service.svc?wsdl. When loaded, the setup wizard will identify the service in question and the operations included. In this case, the last thing to define will be the name of the namespace where the classes and files within the project will be generated to access this service.
When you select Finish, the wizard will perform a scaffolding process, that is, generate the corresponding files to access this service.
At the end of the operation, we will see some classes and files like these:
Models and logic of the application
Next, we need to implement the models and services that will consume the operations implemented in SOAP 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:
A. UserListModel
public class UserListModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
B. UserDetailModel
public class UserDetailModel
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public DateTime EnrollmentDate { get; set; }
And then the logic services of our application. In this case we have the user service that will allow us to establish CRUD operations according to the service contracts in SOAP.
For User service initialization, we can start with a definition like this:
ServiceClient reference = new ServiceClient();
This statement corresponds to the instantiation of one of the previously generated classes. From this base object for SOAP service consumption, the following are the corresponding methods:
A. Get the list of all registered users
public List<UserListModel> GetAllUsers()
{
return reference.GetAsync().Result.Select(
s => new UserListModel
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName
}).ToList();
}
B. Get the data of a specific user according to their ID
public async Task<UserDetailModel> GetUserByIdAsync(int Id)
{
var result = await reference.GetUserByIdAsync(Id);
return new UserDetailModel()
{
Id = result.Id,
FirstName = result.FirstName,
LastName = result.LastName,
Username = result.Username,
Password = result.Password,
EnrollmentDate = result.EnrollmentDate
};
}
C. Insert a new user
public async Task InsertUserAsync(UserDetailModel user)
{
UserDTO NewUser = new UserDTO() {
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Username = user.Username,
Password = user.Password,
EnrollmentDate = user.EnrollmentDate
};
await reference.InsertUserAsync(NewUser);
}
D. Update a specific user's data
public async Task UpdateUserAsync(UserDetailModel user)
{
UserDTO UserToUpdate = new UserDTO()
{
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Username = user.Username,
Password = user.Password,
EnrollmentDate = user.EnrollmentDate
};
await reference.UpdateUserAsync(UserToUpdate);
}
E. Delete a user based on their ID
public async Task DeleteUserAsync(int Id)
{
await reference.DeleteUserAsync(Id);
}
Views and viewmodels
Now that the methods that consume the services in XML format 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.
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 view model, a class in C-class 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: will be the main page of the application where the list of registered users will be displayed.
- Create: a page consisting of a form to create new users.
- Detail: to see a student's information in detail.
- Edit: to modify or delete a student's information.
Considering the Views and Viewmodels files, in Visual Studio we'll see something like this:
Next, let's take a closer look at the View and Viewmodel of Default
and its components.
Default Viewmodel
public class DefaultViewModel : MasterPageViewModel
{
private readonly UserService userService;
public DefaultViewModel(UserService userService)
{
this.userService = userService;
}
[Bind(Direction.ServerToClient)]
public List<UserListModel> Users { get; set; }
public override async Task PreRender()
{
Users = userService.GetAllUsers();
await base.PreRender();
}
}
As a 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> Students
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.
Finally, in the Viewmodel of Default
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 layout components.
Default View
@viewModel DotVVM.ViewModels.DefaultViewModel, DotVVM
@masterPage Views/MasterPage.dotmaster
<dot:Content ContentPlaceHolderID="MainContent">
<div class="page-center">
<div class="page-grid-top">
<div class="student-image"></div>
<h1>User List</h1>
<dot:RouteLink Text="New User" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>
</div>
<dot:GridView DataSource="{value: Users}" class="page-grid">
<Columns>
<dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="Firstname" />
<dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="Lastname" />
<dot:GridViewTemplateColumn>
<dot:RouteLink Text="Detail" RouteName="CRUD_Detail" Param-Id="{{value: Id}}" />
</dot:GridViewTemplateColumn>
<dot:GridViewTemplateColumn>
<dot:RouteLink Text="Edit" RouteName="CRUD_Edit" Param-Id="{{value: Id}}" />
</dot:GridViewTemplateColumn>
</Columns>
<EmptyDataTemplate>
There are no registered users.
</EmptyDataTemplate>
</dot:GridView>
</div>
</dot:Content>
As we can see from the Default View, 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 crew to display a certain list of information. In HTML we would be talking about the tag <table>
. One of its attributes is DataSource
: DataSource
, which allows you to specify the data source, in this case, we refer to the list of students: Users
, which was defined in the Viewmodel as we saw above.
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 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 path 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:
<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:
config.RouteTable.Add("Edit", "edit/{Id}", "Views/Edit.dothtml");
To create, view detail, and modify pages, the same logic is followed for the View and ViewModel components.
Execution of solutions
Up to this point, we have implemented two solutions, the first to provide SOAP services through a WCF project, and the second, consume these services through a web application with DotVVM. To test locally, both Visual Studio solutions must run before you can enable the SOAP service and consume it in turn. Below we can see some screenshots about the deployed web application:
- Create a new record
- Get the detail of a specific record
- General list of users
What's next?
With this article, we've learned step-by-step how to implement SOAP services that handle database information with ASP.NET Core and how to consume it through a Project with DotVVM.
The source code for the two projects used in this article is available in the following GitHub repositories: WCF (Windows Communication Foundation) Service on ASP.NET Framework and Consuming WCF Service 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!
Top comments (2)
An important change since this article was written. Version 3.1.32 of the Microsoft.EntityFrameworkCore.SqlServer NuGet package is the last version to support NetFx. Any version newer than this will result in an error and will not be able to be installed in your project. This is due to the fact that standard WMF does not support Net Core, which is all that the later versions of the package support.
Great explanation and detail.
It would be great to see how to activate HTTPS and Client Certificate, regards.