Trabajando con servicios web a través de ASP.NET Core y DotVVM
Tags: webdev, dotnet, csharp, dotvvm
Published: 2020-06-29 21:26:28

En la actualidad existen muchas organizaciones que tienen la necesidad de comunicarse e intercambiar datos con aplicaciones externas, para poder adquirir servicios web con los que no cuentan y satisfacer las necesidades de sus clientes potenciales. 

En términos más técnicos, el primer objetivo es implementar el backend de un sistema determinado que acceda a la base de datos y provea servicios para la manipulación de estos datos. A partir de esto, el segundo objetivo es emplear estos servicios, ya sea desde un sitio web o desde una aplicación móvil, con la finalidad de preocuparse únicamente por el diseño del frontend en esta segunda instancia. 

####Idea general de la solución:####

Para este artículo tenemos dos objetivos. El primero se trata sobre la construcción de un API Rest que permitirá poner a disposición servicios web para el manejo de datos de una base de datos con MySQL. El segundo objetivo consiste en utilizar estos servicios en un segundo proyecto a través de una página web implementada con DotVVM. 

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

**Nota**: el código fuente empleado en este articulo está disponible en GitHub en los siguientes repositorios: [REST API with ASP.NET Core and MySQL](https://github.com/esdanielgomez/ASP.NET_WebAPI) y [Consuming Web API in DotVVM with ASP.NET Core](https://github.com/esdanielgomez/DotVVMConsumingWebAPI).
 

####Actividades:####

El articulo tendrá dos partes importantes: 

+ Parte 1: Implementación de un Web API con ASP.NET Core. 
+ Parte 2: Consumiendo servicios Web HTTP con DotVVM.

####Recursos necesarios:####

Para seguir paso a paso este articulo o ejecutar el demo incluido, es necesario tener en funcionamiento las siguientes herramientas: 

+ MySQL. 
+ .NET Core SDK. 
+ Visual Studio 2019.
+ La carga de trabajo Desarrollo web y ASP.NET para Visual Studio 2019.
+ La extensión de DotVVM para Visual Studio 2019. 

##**Parte 1: Implementación de un Web API con ASP.NET Core**

En esta primera parte tendremos tres partes importantes: 
+ 1. Establecer la base de datos. 
+ 2. Establecer el acceso a la base de datos desde ASP.NET Core a través de Entity Framework. 
+ 3. Establecer los controladores y sus funcionalidades. Estos métodos serán los encargados de brindar los servicios web para su posterior utilización. 

Como caso de estudio para este tutorial se manejará la información de usuarios a través de operaciones CRUD: Crear, Leer, Actualizar y Borrar.

####La base de datos para el domino de la aplicación

La base de datos esta conformada por una única tabla llamada: `User`, con los atributos: `Int`, `FirstName`, `LastName`, `Username`, `Password` y `EnrrollmentDate`.

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

La sentencia SQL para la creación de la tabla User es la siguiente: 

```sql
CREATE TABLE `user` (
  `Id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `FirstName` VARCHAR(45) NOT NULL,
  `LastName` VARCHAR(45) NOT NULL,
  `Username` VARCHAR(45) NOT NULL,
  `Password` VARCHAR(45) NOT NULL,
  `EnrollmentDate` datetime NOT NULL 
) AUTO_INCREMENT=1;

```
Muy bien, con la base de datos preparada, ya podremos comenzar con la implementación del primer proyecto para el desarrollo de servicios API Rest. 

####Proyecto ASP.NET Core del tipo Web API

En Visual Studio 2019 lo primero que haremos es crear un nuevo proyecto de tipo ASP.NET Core Web Application (dentro de la categoría .NET Core - C#): 

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

Después de especificar el nombre del proyecto, seleccionaremos la plantilla de tipo: API, para el proyecto de ASP.NET Core Web a crear: 

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

Con este proyecto crearemos el acceso a la base de datos e implementaremos el controlador correspondiente, en este caso, para la clase `User`. 

####Acceso a la base de datos con Entity Framework

Para establecer las entidades a través de clases y la conexión de la base de datos se puede emplear el enfoque Database First de Entity Framework, el cual permite hacer *scaffolding* desde la base de datos hacia el proyecto, es decir, generar clases automáticamente de acuerdo a las entidades establecidas en la base de datos y la conexión en el proyecto. 

Para este propósito, es necesario instalar tres paquetes NuGet: 

+ `Microsoft.EntityFrameworkCore.Design`
+ `Microsoft.EntityFrameworkCore.Tools`
+ `MySql.Data.EntityFrameworkCore`

En caso de que se este trabajando con SQL Server, el paquete NuGet a instalar será: `Microsoft.EntityFrameworkCore.SQLServer`. 

**Nota**: para encontrar el centro de administración de los paquetes NuGet podemos dirigirnos al menú de opciones -> proyecto -> Manejar paquetes NuGet. 

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

Con la instalación de estos paquetes NuGet, ahora abriremos la consola de administración de paquetes para introducir un comando que permitirá realizar *scaffolding* desde la base de datos: 

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

**Comando:**

```
Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir Entities -f
```
El resultado es el siguiente:

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

La clase `User` esta definida de la siguiente manera:

```csharp
public partial class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public DateTime EnrollmentDate { get; set; }
}

