Create A DotVVM CRUD Application With Cloud Firestore

Introduction 

 
In the previous post, we demonstrated how to use DotVVM to create CRUD application with Azure Cosmos DB as your data store.
 
In this post, we will demonstrate how to do the same with Cloud Firestore by Google.
 
To know more about DotVVM Framework you can check its official website.
 

What is Cloud Firestore?

 
Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform. It keeps your data in-sync across client apps through realtime listeners and offers offline support for mobile and web apps.
 
To learn more about Cloud Firebase, visit here.
 

Install the DotVVM extension

 
Refer to the previous post and follow the steps.
 
To know more about the DotVVM extension visit its page on visual studio marketplace: https://marketplace.visualstudio.com/items?itemName=TomasHerceg.DotVVM-VSExtension2019
 
Now you are ready to start coding with the DotVVM framework. DotVVM extension installs the DotVVM sample project, which we will use to demonstrate how it is easy to connect to the Cloud Firestore database from a DotVVM application.
 

Create A Cloud Firestore Project

 
To use the cloud Firestore as your database, you need to create a project on Firestore.
  • Go here and log in with your Google account or create a new one.
  • Click on Add Project
  • Enter dotvvm as the project name.
  • In the next step, Disable Google Analytics for this project. It is recommended to enable it for a real project.
  • Click on Create Project. it will take one or two mins to create your project. When it is ready, click on continue.

    DotVVM CRUD Application With Cloud Firestore
  • From the left navigation panel, open Database then click on Create database

    DotVVM CRUD Application With Cloud Firestore 
  • In the popup window, select Start in test mode then click next.
  • Select the location of your database(you can keep the default selection), then Click Done.
  • Congratulations, your Cloud Firestore database is created and ready!
To be able to connect to your Firestore database from your application(server) you need to set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to the JSON service account key file.
 
To set up your service account, go here and follow the instructions.
 

