Developing web applications with ASP.NET Core, DotVVM and MongoDB
Tags: webdev, dotnet, mongodb, dotvvm
Published: 2020-04-02 16:13:25

Today you've probably heard about NoSQL databases. It's something that's been very fashionable lately in the development world.

At present we are used to SQL databases like MySQL, Oracle or SQL Server, but for some time have appeared others that are called NoSQL (Not only SQL) and that have come with the intention of dealing with the bases related used by most users.

In short, NoSQL databases differ in several aspects of lifelong relational databases such as: they can handle a large amount of data, no fixed structures (tables, columns, etc.) are needed for data storage, they do not usually allow JOIN operations, among other aspects.

There are several types of NoSQL databases that we could group together different types: documentaries, graphs, key/value, multivalue, object-oriented, or tabular.

Taking advantage of the growing popularity of the .NET ecosystem, in our case, we will learn how to make web applications in ASP.NET Core with the MVVM design pattern (Model-View-Viewmodel) thanks to DotVVM and through document databases with MongoDB.

####Resources and tools needed:

+ Visual Studio 2019: [http://visualstudio.microsoft.com/en/downloads/](http://visualstudio.microsoft.com/en/downloads/).
+ The workload in Visual Studio 2019: ASP.NET and web development.
![](https://thepracticaldev.s3.amazonaws.com/i/xqyw33v95om0wozvckh3.jpg)

+ DotVVM extension for Visual Studio 2019: [www.dotvvm.com/landing/dotvvm-for-visual-studio-extension](www.dotvvm.com/landing/dotvvm-for-visual-studio-extension).
+ MongoDB: [https://www.mongodb.com/download-center/community](https://www.mongodb.com/download-center/community).

##The solution environment##

For our case study, we will take as an example document the information of a student to develop CRUD operations and manage the application in three parts:

1. *Data Access Layer Implementation*: to manage connection and access to the document database in MongoDB. 
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 work with MongoDB you need to install the following NuGet package:

`MongoDB.Driver`

As the first point to create our application, in the 'Data Access Layer' we must define the collections that the application domain will have and define a configuration module to reference the properties of the database (connection string, name of the database, and database collections).

![](https://dev-to-uploads.s3.amazonaws.com/i/jt0gtzkqcwpgo8cpgz0s.png)

In the application we will handle the collection: `Student`. If we had other collections, for example: `Professor`, `Subject`, etc; these will be located in the `Collections` folder. For our case study, the `Student` collection will be defined as follows:

```csharp
public class Student
{
    [BsonId]
    [BsonRepresentation(BsonType.Int32)]
    [BsonElement("_id")]
    public int Id { get; set; }

    [BsonElement("FirstName")]
    public string FirstName { get; set; }

    [BsonElement("LastName")]
    public string LastName { get; set; }

    [BsonElement("About")]
    public string About { get; set; }

    [BsonElement("EnrollmentDate")]
    [BsonRepresentation(BsonType.DateTime)]
    public DateTime EnrollmentDate { get; set; }
}
```

BSON format is used for storage and data transfer in MongoDB, which allows us to work with documents in MongoDB. In this case, in the `Student` class we can specify the name of the attribute of the collection we refer to (usually used when the names are different), the BSON data type, and other directives. For example, in MongoDB, the primary key of a collection is identified by the attribute: `_id`, which will allow us to identify a document, in this case, the `Id` of the student will be the primary identifier. The BSON data types can be found in the MongoDB documentation: [https://docs.mongodb.com/manual/reference/bson-types/](https://docs.mongodb.com/manual/reference/bson-types/).

On the other hand we have the interface and its implementation: `DatabaseSettings` for handling the specific properties of the database with MongoDB:

```csharp
public class DatabaseSettings : IDatabaseSettings
{
    public string CollectionName { get; set; }
    public string ConnectionString { get; set; }
    public string DatabaseName { get; set; }
}

public interface IDatabaseSettings
{
    string CollectionName { get; set; }
    string ConnectionString { get; set; }
    string DatabaseName { get; set; }
}
```

The values of each of the attributes in `DatabaseSettings` will be defined in the `appsettings.json` file. The names of the JSON and C-properties are the same to make the mapping process easier.

#####appsettings.json file#####

```csharp
{
  "DatabaseSettings": {
    "CollectionName": "Student",
    "ConnectionString": "mongodb://...",
    "DatabaseName": "StudentDb"
  }
}
```

To establish the relation between the `DatabaseSettings` class and the `appsettings.json` configuration file, we need to define this setting in the constructor and in the `ConfigureServices` method of the `Startup.cs` class as shown below:

```csharp
public IConfiguration Configuration { get; private set; }

public Startup(IHostingEnvironment env)
{
    // Set up configuration sources.
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json");

    builder.AddEnvironmentVariables();
    Configuration = builder.Build();
}
```


```csharp
Public void ConfigureServices(IServiceCollection services)
{
 // requires using Microsoft.Extensions.Options
 services.Configure<BookstoreDatabaseSettings>( Configuration.GetSection(nameof(DatabaseSettings)));
}
```
There are two important aspects to say this code:

+ The configuration instance to which the `DatabaseSettings` section of the `appsettings.json` file links is registered in the dependency injection container. For example, a `ConnectionString` property of the `DatabaseSettings` object is populated with the `DatabaseSettings:ConnectionString` property in `appsettings.json`.

+ The `DatabaseSettings` interface is registered in dependency injection singleton service life (design pattern). When inserted, the interface instance resolves to a `DatabaseSettings` object.

##Part 2: Business Layer - BL##

Now we need to define the models and create the services to handle 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####

To do this, as a first point we will define our models:

**StudentList**:

```csharp
public class StudentListModel
{
    public int Id {get; set;}
    public string FirstName {get; set;}
    public string LastName {get; set;}
}
```

**StudentDetailModel**:

```csharp
public class StudentDetailModel
{
    public int Id { get; set; }
    
    [Required]
    public string FirstName { get; set; }
    
    [Required]
    public string LastName { get; set; }
    
    [Required]
    public DateTime EnrollmentDate {get; set;}

    public string About { get; set; }
}
```
And then the services of our application.

####Services####

In this case, we have the 'Students' service that will allow us to implement CRUD operations: Create, Read, Update and Delete documents in MongoDB. Next, let's look at each of them.

#####A. Service initialization#####

```csharp
private readonly IMongoCollection<Student> _students;

public StudentService(IDatabaseSettings settings)
{
    var client = new MongoClient(settings.ConnectionString);
    var database = client.GetDatabase(settings.DatabaseName);

    _students = database.GetCollection<Student>(settings.CollectionName);
}
```

In short, in this part the work done is to establish the connection with MongoDB, selecting the database with which we will work and the corresponding collection.

#####B. Read documents#####

```csharp
public List<StudentListModel> GetAllStudents()
{
    return _students.Find(Student => true).ToList().Select(
        s => new StudentListModel
        {
            Id = s.Id,
            FirstName = s.FirstName,
            LastName = s.LastName
        }
    ).ToList();
}
```

The first option we have is to get all the documents from the collection we are working with. Through the `_students` attribute that refers to the collection, we can use the [methods provided by MongoDB](https://docs.mongodb.com/manual/reference/method/js-collection/) to perform CRUD, query, and other operations.

In this case, the `Find()` method is used to query documents in a collection. With the result of this query, we can use *LINQ (Language Integrated Query)*, a component in which we can query through objects (very similar to SQL). *LINQ* will help us to set a list of objects of type `StudentListModel` with the query result documents in MongoDB.

We can also query with conditionals, for example, the following query:

```csharp
public async Task<StudentDetailModel> GetStudentByIdAsync(int studentId)
{
    Student document = await _students.FindAsync<Student>(Student => Student.Id == studentId).Result.FirstOrDefaultAsync();

    StudentDetailModel student = new StudentDetailModel();
    student.Id = document.Id;
    student.FirstName = document.FirstName;
    student.LastName = document.LastName;
    student.About = document.About;
    student.EnrollmentDate = document.EnrollmentDate;

    return student;
}
```

#####C. Create documents#####

For the creation of documents in a collection we have the methods: `InsertOne`, for inserting a document; and `InsertMany`, for the insertion of several of them. For this case, the method that we have intended for this purpose `InsertStudentAsync` receives a model `StudentDetailModel` with the data to be inserted, the process consists in constructing a `Student` class (the one that references the database) and inserting it then through the reference `_students`.

```csharp
public async Task InsertStudentAsync(StudentDetailModel student)
{
    var document = new Student()
    {
        Id = student.Id,
        FirstName = student.FirstName,
        LastName = student.LastName,
        About = student.About,
        EnrollmentDate = student.EnrollmentDate
    };
    await _students.InsertOneAsync(document);
}
```

#####D. Update documents#####

The same logic of the creation and insert methods is followed for document update. The reference document will be updated according to the id of the reference document.

```csharp
public async Task UpdateStudentAsync(StudentDetailModel student)
{
    var document = new Student()
    {
        Id = student.Id,
        FirstName = student.FirstName,
        LastName = student.LastName,
        About = student.About,
        EnrollmentDate = student.EnrollmentDate
    };

    await _students.ReplaceOneAsync(Student => Student.Id == student.Id, document);
}
```

#####E. Delete documents#####

To delete a document, the `DeleteOne` method is used, which allows you to delete a document according to the document ID.

```csharp
public async Task DeleteStudentAsync(int IdStudent)
{
    await _students.DeleteOneAsync(Student => Student.Id == IdStudent);
}
```

So far we have defined the services of our application domain model. What remains is to use these services from an application, which will be designed in ASP.NET Core with DotVVM.

##Part 3: Application Presentation Layer##

Now that we have defined the `DAL` and the `BL`, we must now perform 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.
 
Before proceeding, it is necessary to associate with our application (register) the services that will be used. For this, we should look at the `ConfigureServices` method in the `Startup` class and have something like this:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection();
    services.AddAuthorization();
    services.AddWebEncoders();
    services.AddTransient(typeof(StudentService));
    services.Configure<DatabaseSettings>(Configuration.GetSection(nameof(DatabaseSettings)));
    services.AddSingleton<IDatabaseSettings>(sp => sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
    services.AddDotVVM<DotvvmStartup>();
}
```

Now, 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 CTM that describes the state of the page (for example, values in the form fields) and handles user interactions (for example, button clicks).

In our application 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 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'll see something like this: 

![](https://dev-to-uploads.s3.amazonaws.com/i/h0ty16xm597t86tyc6ma.png)
 
Next, let's take a closer look at the View and Viewmodel of `Default` and its components.

####Default viewmodel####

```csharp
public class DefaultViewModel : MasterPageViewModel
    {
        private readonly StudentService studentService;

		public DefaultViewModel(StudentService studentService)
        {
            this.studentService = studentService;
        }

        [Bind(Direction.ServerToClient)]
        public List<StudentListModel> Students { get; set; }

        public override async Task PreRender()
        {
            Students =  await studentService.GetAllStudentsAsync();
            await base.PreRender();
        }
    }
```

As a first point, 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 the server to the eye will suffice in this case.

Learn more about Binding Directions here: [https://www.dotvvm.com/docs/tutorials/basics-binding-direction/2.0](https://www.dotvvm.com/docs/tutorials/basics-binding-direction/2.0).

Finally, in the `Viewmodel` of `Default` 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####

```html
<dot:Content ContentPlaceHolderID="MainContent">
	<div class="page-center">
		<div class="page-grid-top">
		<div class="student-image"></div>
			<h1>Student List</h1>
			<dot:RouteLink Text="New Student" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>
		</div>
		<dot:GridView DataSource="{value: Students}" class="page-grid">
			<Columns>
				<dot:GridViewTextColumn ValueBinding="{value: Id}" HeaderText="Id" />
				<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 students. First sign in or sign up and add some students.
            </EmptyDataTemplate>
		</dot:GridView>
	</div>
</dot:Content>
```

As we can see the Default View, the layout of the page 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 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, for text boxes, `ComboBox`, file handling, among others that allow us to maintain communication between the View and the sources of information defined in `Viewmodels`. See more here: [https://www.dotvvm.com/docs/controls/](https://www.dotvvm.com/docs/controls/).

Continuing our analysis, in the GridView we have the columns `Id`, `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, for example, view detail or modify the record of a student in particular according to their `Id`:

```html
<dot:RouteLink RouteName="Edit" Param-Id="{{value: Id}}" />
```

These paths and their corresponding parameters must be defined in the file `DotvvmStartup.cs` in the `ConfigureRoutes` method as follows:

```csharp
config.RouteTable.Add("Edit", "edit/{Id}", "Views/Edit.dothtml");
```

To learn more about routing in DotVVM you can go here: [https://www.dotvvm.com/docs/tutorials/basics-routing/2.0](https://www.dotvvm.com/docs/tutorials/basics-routing/2.0).

The Create, View Detail, and Modify pages follow the same logic for the `View` and `Viewmodel` components. When you add some student records in our app and load the homepage with the list of them, we'll have something like this:

![](https://dev-to-uploads.s3.amazonaws.com/i/ktrxa521fk0stdg45fo9.png) 

##Plus: Resources for hosting MongoDB data and NoSQL databases in the cloud##

Today the trend is in publishing web pages in the cloud, for this, there are several services that allow us to meet these objectives, whatever database manager is being used, whether SQL or NoSQL. For NoSQL databases we have these recommended options:

####A. mLab####

mLab is a free managed cloud database service that hosts MongoDB databases. It has the same local MongoDB operation, the only thing that changes is the connection string. To learn more about mLab you can go to: [https://mlab.com/](https://mlab.com/).

####B. Azure CosmosDB - The API for MongoDB####

Azure Cosmos DB is a multi-model, global Microsoft distribution database service. For this case, Azure provides a Cosmos DB API for MongoDB, which, in addition to working with all the capabilities of MongoDB, we can also use the full power of Azure for handling NoSQL databases, for example: global distribution, automatic partitioning, availability, and latency guarantees, encryption at rest, backups, among others. To learn more about the Cosmos DB API for MongoDB we can visit this Microsoft document: [https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-introduction](https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-introduction).

##What's next?##

With this tutorial, we learned how to work with MongoDB in applications with ASP.NET Core and DotVVM through Visual Studio 2019 to implement CRUD operations on a collection of Students. 

The code in this tutorial can be found in the following repository on GitHub: [DotVVM + MongoDB](https://github.com/esdanielgomez/dotnetMongoDB).

Thank you!
See you on [Twitter](https://twitter.com/enDanielGomez)!! :)