```
Y el `DBContext`, el cual tiene la configuración con la base de datos, cuyo método principal es: `OnConfiguring`, se verá algo como esto:

```csharp
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {                optionsBuilder.UseMySQL("server=localhost;port=3306;user=root;password=;database=database");
            }
        }

```
Ahora, no es lo más adecuado que la cadena de conexión a la base de datos se encuentre especificada en este método `OnConfiguring`, para ello, especificaremos la cadena de conexión en el archivo `appsettings.json` de la siguiente manera: 

```csharp
"AllowedHosts": "*",
"ConnectionStrings": {
  "DefaultConnection": "server=servername;port=portnumber;user=username;password=pass;database=databasename;"
}
```
Luego, en la clase `Startup`, en el método `ConfigureServices`, agregamos como servicio al `DBContext` y hacemos referencia a la propiedad `DefaultConnection` especificada en el archivo `appsettings.json`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFrameworkMySQL()
        .AddDbContext<DBContext>(options =>
        {
            options.UseMySQL(Configuration.GetConnectionString("DefaultConnection"));
        });
    services.AddControllers();
}
```
En este caso, regresando a la clase del `DBContext`, borramos la cadena de conexión especificada en el método `OnConfiguring`. A la final tendríamos el método vacío:

```csharp
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{}
```

Y el constructor del `DBContext` estaría constituido de la siguiente manera: 

```
public DBContext(DbContextOptions<DBContext> options)
    : base(options) {}
```
Con estos pasos ya tenemos lista la conexión y las configuraciones necesarias para trabajar con la base de datos en ASP.NET Core con la ayuda de Entity Framework. 

####Establecer los DTOs - Objetos de Transferencia de Datos 

Con el objetivo de transportar los datos entre los procesos para el manejo de la base de datos y los procesos para trabajar con los servicios web, es recomendable establecer las clases DTO por cada entidad del proyecto, en este caso, un DTO para la entidad User. 

Para ello crearemos una nueva carpeta dentro del proyecto llamada DTO, y crearemos una clase con nombre: `UserDTO`, cuyos atributos serán los mismos que la clase `User` definida en la sección `Entities` anteriormente: 

```csharp
public class UserDTO
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public DateTime EnrollmentDate { get; set; }
}
```

####Controladores para el Web API ####

Ahora lo que haremos es agregar los controladores, en este caso el controlador para el usuario, el cual permitirá establecer métodos para realizar operaciones CRUD sobre las tablas de la base de datos y exponerlos a través del Web API. Sobre la carpeta `Controllers`, agregaremos un controlador llamado `User`: 

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

La definición de la clase y su constructor se verá así:

```csharp
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly DBContext DBContext;

    public UserController( DBContext DBContext)
    {
        this.DBContext = DBContext;
    }
    ...
}
```
Ahora el objetivo es implementar los métodos de este controlador para realizar las operaciones CRUD. En este punto es importante mencionar los métodos básicos de HTTP. Estos métodos son: 

+ **POST (create)**: es un método para cuando se envía información para insertar por ejemplo un registro en la base de datos. La información es enviada en el cuerpo de la petición, es decir, que los datos no son visibles para usuario.
+ **GET (read)**: es un método empleado para un modo lectura, por ejemplo: cuando se desea listar a todos los usuarios de una base de datos. Los parámetros son enviados por una URL.
+ **PUT (update)**: es un método para cuando se desea actualizar un registro. 
+ **DELETE (delete)**: es un método para cuando se desea eliminar un registro. Borrar un usuario X de una base de datos.

Teniendo en cuenta estas consideraciones, para la implementación de operaciones CRUD utilizaremos métodos para acceder a la información (Get), para insertar datos (Post), para modificar (Put) y para eliminar un registro (Delete).

A continuación, se muestra el código final de cada uno de los métodos:

**A. Obtener el listado de todos los usuarios registrados**