DotVVM Web Application

 
In the previous post, you can find the complete steps for creating the DotVVM web application. In this post, we focus on code changes required to connect to Firestore database.
  1. Uninstall the entity framework SQL Server related NuGet package using the following commands in the Package Manager Console pane in Visual Studio, as we are going to connect to our Cloud Firestore database, not the SQL Server database.
    1. uninstall-package Microsoft.EntityFrameworkCore.SqlServer  
    2.   
    3. uninstall-package Microsoft.EntityFrameworkCore  
  2. Install Google.Cloud.Firestore nuget package. This package is required to run CRUD operations against your Firestore database from your web application.
    1. install-package Google.Cloud.Firestore  
  3. Open appsettings.json file and remove the connection string. keeping it will not harm but we need our code to be clean.
  4. Delete DAL/StudentDbContext.cs file.
  5. Open Startup.cs file and delete the following lines
    1. services.AddEntityFrameworkSqlServer()    
    2.         .AddDbContext<Studentdbcontext>(options =>    
    3.         {    
    4.             options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));    
    5.         });  
  6. Open DAL/Entities/Student.cs file and add the following using statement:
    1. using Google.Cloud.Firestore;
  7. Decorate the Student class with [FirestoreData] attribute and each property with [FirestoreProperty] attribute. Also, change the type of EnrollmentDate property to Timestamp instead of DateTime. The complete code of Student class will be like below:
    1. [FirestoreData]  
    2. public class Student  
    3. {  
    4.     [FirestoreProperty]  
    5.     public int Id { getset; }  
    6.   
    7.     [FirestoreProperty]  
    8.     public string FirstName { getset; }  
    9.   
    10.     [FirestoreProperty]  
    11.     public string LastName { getset; }  
    12.   
    13.     [FirestoreProperty]  
    14.     public string About { getset; }  
    15.   
    16.     [FirestoreProperty]  
    17.     public Timestamp EnrollmentDate { getset; }  
    18. }  
    Those attributes are required and used to serialize/deserialize your entities from and to json objects. Timestamp is the Firestore datatype used to represent date/time values.
  8. Open Services/StudentService.cs file and add the following private properties to StudentService class, where the project is the name of your Firestore project and collection is the name of your database collection where you will insert/retrieve records.
    1. private const string project = "dotvvm";  
    2. private const string collection = "students";  

  9. Replace the GetAllStudentsAsync method with the following:
    1. public async Task<List<StudentListModel>> GetAllStudentsAsync()  
    2. {  
    3.    var studentsList = new List<Student>();  
    4.    FirestoreDb db = FirestoreDb.Create(Project);  
    5.    Query allStudentsQuery = db.Collection(Collection);  
    6.    QuerySnapshot allStudentsQuerySnapshot = await allStudentsQuery.GetSnapshotAsync();  
    7.    foreach (DocumentSnapshot documentSnapshot in allStudentsQuerySnapshot.Documents)  
    8.    {  
    9.       studentsList.Add(documentSnapshot.ConvertTo<Student>());  
    10.    }  
    11.    return studentsList.Select(s => new StudentListModel  
    12.                                     {  
    13.                                        Id = s.Id,  
    14.                                        FirstName = s.FirstName,  
    15.                                        LastName = s.LastName  
    16.                                     }  
    17.                               ).ToList();  
    18. }  
  10. Replace the GetStudentByIdAsync method with the following:
    1. public async Task<StudentListModel> GetStudentByIdAsync()    
    2. {    
    3.    FirestoreDb db = FirestoreDb.Create(Project);    
    4.    Query docRef = db.Collection(Collection).WhereEqualTo("Id", studentId).Limit(1);    
    5.    QuerySnapshot snapshot = await docRef.GetSnapshotAsync();    
    6.    if (snapshot.Count > 0)    
    7.    {    
    8.       Student student = snapshot.ElementAt(0).ConvertTo<Student>();    
    9.       return new StudentDetailModel()    
    10.                      {    
    11.                           About = student.About,    
    12.                           EnrollmentDate = student.EnrollmentDate.ToDateTime(),    
    13.                           FirstName = student.FirstName,    
    14.                           Id = student.Id,    
    15.                           LastName = student.LastName    
    16.                      };    
    17.    }    
    18.    else       
    19.    {    
    20.       return null;    
    21.    }    
    22. }    
  11. Replace the UpdateStudentAsync method with the following:
    1. public async Task UpdateStudentAsync(StudentDetailModel student)  
    2. {  
    3.    FirestoreDb db = FirestoreDb.Create(Project);  
    4.    Query docRef = db.Collection(Collection).WhereEqualTo("Id", student.Id).Limit(1);  
    5.    QuerySnapshot snapshot = await docRef.GetSnapshotAsync();  
    6.    if (snapshot.Count > 0)  
    7.    {  
    8.       DocumentReference studentRef = db.Collection(Collection).Document(snapshot.ElementAt(0).Id);  
    9.       Dictionary<stringobject> updates = new Dictionary<stringobject>  
    10.       {  
    11.          { nameof(student.About), student.About},  
    12.          { nameof(student.EnrollmentDate), Timestamp.FromDateTime(student.EnrollmentDate.ToUniversalTime())},  
    13.          { nameof(student.FirstName), student.FirstName},  
    14.          { nameof(student.LastName), student.LastName}  
    15.       };  
    16.    await studentRef.UpdateAsync(updates);  
    17.    }  
  12. Replace the InsertStudentAsync method with the following:
    1. public async Task InsertStudentAsync(StudentDetailModel student)  
    2. {  
    3.    var entity = new Student()  
    4.    {  
    5.       Id = new Random().Next(1, int.MaxValue),  
    6.       FirstName = student.FirstName,  
    7.       LastName = student.LastName,  
    8.       About = student.About,  
    9.       //create Timestamp from DateTime value  
    10.       EnrollmentDate = Timestamp.FromDateTime(student.EnrollmentDate.ToUniversalTime())  
    11.    };  
    12.    FirestoreDb db = FirestoreDb.Create(Project);  
    13.    var Id = Guid.NewGuid().ToString();  
    14.    DocumentReference docRef = db.Collection(Collection).Document(Id);  
    15.    await docRef.SetAsync(entity);  
    16. }  
  13. Replace the DeleteStudentAsync method with the following
    1. public async Task DeleteStudentAsync(int studentId)    
    2. {    
    3.    FirestoreDb db = FirestoreDb.Create(Project);    
    4.    Query docRef = db.Collection(Collection).WhereEqualTo("Id", studentId).Limit(1);    
    5.    QuerySnapshot snapshot = await docRef.GetSnapshotAsync();    
    6.    if (snapshot.Count > 0)    
    7.    {    
    8.       DocumentReference studentRef = db.Collection(Collection).Document(snapshot.ElementAt(0).Id);    
    9.       await studentRef.DeleteAsync();    
    10.    }    
    11. }    
  14. Run your application. If there is no error, you will see the following page in your browser:

    DotVVM CRUD Application With Cloud Firestore

  15. Click on New Item, fill the form, then click on Add

    DotVVM CRUD Application With Cloud Firestore

  16. Congratulations, you added your student to students Firestore collection!

    DotVVM CRUD Application With Cloud Firestore

  17. Go to your Firestore database, it should look like the following:

    DotVVM CRUD Application With Cloud Firestore

Notice that Firestore is created a student collection and is inserted your student entity as a JSON document. Also, notice how it represents the EnrollmentDate value as a timestamp.
 

Summary

 
In this article, we demonstrated how to create a sample DotVVM web application, Cloud Firestore database, and perform CRUD operations as we used to do with the SQL Server database or any other relational database. We have done changes in the sample project code to twist it from using Entity Framework Code First approach (SQL Server) to use Google Cloud FireStore .NET SDK.
 
You can find the complete source code on GitHub