DotVVM y ASP.NET Core: Implementación de operaciones CRUD
Tags: webdev, dotnet, csharp, dotvvm
Published: 2020-01-16 18:41:26

DotVVM es un marco de trabajo de ASP.NET que nos permite crear aplicaciones web utilizando el patrón de diseño MVVM (Modelo Vista Vista-Modelo) empleando C# y HTML. En este tutorial aprenderemos como crear operaciones CRUD (Crear, Leer, Actualizar y Borrar) empleando el framework DotVVM y .NET Core.

¿Deseas saber cuáles son los pasos para crear una aplicación DotVVM? Para ello puedes revisar este articulo: [Pasos para crear una aplicación MVVM (Model-View-Viewmodel) 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)

Para nuestro caso de estudio, tomaremos como entidad de ejemplo a la información de un estudiante para la realización de las operaciones CRUD y dividiremos 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. 
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 ##

Como primer punto para crear nuestra aplicación, en el Data Access Layer debemos definir las entidades que tendrá el dominio de la aplicación y el `DBContext` en el cual se definirá la referencia para la conexión a nuestra base de datos. 

![](https://thepracticaldev.s3.amazonaws.com/i/65nfpq83jborm4vc8ws8.jpg)

En la aplicación manejaremos la entidad: `Student`. Si tuviéramos otras entidades, por ejemplo: `Profesor`, `Asignatura`, etc; estas serán ubicadas en la carpeta *Entities*. Para nuestro caso de estudio, la entidad `Student` estará definida de la siguiente manera:

```csharp
public class Student
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string About { get; set; }
    public DateTime EnrollmentDate { get; set; }
}
```

Por otro lado, tenemos el `DBContext`, la clase primaria con la que va a interactuar nuestra aplicación (Las operaciones en la lógica del negocio) y que permitirá la comunicación con la base de datos:

```csharp
public class StudentDbContext : DbContext
{
    public StudentDbContext(DbContextOptions options) : base(options)
    {
        Database.EnsureCreated();
    }

    public DbSet<Student> Students { get; set; }
}
```

En el `StudentDbContext` hay dos cosas importantes por mencionar: 

+ `context.Database.EnsureCreated()` es un método principal de Entity Framework - EF que garantiza que exista la base de datos para el contexto. Si existe, no se toman medidas. Si no existe, se crea la base de datos y todos sus esquemas y también se garantiza que sea compatible con el modelo para este contexto.

+ `DbSet<Student> Students {get; set;}`; representa una colección para la entidad `Student` dentro del modelo de datos y es la puerta de entrada a las operaciones en la base de datos con esta entidad. 

Normalmente esta sección del DAL (Las entidades como `Student` y las asociaciones en el `DbContext`) es generada automáticamente según las entidades ya definidas en la base de datos a través de *Entity Framework*. 

## Parte 2: Business Layer - BL ##

Ahora nos toca 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####

Como primer punto definiremos nuestros modelos:

**StudentListModel**:
```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 implementamos los servicios de nuestra aplicación.

####Servicios####

En este caso tenemos el servicio de `Student` que nos permitirá realizar las operaciones CRUD.
```csharp
public class StudentService
{
    private readonly StudentDbContext studentDbContext;

    public StudentService(StudentDbContext studentDbContext)
    {
        this.studentDbContext = studentDbContext;
    }

    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)...
}
```
Para el manejo de la información almacenada en la base de datos se utiliza LINQ - *Language Integrated Query*, un componente de la plataforma Microsoft .NET que agrega capacidades de consulta a datos de manera nativa a los lenguajes .NET. En otras palabras, LINQ nos permite realizar consultas sobre colecciones de objetos (las entidades definidas en el DAL) para manejar la información y realizar operaciones sobre la base de datos. 

Para tener un poco más claro el funcionamiento de LINQ, en el siguiente método podemos ver el procedimiento que nos permite obtener la información de un estudiante en particular según su identificación: 

```csharp
public async Task<StudentDetailModel> GetStudentByIdAsync(int studentId)
{
    return await studentDbContext.Students.Select(
            s => new StudentDetailModel
            {
                Id = s.Id,
                FirstName = s.FirstName,
                LastName = s.LastName,
                About = s.About,
                EnrollmentDate = s.EnrollmentDate
            })
        .FirstOrDefaultAsync(s => s.Id == studentId);

}
```
Para ver más a detalle el funcionamiento de LINQ puedes consultar la documentación de Microsoft en: [https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities)

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

Ahora que ya tenemos definido el DAL y el BL, ahora nos toca realizar el diseño de la página web para que el usuario pueda interactuar con ella y en este caso, realizar las operaciones CRUD para el manejo de Estudiantes. 

Esta es la parte en la cual 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 página 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: 

![](https://thepracticaldev.s3.amazonaws.com/i/8okcb37914uzhimfi50e.jpg)

A continuación, analicemos mas 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>Estudiantes</h1>
			
				<dot:RouteLink Text="{resource: Texts.Label_NewStudent}" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>
			
		</div>
		<dot:GridView DataSource="{value: Students}" class="page-grid">
			<Columns>
				<dot:GridViewTextColumn ValueBinding="{value: FirstName}" HeaderText="{resource: Texts.Label_Firstname}" />
				<dot:GridViewTextColumn ValueBinding="{value: LastName}" HeaderText="{resource: Texts.Label_Lastname}" />
				<dot:GridViewTemplateColumn>
					<dot:RouteLink Text="{resource: Texts.Label_Detail}" RouteName="CRUD_Detail" Param-Id="{{value: Id}}" />
				</dot:GridViewTemplateColumn>
				<dot:GridViewTemplateColumn>
					<dot:RouteLink Text="{resource: Texts.Label_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/

Siguiendo con nuestro análisis, en el GridView tenemos las columnas `FirstName` y `LastName` de los estudiantes, pero adicionalmente, nosotros 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 páginas o realizar operaciones adicionales, por ejemplo, ver detalle o modificar el registro de un estudiante en particular según su ID: 

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

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

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

Para aprender más sobre *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 páginas Crear, Ver Detalle y Modificar se sigue la misma lógica en cuanto a los componentes del 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://thepracticaldev.s3.amazonaws.com/i/ab3xe6zjvlu3998ndax2.jpg)

## ¿Qué sigue? ##

Con estos pasos hemos analizado las partes más importantes de una aplicación web MVVM utilizando DotVVM y.NET Core con la ayuda de Visual Studio 2019 para implementar un CRUD sobre Estudiantes. 

El código de este ejemplo se lo puede encontrar en la página de muestra que Visual Studio 2019 tiene para DotVVM al generar un nuevo proyecto de tipo: DotVVM con ASP.NET Core y seleccionando la opción: Página CRUD de muestra.

También podemos encontrar el código fuente en este [repositorio](https://github.com/esdanielgomez/DotVVMcrud).

#### Gracias ####

En los próximos artículos continuaremos revisando los componentes que tiene DotVVM para el diseño de páginas web y casos prácticos que nos pueden ser de utilidad en el futuro. 

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