```csharp
[HttpGet("GetUsers")]
public async Task<ActionResult<List<UserDTO>>> Get()
{
    var List = await DBContext.User.Select(
        s => new UserDTO
        {
            Id = s.Id,
            FirstName = s.FirstName,
            LastName = s.LastName,
            Username = s.Username,
            Password = s.Password,
            EnrollmentDate = s.EnrollmentDate
        }
    ).ToListAsync();

    if (List.Count < 0)
    {
        return NotFound();
    }
    else
    {
        return List;
    }
}
```

**B. Obtener los datos de un usuario especifico según su Id**

```csharp
[HttpGet("GetUserById")]
public async Task<ActionResult<UserDTO>> GetUserById(int Id)
{
    UserDTO User = await DBContext.User.Select(
            s => new UserDTO
            {
                Id = s.Id,
                FirstName = s.FirstName,
                LastName = s.LastName,
                Username = s.Username,
                Password = s.Password,
                EnrollmentDate = s.EnrollmentDate
            })
        .FirstOrDefaultAsync(s => s.Id == Id);

    if (User == null)
    {
        return NotFound();
    }
    else
    {
        return User;
    }
}
```

**C. Insertar un nuevo usuario**

```csharp
[HttpPost("InsertUser")]
public async Task<HttpStatusCode> InsertUser(UserDTO User)
{
    var entity = new User()
    {
        FirstName = User.FirstName,
        LastName = User.LastName,
        Username = User.Username,
        Password = User.Password,
        EnrollmentDate = User.EnrollmentDate
    };

    DBContext.User.Add(entity);
    await DBContext.SaveChangesAsync();

    return HttpStatusCode.Created;
}
```

**D. Actualizar los datos de un usuario especifico**

```csharp
[HttpPut ("UpdateUser")]
public async Task<HttpStatusCode> UpdateUser(UserDTO User)
{
    var entity = await DBContext.User.FirstOrDefaultAsync(s => s.Id == User.Id);

    entity.FirstName = User.FirstName;
    entity.LastName = User.LastName;
    entity.Username = User.Username;
    entity.Password = User.Password;
    entity.EnrollmentDate = User.EnrollmentDate;

    await DBContext.SaveChangesAsync();
    return HttpStatusCode.OK;
}
```

**E. Eliminar a un usuario según su Id**

```csharp
[HttpDelete("DeleteUser/{Id}")]
public async Task<HttpStatusCode> DeleteUser(int Id)
{
    var entity = new User()
    {
        Id = Id
    };
    DBContext.User.Attach(entity);
    DBContext.User.Remove(entity);
    await DBContext.SaveChangesAsync();
    return HttpStatusCode.OK;
}
```

Con estos métodos y con los pasos seguidos hasta este punto, los servicios están listos para ponerse en ejecución. 

####Probar el Web API implementado 

