DotVVM And ASP.NET Core - Implementing CRUD Operations

Introduction 

 
DotVVM is an ASP.NET framework that allows us to create web applications using the MVVM (Model-View-Viewmodel) design pattern with C# and HTML. In this tutorial, we will learn how to create CRUD operations (Create, Read, Update, and Delete) using the DotVVM and .NET Core framework.
 
Want to know the steps to create a DotVVM app? To learn about this, you can review this article - Steps to create an MVVM application (Model-View-ViewModel) with DotVVM and ASP.NET Core.
 
For our case study, as an example we will take an entity of the information of a student for the realization of CRUD operations and divide the application into three parts:
  1. Data Access Layer Implementation: to manage connection and database access.
  2. Implementation of the BL (Business Layer): for the management of services and logic of the application domain.
  3. Implementation of the application presentation layer. This section is where DotVVM comes into action.

Part 1 - Data Access Layer - DAL

 
To create our application, first in the Data Access Layer, we must define the entities that the application domain will have and the DBContext at which the reference for the connection to our database will be defined.
 
DotVVM And ASP.NET Core - Implementing CRUD Operations
 
In the application, we will use the entity Student. If we had other entities, for example, Professor, Subject, etc; these would be located in the Entities folder. For our case study, the Student entity will be defined as follows:
  1. public class Student {  
  2.     public int Id {  
  3.         get;  
  4.         set;  
  5.     }  
  6.     public string FirstName {  
  7.         get;  
  8.         set;  
  9.     }  
  10.     public string LastName {  
  11.         get;  
  12.         set;  
  13.     }  
  14.     public string About {  
  15.         get;  
  16.         set;  
  17.     }  
  18.     public DateTime EnrollmentDate {  
  19.         get;  
  20.         set;  
  21.     }  
  22. }   
On the other hand, we have the DBContext, the primary class with which our application will interact (Operations in business logic) and that allows communication with the database:
  1. public class StudentDbContext: DbContext {  
  2.     public StudentDbContext(DbContextOptions options): base(options) {  
  3.         Database.EnsureCreated();  
  4.     }  
  5.     public DbSet < Student > Students {  
  6.         get;  
  7.         set;  
  8.     }  
  9. }  
In the StudentDbContext there are two important things to say:
  • context.Database.EnsureCreated() is a primary Entity Framework - EF method that ensures that the database exists for the context. If it exists, no action is taken. If it does not exist, the database and all its schemas are created and are also guaranteed to support the model for this context.
  • DbSet<Student> Students {get; set;}; represents a collection for the Student entity within the data model and is the gateway to database operations with this entity.
Typically this section of the DAL (entities such as Student and associations in the DbContext) is automatically generated based on entities already defined in the database through Entity Framework.
 

Part 2 - Business Layer - BL

 
Now we need to define the models and create the services to control the logic of our application. In this case, what we are looking for is to have a general list of students and the specific information of each of them.
 
Models
 
As a first point, we will define our models.
 
StudentListModel
  1. public class StudentListModel {  
  2.     public int Id {  
  3.         get;  
  4.         set;  
  5.     }  
  6.     public string FirstName {  
  7.         get;  
  8.         set;  
  9.     }  
  10.     public string LastName {  
  11.         get;  
  12.         set;  
  13.     }  
  14. }  
StudentDetailModel
  1. public class StudentDetailModel {  
  2.     public int Id {  
  3.         get;  
  4.         set;  
  5.     } [Required] public string FirstName {  
  6.         get;  
  7.         set;  
  8.     } [Required] public string LastName {  
  9.         get;  
  10.         set;  
  11.     } [Required] public DateTime EnrollmentDate {  
  12.         get;  
  13.         set;  
  14.     }  
  15.     public string About {  
  16.         get;  
  17.         set;  
  18.     }  
  19. }  
Then we implement the services of our application.
 
Services
 
In this case, we have the Student service that will allow us to carry out CRUD operations.
  1. public class StudentService {  
  2.     private readonly StudentDbContext studentDbContext;  
  3.     public StudentService(StudentDbContext studentDbContext) {  
  4.         this.studentDbContext = studentDbContext;  
  5.     }  
  6.     public async Task < List < StudentListModel >> GetAllStudentsAsync()...public async Task < StudentDetailModel > GetStudentByIdAsync(int studentId)...public async Task UpdateStudentAsync(StudentDetailModel student)...public async Task InsertStudentAsync(StudentDetailModel student)...public async Task DeleteStudentAsync(int studentId)...  
  7. }  
For managing information stored in the database, we are using LINQ - Language Integrated Query, a component of the Microsoft .NET platform that adds data query capabilities natively to the .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.
 
To understand a little more about how LINQ works, in the following method we can see the procedure that allows us to obtain the information of a particular student according to their identification:
  1. public async Task < StudentDetailModel > GetStudentByIdAsync(int studentId) {  
  2.     return await studentDbContext.Students.Select(s => new StudentDetailModel {  
  3.         Id = s.Id, FirstName = s.FirstName, LastName = s.LastName, About = s.About, EnrollmentDate = s.EnrollmentDate  
  4.     }).FirstOrDefaultAsync(s => s.Id == studentId);  
  5. }  
For more details on how LINQ works, you can refer to the Microsoft documentation here.
 

