Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL

DotVVM is an ASP.NET framework that allows us to create web applications using the MVVM (View-Model Model) design pattern using C# and HTML. In this tutorial, we will learn how to create CRUD (Create, Read, Update, and Delete) operations using a relational database with PostgreSQL, from ASP.NET Core.
 
Want to know what the steps are to create a DotVVM app? To do this, you can review this article - Steps to Create an MVVM Application (Model-View-ViewModel) with DotVVM and ASP.NET Core.
 

Introduction to ADO.NET Entity Framework

 
ADO.NET Entity Framework is an Object Relational Mapping (ORM) framework. It is designed to allow us to create data access applications by programming in a conceptual application model instead of programming directly into a relational storage scheme. The goal is to reduce the amount of code and maintenance required for data-oriented applications.
 
Resources required
 
To work with PostgreSQL ASP.NET Core with DotVVM, the recommended tools for setting up our work environment are as follows,
  • Visual Studio 2019
  • The workload in Visual Studio 2019: Developing ASP.NET and Web.
  • DotVVM extension for Visual Studio 2019
  • PostgreSQL

The database in PostgreSQL

 
For this tutorial article, let's create a sample database in PostgreSQL, which allows us to work with user data and then implement CRUD operations on ASP.NET Core.
 
To establish our database we can use the pgAdmin4 tool, which is usually included when installing PostgreSQL. When you create the schema, the SQL code for creating our user table is as follows,
  1. CREATE SEQUENCE id_seq  
  2.     INCREMENT 1  
  3.     START 1  
  4.     MINVALUE 1  
  5.     MAXVALUE 9223372036854775807  
  6.     CACHE 1;  
  7.   
  8. CREATE TABLE person  
  9. (  
  10.     id_ integer NOT NULL DEFAULT nextval('id_seq'::regclass),  
  11.     firstname character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  12.     lastname character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  13.     username character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  14.     city character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  15.     country character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  16.     postalcode integer NOT NULL,  
  17.     about character varying(45) COLLATE pg_catalog."default" NOT NULL,  
  18.     enrollmentdate timestamp without time zone NOT NULL,  
  19.     CONSTRAINT person_pkey PRIMARY KEY (id_)  
  20. )  
Here the primary key id_ is in a sequence so that the value of this key is self-incremental.
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 

Project in Visual Studio 2019

 
Now that the database is set, then we can start developing our solution in Visual Studio. In this new project we will have three parts,
  • Data Access Layer (DAL): To handle the connection to PostgreSQL and database access.
  • BL (Business Layer): for the management of the services and the logic of the application domain.
  • PL: to work with the presentation layer of the application, in this case, with DotVVM.
To get started, we'll create a new project in Visual Studio 2019 of the type DotVVM Web Application,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
When creating the project, the DotVVM setup wizard allows you to select a number of options with settings, styles, and templates already preset,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
In this case, we will leave everything like this and create the project. When you create a DAL and BL folder for the data access layer and business logic respectively, in the solution you'll have something like this,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 

Connection to PostgreSQL

 
All right, as a first point, we're going to relate our project to the database created in PostgreSQL. In the Entity Framework, there are two approaches, the first Code-First, which allows us to generate the database through classes, and the second, Database-First, which allows us to generate feature classes from an existing database. As expected, in this case, we will use the Database-First approach. To meet this goal, we will need to install three Nuget packages,
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.Tools
  • Npgsql.EntityFrameworkCore.PostgreSQL
Then we will need to insert command from the package manager console. This console can be activated from the Options Menu -> View -> Other Windows -> the Package Management Console.
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
In this console we will insert the following command,
  1. Scaffold-DbContext "Host=hostname;port=portnumber;Username=username;Password=pass;Database=databasename" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir DAL/Entities  
Where,
  • Hostname, is the name of the server from which the database is located. Example: localhost.
  • Portnumber, host port. PostgreSQL is usually located on port 5432.
  • Username, database username.
  • Password, password of the user who will access the database.
  • Database, database name.
Also,
  • -OutputDir, with this keyword we can indicate where the files will be generated.
  • -Table, additional abbreviation in case you want to indicate the specific tables to generate in our data access layer.
When you enter the command correctly, you'll have something like this,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
Where Person is the class that is associated with the entity with the same name in the database and the DBContext is where the corresponding configurations are located.
 
