DotVVM CRUD application with Cloud Firestore

In 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 listener and offers offline support for mobile and web apps.

You can know more about Cloud Firebase here.

Install DotVVM extension

refer to 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 DotVVM framework. DotVVM extension installs DotVVM sample project which we will use to demonstrate how it is easy to connect to Cloud Firestore database from DotVVM application.

Create Cloud Firestore Project

To use cloud Firestore as your database you need to create a project on Firestore.
  1. Go to https://console.firebase.google.com/ and login with your google account or create new one.
  2. Click on Add Project
  3. Enter "dotvvm" as project name.
  4. In next step, Disable Google Analytics for this project. It is recommended to enable it for real project.
  5. Click on Create Project. it will take one or two mins to create your project. When it is ready click on continue.
  6. From the left navigation pan, open database then click on Create database
  7. In the popup window, select Start in test mode then click next.
  8. Select the location of your database(you can keep the default selection) then Click Done.
  9. Congratulation, 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 JSON service account key file.

To setup your service account, go to https://cloud.google.com/docs/authentication/getting-started and follow the instructions.

DotVVM Web Application

In previous post you can find the complete steps for creating DotVVM web application. In this post we focus on code changes required to connect to Firestore database.
  • Uninstall entity framework Sql Server related nuget package
    Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore
    As we will connect to Cloud Firestore database not Sql Server database
  • Install Google.Cloud.Firestore nuget package. This package is required to run CRUD operations against your firestore database from your web application.
  • Open appsettings.json file and remove the connection string. keeping it will not harm but we need our code to be clean.
  • Delete DAL/StudentDbContext.cs file.
  • Open Startup.cs file and delete the following lines
    services.AddEntityFrameworkSqlServer()
                    .AddDbContext<Studentdbcontext>(options =>
                    {
                        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
                    });
    
  • Open DAL/Entities/Student.cs file and add the following using statement
    using Google.Cloud.Firestore;
  • 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
        [FirestoreData]
        public class Student
        {
            [FirestoreProperty]
            public int Id { get; set; }
    
            [FirestoreProperty]
            public string FirstName { get; set; }
    
            [FirestoreProperty]
            public string LastName { get; set; }
    
            [FirestoreProperty]
            public string About { get; set; }
    
            [FirestoreProperty]
            public Timestamp EnrollmentDate { get; set; }
        }
    
    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.
  • Open Services/StudentService.cs file and add the following private properties to StudentService class
    private const string project = "dotvvm";
    private const string collection = "students";
    
    where project is the name of your Firestore project and collection is the name of your database collection where you will insert/retrieve records
  • Replace the GetAllStudentsAsync method with the following
    public async Task<List<StudentListModel>> GetAllStudentsAsync()
    {
         var studentsList = new List<Student>();
         FirestoreDb db = FirestoreDb.Create(Project);
         Query allStudentsQuery = db.Collection(Collection);
    
         QuerySnapshot allStudentsQuerySnapshot = await allStudentsQuery.GetSnapshotAsync();
         foreach (DocumentSnapshot documentSnapshot in allStudentsQuerySnapshot.Documents)
         {
             studentsList.Add(documentSnapshot.ConvertTo<Student>());
         }
    
         return studentsList.Select(
                s => new StudentListModel
                    {
                        Id = s.Id,
                        FirstName = s.FirstName,
                        LastName = s.LastName
                    }
                ).ToList();
    }
    
  • Replace the GetStudentByIdAsync method with the following
    public async Task<StudentListModel> GetStudentByIdAsync()
    {
         FirestoreDb db = FirestoreDb.Create(Project);
         Query docRef = db.Collection(Collection).WhereEqualTo("Id", studentId).Limit(1);
         QuerySnapshot snapshot = await docRef.GetSnapshotAsync();
         if (snapshot.Count > 0)
         {
              Student student = snapshot.ElementAt(0).ConvertTo<Student>();
              return new StudentDetailModel()
              {
                  About = student.About,
                  EnrollmentDate = student.EnrollmentDate.ToDateTime(),
                  FirstName = student.FirstName,
                  Id = student.Id,
                  LastName = student.LastName
              };
          }
          else
          {
               return null;
          }
    }
  • Replace the UpdateStudentAsync method with the following
    public async Task UpdateStudentAsync(StudentDetailModel student)
    {
         FirestoreDb db = FirestoreDb.Create(Project);
         Query docRef = db.Collection(Collection).WhereEqualTo("Id", student.Id).Limit(1);
         QuerySnapshot snapshot = await docRef.GetSnapshotAsync();
         if (snapshot.Count > 0)
         {
               DocumentReference studentRef = db.Collection(Collection).Document(snapshot.ElementAt(0).Id);
               Dictionary<string, object> updates = new Dictionary<string, object>
               {
                   { nameof(student.About), student.About},
                   { nameof(student.EnrollmentDate), Timestamp.FromDateTime(student.EnrollmentDate.ToUniversalTime())},
                   { nameof(student.FirstName), student.FirstName},
                   { nameof(student.LastName), student.LastName}
               };
               await studentRef.UpdateAsync(updates);
         }
    }
  • Replace the InsertStudentAsync method with the following
    public async Task InsertStudentAsync(StudentDetailModel student)
    {
         var entity = new Student()
         {
              Id = new Random().Next(1, int.MaxValue),
              FirstName = student.FirstName,
              LastName = student.LastName,
              About = student.About,
              //create Timestamp from DateTime value
              EnrollmentDate = Timestamp.FromDateTime(student.EnrollmentDate.ToUniversalTime())
         };
    
         FirestoreDb db = FirestoreDb.Create(Project);
         var Id = Guid.NewGuid().ToString();
         DocumentReference docRef = db.Collection(Collection).Document(Id);
         await docRef.SetAsync(entity);
    }
  • Replace the DeleteStudentAsync method with the following
    public async Task DeleteStudentAsync(int studentId)
    {
         FirestoreDb db = FirestoreDb.Create(Project);
         Query docRef = db.Collection(Collection).WhereEqualTo("Id", studentId).Limit(1);
         QuerySnapshot snapshot = await docRef.GetSnapshotAsync();
         if (snapshot.Count > 0)
         {
             DocumentReference studentRef = db.Collection(Collection).Document(snapshot.ElementAt(0).Id);
             await studentRef.DeleteAsync();
         }
    }
  • Run your application and if there is no error you will see the following page in your browser
  • Click on New Item and fill the form then click Add

  • Congratulation, you added your student to students Firestore collection

  • Go to your Firestore database, it should look like

    notice that, Firestore is created a student collection and inserted your student entity as JSON document and also notice how it represents the EnrollmentDate value as 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 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

Comments

Popular posts from this blog

ASP.Net MVC : ActionLink with bootstrap icon

Android : How to change progress bar color at runtime programmatically?

ASP.Net MVC : Conditional Validation using ValidationAttribute