Para probar el Web API implementado haremos uso de Postman, una herramienta multiplataforma que permite hacer testing de API REST. El instalador lo podemos encontrar en la siguiente dirección: [https://www.postman.com/downloads/](https://www.postman.com/downloads/). 

Al compilar y ejecutar la aplicación desde Visual Studio 2019: 

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

Una vez iniciado Postman, podemos realizar la primera prueba. A continuación, podemos ver la utilización del servicio para recuperar todos los usuarios registrados: 

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

Como podemos ver en la imagen, es necesario especificar el tipo de operación, en este caso es de tipo GET, tal y como se especificó en el controlador del usuario. Asimismo, podemos ver que el estatus del resultado es 200 OK – es decir, que todo salió bien. Y finalmente podemos ver en la respuesta el JSON con los resultados.

En un ejemplo adicional podemos ver el caso para obtener un usuario especifico:  

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

##**Parte 2: Consumiendo servicios HTTP con DotVVM**

En esta segunda parte tendremos dos partes importantes: 

+ 1. Establecer los métodos para la comunicación con los servicios HTTP definidos anteriormente. 
+ 2. Implementar las vistas y los modelos de estas vistas para el desarrollo de la página web correspondiente.  

El objetivo con las páginas web es consumir las operaciones CRUD: crear, leer, actualizar y borrar establecidas en los servicios HTTP. 

####Proyecto DotVVM con ASP.NET Core

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

En DotVVM la comunicación entre HTML (páginas web) y C# (código fuente) se lleva a cabo a través del patrón de diseño MVVM (Modelo, Vista, Vistamodelo). La finalidad de estos elementos son los siguientes:

+ **El modelo.** — es responsable de todos los datos de la aplicación y de la lógica de negocios relacionada.

+ **La vista.** — Representaciones para el usuario final del modelo de la aplicación. La vista es responsable de mostrar los datos al usuario y de permitir la manipulación de los datos de la aplicación.

+ **El Modelo-Vista o Vista-Modelo.** — uno o más por vista; el modelo-vista es responsable de implementar el comportamiento de la vista para responder a las acciones del usuario y de exponer los datos del modelo fácilmente.

####Modelos y lógica de la aplicación

En esta primera parte definiremos los modelos y los servicios para consumir los servicios HTTP y establecer la lógica de nuestra aplicación. En este caso, lo que se busca es tener un listado general de los usuarios e información específica de cada uno de ellos.

Para esto, como primer punto definiremos los modelos:

**UserList**

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

**UserDetailModel**
```csharp
public class UserDetailModel
{
    public int Id { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    [Required]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
    [Required]
    public DateTime EnrollmentDate { get; set; }
```
Y luego los servicios de nuestra aplicación. En este caso tenemos el servicio de usuarios que nos permitirá establecer las operaciones CRUD de acuerdo a los métodos HTTP.

Para la inicialización del servicio `User`, se puede comenzar con una definición como esta: 

```csharp
public class UserService
{
    private readonly string URLbase = "https://localhost:5003/api/User";
    …
```

A partir de la dirección base para el consumo de servicios HTTP, a continuación, se muestran los métodos correspondientes: 

**A. Obtener el listado de todos los usuarios registrados**

```csharp
public async Task<List<UserListModel>> GetAllUsersAsync()
{
    List<UserListModel> usersList = new List<UserListModel>();
    using (var httpClient = new HttpClient())
    {
        string URL = URLbase + "/GetUsers";
        HttpResponseMessage response = await httpClient.GetAsync(URL);
        string apiResponse = await response.Content.ReadAsStringAsync();
        usersList = JsonConvert.DeserializeObject<List<UserListModel>>(apiResponse).Select(
            s => new UserListModel
            {
                Id = s.Id,
                FirstName = s.FirstName,
                LastName = s.LastName
            }
            ).ToList();
    }
    return usersList;
}
```

**B. Obtener los datos de un usuario especifico según su Id**

```csharp
public async Task<UserDetailModel> GetUserByIdAsync(int Id)
{
    string URL = URLbase + "/GetUserById?id=" + Id;
    UserDetailModel User = new UserDetailModel();

    using (var httpClient = new HttpClient())
    {
        HttpResponseMessage response = await httpClient.GetAsync(URL);
        string apiResponse = await response.Content.ReadAsStringAsync();
        User = JsonConvert.DeserializeObject<UserDetailModel>(apiResponse);
    }
    return User;
}
```

**C. Insertar un nuevo usuario**

```csharp
public async Task InsertUserAsync(UserDetailModel user)
{
    string URL = URLbase + "/InsertUser";
            
    using (var httpClient = new HttpClient())
    {
        StringContent content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");
        HttpResponseMessage response = await httpClient.PostAsync(URL, content);
        string apiResponse = await response.Content.ReadAsStringAsync();       
    }
}
```

**D. Actualizar los datos de un usuario especifico**

```csharp
public async Task UpdateUserAsync(UserDetailModel user)
{
    string URL = URLbase + "/UpdateUser";

    using (var httpClient = new HttpClient())
    {
        StringContent content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");

        HttpResponseMessage response = await httpClient.PutAsync(URL, content);
        string apiResponse = await response.Content.ReadAsStringAsync();
    }
}
```

**E. Eliminar a un usuario según su Id**

```csharp
public async Task DeleteUserAsync(int Id)
{
    string URL = URLbase + "/DeleteUser/" + Id;

    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.DeleteAsync(URL);
        string apiResponse = await response.Content.ReadAsStringAsync();
    }
}
```

####Vistas y vistamodelos

Ahora que ya tenemos definidos los métodos que consumen los servicios HTTP, ahora solo nos resta diseñar la página web para que el usuario pueda interactuar con ella y en este caso, realizar las operaciones CRUD para el manejo de usuarios. 

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*, es decir, 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 usuarios registrados. 

+ *Create*: una página conformada por un formulario para crear nuevos usuarios. 

+ *Detail*: para ver a detalle la información de un usuario. 

+ *Edit*: para modificar la información de un usuario o eliminarlo. 

Teniendo en cuenta los archivos Views y Viewmodels, en Visual Studio visualizaremos algo como esto: 

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

A continuación, analicemos a detalle el View y Viewmodel de `Default` y sus componentes. 

**Viewmodel del Default**

```csharp
public class DefaultViewModel : MasterPageViewModel
{
    private readonly UserService userService;

	public DefaultViewModel(UserService userService)
    {
        this.userService = userService;
    }

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

    public override async Task PreRender()
    {
        Users = await userService.GetAllUsersAsync();
        await base.PreRender();
    }
}
```

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

Luego tenemos la definición `List<UserListModel> Users` de tipo `UserListModel` definido en las clases de los modelos, que tendrá el listado de los usuarios (`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 usuarios, 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. 

Más 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 `userService.GetAllUsersAsync()`, luego los resultados serán asignados en la colección `Users` de tipo `UserListModel` y luego la página será cargada junto con los demás componentes de diseño. 

**View de Default**

```html
@viewModel DotVVM_APIConsume.ViewModels.DefaultViewModel, DotVVM_APIConsume
@masterPage Views/MasterPage.dotmaster
<dot:Content ContentPlaceHolderID="MainContent">
	<div class="page-center">
		<div class="page-grid-top">
		<div class="student-image"></div>
			<h1>User List</h1>
			
				<dot:RouteLink Text="New User" RouteName="CRUD_Create" class="page-button btn-add btn-long"/>
			
		</div>
		<dot:GridView DataSource="{value: Users}" class="page-grid">
			<Columns>
				<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 users. 
            </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: Users}"`, el cual permite especificar la fuente de datos, en este caso hacemos referencia al listado de usuarios: `Users`, el cual fue definido en el Viewmodel como vimos anteriormente.  

Además de las tablas, 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 usuarios, 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 usuario en particular según su ID: 

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

```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 páginas Crear, Ver Detalle y Modificar se sigue la misma lógica en cuanto a los componentes del View y Viewmodel. 

####Ejecución de las soluciones 

Hasta este punto hemos implementado dos soluciones, la primera para brindar servicios HTTP a través de ASP.NET Core, y la segunda, consumir estos servicios a través de una aplicación web con DotVVM. Para hacer las pruebas a nivel local ambas soluciones de Visual Studio deben ejecutarse para poder habilitar los servicios HTTP y consumirlos a su vez. A continuación, podemos ver algunas capturas sobre la aplicación web implementada:  

**Crear un nuevo registro**

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

**Obtener el detalle de un registro especifico**

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

**Listado general de los usuarios**

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

##**¿Qué sigue?**

Con este articulo hemos aprendido paso a paso como implementar servicios HTTP que manejen la información de la base de datos con ASP.NET Core y como consumirlos a través de un proyecto con DotVVM. 

La siguiente actividad es publicar estos servicios en la nube para que puedan ser accedidos desde cualquier lugar. En Azure, por ejemplo, el proceso para subir un proyecto de tipo Web API o un proyecto constituido por páginas web, es el mismo. Los pasos para llevar a cabo este proceso se pueden consultar en el siguiente articulo: [Implementación continua en Azure con Visual Studio y GitHub](https://dev.to/esdanielgomez/implementacion-continua-en-azure-con-visual-studio-y-github-2153). 

El código fuente de los dos proyectos utilizados en este articulo están disponibles en los siguientes repositorios: [REST API with ASP.NET Core and MySQL](https://github.com/esdanielgomez/ASP.NET_WebAPI) y [Consuming Web API in DotVVM with ASP.NET Core](https://github.com/esdanielgomez/DotVVMConsumingWebAPI).

####Recursos adicionales

A continuación se muestran algunos recursos que podrían ser de tu interés para seguir adquiriendo nuevos conocimientos en esta área:

+ Curso gratuito y abierto en YouTube: [Fundamentos de ASP.NET Core y DotVVM](http://bit.ly/cursoaspnetdotvvm).

+ [Desarrollo de aplicaciones web con ASP.NET Core, DotVVM y MongoDB](https://dev.to/dotvvm/desarrollo-de-aplicaciones-web-con-asp-net-core-dotvvm-y-mongodb-59pp).

+ [Despliegue de aplicaciones web ASP.NET Core y DotVVM a través de una máquina virtual en Azure](https://dev.to/dotvvm/despliegue-de-aplicaciones-web-asp-net-core-y-dotvvm-a-traves-de-una-maquina-virtual-en-azure-4g6)

####Gracias por leer:

Espero que te haya gustado el artículo. Para estar al tanto sobre futuras contribuciones o si tienes alguna inquietud, puedes contactarme en Twitter: [twitter.com/esDanielGomez](https://twitter.com/esDanielGomez).

¡Hasta pronto!