Desarrollo de aplicaciones web con ASP.NET Core, DotVVM y MongoDB
Tags: webdev, dotnet, mongodb, dotvvm
Published: 2020-04-08 02:55:12

Hoy en día seguramente hayas oído hablar de las bases de datos NoSQL. Es algo que últimamente está muy de moda en el mundo del desarrollo. 

En la actualidad estamos acostumbrados a utilizar bases de datos SQL como MySQL, Oracle o SQL Server, pero desde hace ya algún tiempo han aparecido otras que reciben el nombre de NoSQL (Not only SQL – No sólo SQL) y que han llegado con la intención de hacer frente a las bases relacionales utilizadas por la mayoría de los usuarios.

Resumiendo, las bases de datos NoSQL, difieren en varios aspectos de las bases de datos relacionales de toda la vida como son: pueden manejar gran cantidad de datos, no se necesitan estructuras fijas (tablas, columnas etc.) para el almacenamiento de datos, no suelen permitir operaciones JOIN, entre otros aspectos.
 
Existen varios tipos de bases de datos NoSQL que podríamos agrupar distintos tipos: documentales, de grafo, de clave/valor, multivalor, orientadas a objetos o tabulares. 

Aprovechando el aumento de la popularidad del ecosistema de .NET, en nuestro caso aprenderemos como realizar aplicaciones web en ASP.NET Core con el patrón de diseño MVVM (Modelo Vista Vista-Modelo) gracias a DotVVM y a través bases de datos documentales con MongoDB. 

####Recursos y herramientas necesarias:

+ Visual Studio 2019: [http://visualstudio.microsoft.com/es/downloads/](http://visualstudio.microsoft.com/es/downloads/).
+ La carga de trabajo en Visual Studio 2019: Desarrollo de ASP.NET y web.
![](https://thepracticaldev.s3.amazonaws.com/i/xqyw33v95om0wozvckh3.jpg)

+ Extensión de DotVVM para 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).

##El entorno de la solución##

Para nuestro caso de estudio, tomaremos como documento de ejemplo la información de un estudiante para la realización de operaciones CRUD y manejaremos la aplicación en tres partes: 

1. *Implementación del DAL (Data Access Layer)*: para manejar la conexión y el acceso a la base de datos documental en MongoDB. 
2. *Implementación del BL (Business Layer)*: para el manejo de los servicios y la lógica del dominio de la aplicación.  
3. *Implementación de la capa de presentación de la aplicación*. En esta sección es donde DotVVM entra en acción. 

##Parte 1: Data Access Layer - DAL##

Para trabajar con MongoDB es necesario instalar el siguiente paquete NuGet: 

`MongoDB.Driver`

Como primer punto para crear nuestra aplicación, en el `Data Access Layer` debemos definir las colecciones que tendrá el dominio de la aplicación y definir un módulo de configuración para hacer referencia a las propiedades de la base de datos (cadena de conexión, nombre de la base de datos, y las colecciones de esta). 

![](https://dev-to-uploads.s3.amazonaws.com/i/jt0gtzkqcwpgo8cpgz0s.png)
 
En la aplicación manejaremos la colección: `Student`. Si tuviéramos otras colecciones, por ejemplo: `Profesor`, `Asignatura`, etc; estas serán ubicadas en la carpeta `Collections`. Para nuestro caso de estudio, la colección `Student` estará definida de la siguiente manera: 

```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; }
}
```

Para el almacenamiento y la transferencia de datos en MongoDB se emplea el formato BSON, el cual nos permite trabajar con documentos en MongoDB. En este caso, en la clase `Student` podemos especificar el nombre del atributo de la colección al que hacemos referencia (normalmente se emplean cuando los nombres son diferentes), el tipo de dato BSON y otras directivas. Por ejemplo, en MongoDB, la clave primaria de una colección esta identificada por el atributo: `_id`, lo cual nos permitirá identificar a un documento, en este caso, será el `Id` del estudiante el identificador principal. Los tipos de datos BSON los podemos encontrar en la documentación de MongoDB: [https://docs.mongodb.com/manual/reference/bson-types/](https://docs.mongodb.com/manual/reference/bson-types/).

Por otro lado tenemos la interfaz y su implementación: `DatabaseSettings` para el manejo de las propiedades especificas de la base de datos con 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; }
}
```

Los valores de cada uno de los atributos en `DatabaseSettings` estarán definidos en el archivo `appsettings.json`. Los nombres de las propiedades de JSON y C# son iguales para facilitar el proceso de asignación.

#####Archivo appsettings.json#####

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

Para establecer la relación entre la clase `DatabaseSettings` y el archivo de configuración `appsettings.json`, es necesario definir este ajuste en el constructor y en el método `ConfigureServices` de la clase `Startup.cs` como se muestra a continuación: 

```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)));
}
```



En este código hay dos aspectos importantes por mencionar:

+ La instancia de configuración a la que la sección `DatabaseSettings` del archivo `appsettings.json` enlaza está registrada en el contenedor de inserción de dependencias. Por ejemplo, una propiedad `ConnectionString` del objeto `DatabaseSettings` se rellena con la propiedad `DatabaseSettings:ConnectionString` en `appsettings.json`.

+ La interfaz `DatabaseSettings` se registra en la inserción de dependencias con una duración de servicio de tipo singleton (patrón de diseño). Cuando se inserta, la instancia de la interfaz se resuelve en un objeto `DatabaseSettings`.

##Parte 2: Business Layer - BL##

Ahora debemos definir los modelos y crear los servicios para manejar la lógica de nuestra aplicación. En este caso, lo que se busca es tener un listado general de los estudiantes y la información específica de cada uno de ellos.

####Modelos####

Para ello, como primer punto definiremos nuestros modelos:

**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; }
}
```