Part 3 - Application Presentation Layer

 
Now that we have defined the DAL and BL, we must carry out the design of the website so that the user can interact with it and in this case, perform CRUD operations for the management of Students.
 
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 how the page will look.
  • A view model, which is a class in C# that describes the state of the page (for example, values in the form fields) and controls user interactions (for example, button clicks).
For us, we will have four Views and four Models associated with these Views (Viewmodels):
  • Default: will be the main page of the application where the list of registered students will be displayed.
  • Create: a page made up of a form to create new students.
  • Detail: to view a student's information in detail.
  • Edit: to modify a student's information or delete it.
Considering the Views and Viewmodels files, in Visual Studio we will see something like this:
 
DotVVM And ASP.NET Core - Implementing CRUD Operations
 
Next, let's analyze in more detail the View and Viewmodel of Default and its components.
 
Default Viewmodel
  1. public class DefaultViewModel: MasterPageViewModel {  
  2.     private readonly StudentService studentService;  
  3.     public DefaultViewModel(StudentService studentService) {  
  4.         this.studentService = studentService;  
  5.     } [Bind(Direction.ServerToClient)] public List < StudentListModel > Students {  
  6.         get;  
  7.         set;  
  8.     }  
  9.     public override async Task PreRender() {  
  10.         Students = await studentService.GetAllStudentsAsync();  
  11.         await base.PreRender();  
  12.     }  
  13. }  
First, we have the StudentService instance that will allow us to access the methods to handle the operations defined in the Student service implemented in the BL.
 
Then we have the definition List<StudentListModel> Students of type StudentListModel defined in the model classes in the BL, which will have the list of students (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 [Bind(Direction.ServerToClient)] declaration. These types of properties allow you to specify which information is 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 student list, in many cases, it is not necessary to transfer the entire view model in both directions. From server to view will be fine in this case.
 
Learn more about Binding Directions here.
 
Finally, in the Default ViewModel, we have the PreRender() method, which allows you to perform certain types of operations that will be performed when loading the View. In this case, a query will be made to the database by calling the service method studentService.GetAllStudentsAsync(), then the results will be assigned in the Students collection of type StudentListModel and then the page will be loaded along with the other design components.
 
Default View
  1. <dot:Content ContentPlaceHolderID="MainContent">  
  2.     <div class="page-center">  
  3.         <div class="page-grid-top">  
  4.             <div class="student-image"></div>  
  5.             <h1>Students</h1>  
  6.             <dot:RouteLink Text="{resource: Texts.Label_NewStudent}" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>  
  7.         </div>  
  8.         <dot:GridView DataSource="{value: Students}" class="page-grid">  
  9.             <Columns>  
  10.                 <dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="{resource: Texts.Label_Firstname}" />  
  11.                 <dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="{resource: Texts.Label_Lastname}" />  
  12.                 <dot:GridViewTemplateColumn>  
  13.                     <dot:RouteLink Text="{resource: Texts.Label_Detail}" RouteName="CRUD_Detail" Param-Id="{{value: Id}}" />  
  14.                 </dot:GridViewTemplateColumn>  
  15.                 <dot:GridViewTemplateColumn>  
  16.                     <dot:RouteLink Text="{resource: Texts.Label_Edit}" RouteName="CRUD_Edit" Param-Id="{{value: Id}}" />  
  17.                 </dot:GridViewTemplateColumn>  
  18.             </Columns>  
  19.             <EmptyDataTemplate>                 There are no registered students. First sign in or sign up and add some students.             </EmptyDataTemplate>  
  20.         </dot:GridView>  
  21.     </div>  
  22. </dot:Content>  
As we can see, in the View of Default, the layout of the page happens with 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 <table> tag. One of its attributes is DataSource: DataSource="{value: Students}", which allows you to specify the data source, in this case, we refer to the list of students: Students, which was defined in the Viewmodel, as we saw earlier.
 
In addition to grids, DotVVM also has other custom control components, for example, in TextBox, ComboBox, and file handling among others, which allow us to maintain communication between the View and the sources of information defined in the Viewmodels. See more here.
 
Continuing our analysis, in the GridView, we have the columns FirstName and LastName of the students, but additionally, we can also add columns to perform operations on a 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, such as viewing detail or modifying the record of a student in particular based on their ID:
  1. <dot:RouteLink RouteName="Edit" Param-Id="{{value: Id}}" />  
These paths and their corresponding parameters are to be defined in the file DotvvmStartup.cs in the ConfigureRoutes method as follows:
  1. config.RouteTable.Add("Edit""edit/{Id}""Views/Edit.dothtml");  
To learn more about Routing on DotVVM you can go here.
 
The Create, View Detail and Modify pages following the same logic for the View and Viewmodel components. When you add some student records in the app and load the homepage with the list of them, we will have something like this:
DotVVM And ASP.NET Core - Implementing CRUD Operations
 
What's next?
 
With these steps, we have analyzed the most important parts of an MVVM web application using DotVVM y.NET Core with the help of Visual Studio 2019 to implement a CRUD about students' information.
 
The code in this example can be found on the sample page that Visual Studio 2019 has for DotVVM when generating a new project of type: DotVVM with ASP.NET Core and selecting the option: Sample CRUD Page.
 
You can also find the code in this repository on GitHub here.
 
Thank you!
 
In the following articles, we will continue to review the components that DotVVM has for the design of web pages and case studies that may be useful to us in the future.
 
To be aware of this and other news, you can follow me on Twitter: twitter.com/esDanielGomez
 
See you soon!