The Person class is defined as follows,
  1. public partial class Person  
  2. {  
  3.     public int Id { getset; }  
  4.     public string Firstname { getset; }  
  5.     public string Lastname { getset; }  
  6.     public string Username { getset; }  
  7.     public string City { getset; }  
  8.     public string Country { getset; }  
  9.     public int Postalcode { getset; }  
  10.     public string About { getset; }  
  11.     public DateTime Enrollmentdate { getset; }  
And the DBContext, which has the configuration with the database, whose main method OnConfiguring will look something like this,
  1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  2.         {  
  3.             if (!optionsBuilder.IsConfigured)  
  4.             {                optionsBuilder.UseMySQL("host=localhost;port=5432;username=<Username>;password=;database=<Database_name>");  
  5.             }  
  6.         }  
Now, it is not the most appropriate for the database connection string to be specified in this OnConfiguring method, for this, we will specify the connection string in the appsettings.json file as follows,
  1. "AllowedHosts""*",  
  2. "ConnectionStrings": {  
  3.   "DefaultConnection""host=localhost;port=5432;username=<Username>;password=;database=<Database_name>");  
  4. }  
Then, in the Startup class in ConfigureServices method we add as a service to the DBContext and refer to the DefaultConnection property specified in the appsettings.json file:
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     services.AddEntityFrameworkNpgsql ()  
  4.         .AddDbContext<DBContext>(options =>  
  5.         {  
  6.             options. UseNpgsql(Configuration.GetConnectionString("DefaultConnection"));  
  7.         });  
  8. }  
In this case, 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 defined as follows,
  1. public DBContext(DbContextOptions<DBContext> options)  
  2.     : base(options) {}  
With these steps, we already have ready the connection and configurations needed to work with the PostgreSQL database in ASP.NET Core with the help of Entity Framework.
 

Services for working on data

 
Now it's up to us to define the models and create the services to handle the logic of our application. In this case, what you are looking for is to have a general list of users and the specific information of each of them.
 
Models
 
The models will allow us to represent what data we will want to work with when designing our website. In this case, the models will be as follows.
 
A. UserListModel
  1. public class UserListModel  
  2. {  
  3.     public int Id { getset; }  
  4.     public string Name { getset; }  
  5.     public string City { getset; }  
  6.     public string Country { getset; }  
  7.     public DateTime Enrollmentdate { getset; }  
  8. }  
B. UserDetailModel
  1. public class UserDetailModel  
  2. {  
  3.     public int Id { getset; }  
  4.     public string Firstname { getset; }  
  5.     public string Lastname { getset; }  
  6.     public string Username { getset; }  
  7.     public string City { getset; }  
  8.     public string Country { getset; }  
  9.     public int Postalcode { getset; }  
  10.     public string About { getset; }  
  11.     public DateTime Enrollmentdate { getset; }  
  12. }  

Services

 
Secondly, it is necessary to define the services of our application, which will have the necessary methods to access and save data about users in the database. In this case, we have a user service that will allow us to perform CRUD operations.
 
To perform operations on the data we will use LINQ - Language Integrated Query, a component of the Microsoft .NET platform that natively adds query capabilities to data to .NET languages. In other words, LINQ allows us to query collections of objects (the entities defined in the DAL) to handle information and perform operations on the database.
 
Taking these considerations into account, the initial adjustments and methods for processing the data are as follows,
 
A. Initial Settings 
  1. private readonly DBContext DbContext;  
  2.   
  3. public UserService(DBContext DbContext)  
  4. {  
  5.     this.DbContext = DbContext;  
  6. }  
B. Get all registered users
  1. public async Task<List<UserListModel>> GetAllUsersAsync()  
  2. {  
  3.   
  4.     return await DbContext.Person.Select(  
  5.         s => new UserListModel  
  6.         {  
  7.             Id = s.Id,  
  8.             Name = s.Firstname + " " + s.Lastname,  
  9.             City = s.City,  
  10.             Country = s.Country,  
  11.             Enrollmentdate = s.Enrollmentdate  
  12.         }  
  13.     ).ToListAsync();  
  14. }  
C. Get a specific user by their ID
  1. public async Task<UserDetailModel> GetUserByIdAsync(int UserId)  
  2. {  
  3.     return await DbContext.Person.Select(  
  4.             s => new UserDetailModel  
  5.             {  
  6.                 Id = s.Id,  
  7.                 Firstname = s.Firstname,  
  8.                 Lastname = s.Lastname,  
  9.                 Username = s.Username,  
  10.                 City = s.City,  
  11.                 Country = s.Country,  
  12.                 Postalcode = s.Postalcode,  
  13.                 Enrollmentdate = s.Enrollmentdate,  
  14.                 About = s.About  
  15.             })  
  16.         .FirstOrDefaultAsync(s => s.Id == UserId);  
  17. }  