Y luego los servicios de nuestra aplicación. 

####Servicios####

En este caso tenemos el servicio de `Estudiantes` que nos permitirá realizar operaciones CRUD: Crear, Leer, Actualizar y Borrar documentos en MongoDB. A continuación, vamos a ver cada uno de ellos.

#####A. Inicialización del servicio#####

```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);
}
```

De forma resumida, en esta parte el trabajo realizado consiste en establecer la conexión con MongoDB, seleccionando la base de datos con la cual trabajaremos y la colección correspondiente. 

#####B. Leer documentos#####

```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();
}
```
La primera opción que tenemos es la de obtener todos los documentos de la colección con la cual estamos trabajando. A través del atributo `_students` que hace referencia a la colección, podemos emplear los [métodos que brinda MongoDB](https://docs.mongodb.com/manual/reference/method/js-collection/) para la realización de operaciones CRUD, de consulta, entre otras. 

En este caso se hace uso del método `Find()` que permite realizar consultas sobre los documentos de una colección. Con el resultado de esta consulta, podemos emplear *LINQ (Language Integrated Query)*, un componente en el cual podemos realizar consultas a través de objetos (muy parecidas a SQL). *LINQ* nos servirá para establecer un listado de objetos de tipo `StudentListModel` con los documentos resultado de la consulta en MongoDB.
  
También podemos realizar consultas con condicionales, por ejemplo, la consulta que se muestra a continuación: 

```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. Crear documentos#####

Para la creación de documentos en una colección disponemos los métodos: `InsertOne`, para la inserción de un documento; e `InsertMany`, para la inserción de varios de ellos. Para este caso, el método que hemos destinado para este objetivo `InsertStudentAsync` recibe un modelo `StudentDetailModel` con los datos a ser insertados, el proceso consta en construir una clase `Student` (la que hace referencia a la base de datos) e insertarla posteriormente a través de la referencia `_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. Actualizar documentos#####

Para la actualización de documentos se sigue la misma lógica de los métodos de creación e inserción. El documento referencia será actualizado según el identificador de este. 

```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. Eliminar documentos#####

Para eliminar un documento, se utiliza el método `DeleteOne`, el cual permite eliminar un documento de acuerdo al identificador del mismo. 

```csharp
public async Task DeleteStudentAsync(int IdStudent)
{
    await _students.DeleteOneAsync(Student => Student.Id == IdStudent);
}
```
Hasta este momento hemos definido los servicios de nuestro modelo de dominio de la aplicación. Lo que resta es utilizar estos servicios desde una aplicación, la cual será diseñada en ASP.NET Core con DotVVM. 

##Parte 3: Capa de presentación de la aplicación##

Ahora que ya tenemos definido el `DAL` y el `BL`, ahora debemos realizar el diseño de la pagina web para que el usuario pueda interactuar con ella y en este caso, realizar las operaciones CRUD para el manejo de Estudiantes.
 
Antes de continuar, es necesario asociar a nuestra aplicación (registrar) los servicios que se utilizaran. Para esto, debemos dirigirnos al método `ConfigureServices` en la clase `Startup` y tener algo como esto: 

```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>();
}
```

Ahora bien, esta es la parte en la que DotVVM entra en acción. Cada página en DotVVM consta de dos archivos:

+ Una vista, que se basa en la sintaxis HTML y describe cómo se verá la página. 
+ Un modelo de la vista, que es una clase en C# que describe el estado de la página (por ejemplo, valores en los campos del formulario) y maneja las interacciones del usuario (por ejemplo, clics de botones).

Para nuestro caso tendremos cuatro Vistas y cuatro modelos asociados a estas vistas:

+ *Default*: será la pagina principal de la aplicación en donde se visualizará el listado de los estudiantes registrados. 
+ *Create*: una página conformada por un formulario para crear nuevos estudiantes. 
+ *Detail*: para ver a detalle la información de un estudiante. 
+ *Edit*: para modificar la información de un estudiante o eliminarlo. 

Teniendo en cuenta los archivos Views y Viewmodels, en Visual Studio visualizaremos algo como esto: 
 
A continuación, veamos a detalle el View y Viewmodel de `Default` y sus componentes. 

####Viewmodel del Default####

```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();
        }
    }
```

Como primer punto tenemos la instancia de `StudentService` que nos permitirá acceder a los métodos para manejar las operaciones definidas en el servicio de `Student` implementado en el BL.

Luego tenemos la definición `List<StudentListModel> Students` de tipo `StudentListModel` definido en las clases de los modelos en el BL, que tendrá el listado de los estudiantes (`Id`, `FirstName` y `LastName`) para cargarlos en una tabla en la página principal de la aplicación web. 

Una característica muy importante por mencionar es la declaración `[Bind(Direction.ServerToClient)]`. Este tipo de propiedades permiten especificar que información va a ser transferida del servidor al cliente o del cliente al servidor al usar los Binding Directions. Considerando el caso del listado de estudiantes, en muchas ocasiones no es necesario transferir todo el modelo de vista en ambas direcciones. Del servidor a la vista será suficiente en este caso. 

Mas información sobre Binding Directions aquí: 
[https://www.dotvvm.com/docs/tutorials/basics-binding-direction/2.0](https://www.dotvvm.com/docs/tutorials/basics-binding-direction/2.0).

Finalmente en el `Viewmodel` de `Default` tenemos el método `PreRender()`, que permite realizar cierto tipo de operaciones que serán realizadas al momento de cargar la Vista. En este caso, se realizará una consulta a la base de datos a través de la llamada al método del servicio `studentService.GetAllStudentsAsync()`, luego los resultados serán asignados en la colección `Students` de tipo `StudentListModel` y luego la página será cargada junto con los demás componentes de diseño. 

####View de Default####

```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>
```

Como Podemos ver el View de Default, el diseño de la página se torna en el manejo de sentencias HTML y CSS. Para nuestro caso de estudio, hay algunas sentencias y características interesantes que podemos analizar: 

`GridView: <dot:GridView … >`, un control de DotVVM que nos permite crear una tabla o cuadrilla para visualizar un determinado listado de información. En HTML estaríamos hablando de la etiqueta `<table>`. Uno de sus atributos es `DataSource: DataSource="{value: Students}"`, el cual permite especificar la fuente de datos, en este caso hacemos referencia al listado de estudiantes: `Students`, el cual fue definido en el `Viewmodel` como vimos anteriormente.  

Además de las cuadriculas, DotVVM también tiene otros componentes de control personalizados, por ejemplo, para cajas de texto, `ComboBox`, manejo de archivos, entre otros más que nos permiten mantener una comunicación entre la Vista y las fuentes de información definidas en los `Viewmodels`. Ver más aquí: [https://www.dotvvm.com/docs/controls/](https://www.dotvvm.com/docs/controls/).

Siguiendo con nuestro análisis, en el GridView tenemos las columnas `Id`, `FirstName` y `LastName` de los estudiantes, pero adicionalmente, también podemos adicionar columnas para realizar operaciones sobre algún registro en específico. En este caso, con `RouteLink`, podemos definir un hipervínculo que construye una URL a partir de nombres de rutas y valores de parámetros para redirigirnos a otras paginas o realizar operaciones adicionales, por ejemplo, ver detalle o modificar el registro de un estudiante en particular según su `Id`: 

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

Estas rutas y sus parámetros correspondientes las debemos definir en el archivo `DotvvmStartup.cs` en el método `ConfigureRoutes` de la siguiente manera:

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

Para aprender más sobre el Routing en DotVVM puedes ir aquí: 
[https://www.dotvvm.com/docs/tutorials/basics-routing/2.0](https://www.dotvvm.com/docs/tutorials/basics-routing/2.0).

Para las paginas Crear, Ver Detalle y Modificar se sigue la misma lógica en cuanto a los componentes `View` y `Viewmodel`. Al agregar algunos registros de estudiantes en nuestra aplicación y cargar la página principal con el listado de los mismos, tendremos algo como esto: 

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

##Plus: Recursos para el alojamiento de datos de MongoDB y bases de datos NoSQL en la nube##

Hoy en día la tendencia esta en publicar las paginas web en la nube, para ello existen diversos servicios que nos permiten cumplir con estos objetivos, sea cual sea el gestor de base de datos que se este utilizando, ya sea SQL o NoSQL. Para las bases de datos NoSQL tenemos estas opciones recomendadas: 

####A. mLab####

mLab es un servicio gratuito de base de datos en la nube administrado que aloja bases de datos MongoDB. Tiene el mismo funcionamiento de MongoDB local, lo único que cambia es la cadena de conexión. Para conocer más de mLab podemos ir a: [https://mlab.com/](https://mlab.com/). 

####B. Azure CosmosDB - La API para MongoDB####

Azure Cosmos DB es un servicio de base de datos con varios modelos y de distribución global de Microsoft. Para este caso, Azure proporciona una API de Cosmos DB para MongoDB, la cual, además de trabajar con todas las funcionalidades de MongoDB, también podemos hacer uso de todo el poder de Azure para el manejo de bases de datos NoSQL, por ejemplo: distribución global, particionamiento automático, garantías de disponibilidad y latencia, cifrado en reposo, copias de seguridad, entre otras. Para conocer más de la API de Cosmos DB para MongoDB podemos visitar este documento de Microsoft: [https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-introduction](https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-introduction).

##¿Qué sigue?##
 
Con este articulo tutorial hemos aprendido como trabajar con MongoDB en aplicaciones con ASP.NET Core y DotVVM a través de Visual Studio 2019 para implementar operaciones CRUD sobre una colección de Estudiantes. 

El código de este tutorial lo podemos encontrar en el siguiente repositorio en GitHub: [DotVVM + MongoDB](https://github.com/esdanielgomez/dotnetMongoDB).

####Recursos adicionales:####

Aquí se encuentran otros artículos para que puedas seguir adquiriendo nuevos conocimientos sobre .NET:  

+ [Pasos a seguir para crear una aplicación MVVM (Modelo Vista Vista-Modelo) con DotVVM y ASP.NET Core](https://medium.com/@esdanielgomez/pasos-a-seguir-para-crear-una-aplicaci%C3%B3n-mvvm-modelo-vista-vista-modelo-con-dotvvm-y-asp-net-core-41afd67125b1).
+ [Despliegue de aplicaciones web DotVVM y .NET Core en Azure (Todo lo que necesitas saber)](#).
+ [Implementación de un sistema de clasificación de estrellas con DotVVM y CSS](https://dev.to/esdanielgomez/implementacion-de-un-sistema-de-clasificacion-de-estrellas-con-dotvvm-y-css-1mf9). 

¡Gracias!
Nos vemos en [Twitter](https://twitter.com/esDanielGomez)!! :)