D. Insert a new user
  1. public async Task InsertUserAsync(UserDetailModel User)  
  2. {  
  3.     var entity = new Person()  
  4.     {  
  5.         Firstname = User.Firstname,  
  6.         Lastname = User.Lastname,  
  7.         Username = User.Username,  
  8.         City = User.City,  
  9.         Country = User.Country,  
  10.         Postalcode = User.Postalcode,  
  11.         Enrollmentdate = User.Enrollmentdate,  
  12.         About = User.About  
  13.     };  
  14.   
  15.     DbContext.Person.Add(entity);  
  16.     await DbContext.SaveChangesAsync();  
  17. }  
E. Update a user's data
  1. public async Task UpdateUserAsync(UserDetailModel User)  
  2. {  
  3.     var entity = await DbContext.Person.FirstOrDefaultAsync(s => s.Id == User.Id);  
  4.   
  5.     entity.Firstname = User.Firstname;  
  6.     entity.Lastname = User.Lastname;  
  7.     entity.Username = User.Username;  
  8.     entity.City = User.City;  
  9.     entity.Country = User.Country;  
  10.     entity.Postalcode = User.Postalcode;  
  11.     entity.Enrollmentdate = User.Enrollmentdate;  
  12.     entity.About = User.About;  
  13.   
  14.     await DbContext.SaveChangesAsync();  
  15. }  
F. Delete a user
  1. public async Task DeleteUserAsync(int UserId)  
  2.         {  
  3.             var entity = new Person()  
  4.             {  
  5.                 Id = UserId  
  6.             };  
  7.             DbContext.Person.Attach(entity);  
  8.             DbContext.Person.Remove(entity);  
  9.             await DbContext.SaveChangesAsync();  
  10.         }  
For more details on how LINQ works, see the Microsoft documentation at here.
 
To finish with this section, this service must be referenced in the Startup class, in the ConfigureServices method (the same one where the adjustment was made with EntityFramework). Here we need to add the following statement, 
  1. services.AddTransient(typeof(UserService));  
Note
if you generate more services, all of them must be specified in this method of the Startup class.
 
In the end, the solution in Visual Studio about the Business Logic Layer section will look like this:
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 

Representation of data in a web application

 
Now that we have defined data access layers and business logic, we can now design the web page so that people can interact with it and in this case, use the CRUD operations implemented for user management.
 
In this section, we'll look at how to design a dashboard to enter data through forms and list them in tables. This process is described in the following article: Building a Dashboard with ASP.NET Core and DotVVM.
 
The end result of the design using DotVVM and the previously implemented services that access PostgreSQL is as follows,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 

Plus: Hosting the PostgreSQL database in Azure

 
Today the trend is to publish web pages in the cloud, for this, there are various services that allow us to meet these objectives, whatever database manager is being used, the database must also be in the cloud for the web page to work. For PostgreSQL, in Azure, we can find the resource: Azure Database for PostgreSQL servers.
 
To create the resource in Azure we'll need the following,
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
An Azure subscription, specify server details (name, source, location, version, and compute and storage features), and credentials to access this database as an administrator.
 
Once it has been created, we can go to the resource in the Connection Strings section and find the connection string that we will have to change in our project, in Azure we can copy the string that is in section ADO.NET and adapt it to the string that we already have set in the file appsettings.json.
 
Developing Web Applications With ASP.NET Core, DotVVM, And PostgreSQL
 
What's next?
 
Below, as shown above, you can continue with the second part of this article where you can see step by step how to generate a dashboard with DotVVM: Building a Dashboard with ASP.NET Core and DotVVM.
 
The project-entire source code for building the dashboard with ASP.NET Core and DotVVM with PostgreSQL is available in the following repository: User Dashboard.
 
As an additional resource, in this article, you can see step by step how to deploy a web app to Azure: Deploy DotVVM and .NET Core web applications to Azure (Everything you need to know).
 
Thank you so much for reading.
 
If you have any questions or ideas that you need to discuss, it will be nice to be able to collaborate and together exchange knowledge with each other.
 
See you on Twitter! :)