Portal web para gestionar eventos y conferencias con ASP.NET 5
Tags: webdev, dotnet, html, csharp
Published: 2021-02-01 17:10:01

Cuando organizamos grandes eventos como conferencias o hackatones, en muchas ocasiones necesitamos administrar los datos de los speakers, las sesiones o presentaciones dentro del evento, los auspiciantes o los datos generales del mismo. Para este propósito, podemos crear un portal web administrativo que nos permita manejar todos estos datos. 

Teniendo en cuenta estas consideraciones, en este artículo aprenderemos de forma práctica como diseñar un dashboard para manejar los datos de un evento en persona o virtual. Este portal nos puede ayudar muchísimo en cuestiones de tiempo para administrar la información en una base de datos y posteriormente manejar todos estos datos en otro portal web para los usuarios/participantes del evento (este segundo portal lo analizaremos en un segundo articulo). 

El portal web para administrar eventos lo realizaremos con ASP.NET 5 con el patrón MVVM (Modelo, Vista, VistaModelo) con DotVVM. Donde, 

+ **El modelo.** — será responsable de todos los datos de la aplicación y de la lógica de negocios relacionada.
+ **La vista**. — corresponderán a representaciones para el usuario final del modelo de la aplicación. La vista será 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 será responsable de implementar el comportamiento de la vista para responder a las acciones del usuario y de exponer los datos del modelo fácilmente.

El resultado final del portal web será el siguiente:

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

***Nota:*** El código fuente de este proyecto puede ser encontrado en el siguiente repositorio de GitHub: [Event Admin]
(https://github.com/esdanielgomez/EventAdmin).
  
###**Funcionalidades base del portal web administrativo**
 
Para la administración de los datos del evento, las opciones y funcionalidades base que se consideran dentro del dashboard se describen a continuación:

+ **Organizadores**, donde se indica el nombre de las comunidades y/o instituciones que están organizando el evento. Aquí se pueden poner las redes sociales del grupo organizador y también una descripción.
+ **Speakers**, para tener un listado de aquellas personas que presentaran una sesión dentro del evento. 
+ **Sesiones**, donde se registran las presentaciones del evento, con sus respectivos speakers asociados. Aquí se puede especificar también el tipo de la sesión (por ejemplo: Keynote, taller, charla, etc), la fecha de inicio, la fecha de finalización, una descripción y el nivel de dificultad de la sesión (fundamentos, intermedio, avanzada).
+ **Sponsors**, ya que la mayoría de los eventos se pueden llevar a cabo gracias a sus patrocinadores, aquí se puede registrar a cada colaborador al especificar el nombre de la empresa, la página web, el logotipo y una descripción. 

Ahora que ya estamos al tanto de las opciones que serán incluidas, empecemos a construir nuestro portal web. 

###**Recursos y herramientas necesarias**

Como se ha mencionado inicialmente, para la construcción del portal utilizaremos ASP.NET 5 & DotVVM. En este sentido, los recursos y herramientas necesarias para establecer nuestro entorno de trabajo son las siguientes: 

+ [Visual Studio 2019](http://visualstudio.microsoft.com/es/downloads/).
+ La carga de trabajo en Visual Studio 2019: Desarrollo de ASP.NET y web.
 ![](https://dev-to-uploads.s3.amazonaws.com/i/kq86b3usxdrei95toa67.png)
+ [Extensión de DotVVM para Visual Studio 2019](www.dotvvm.com/landing/dotvvm-for-visual-studio-extension).

###**El esquema de la base de datos** 

Teniendo en cuenta las funcionalidades base del portal web administrativo, para el dashboard se ha considerado una base de datos SQL, con las entidades: `Organizer`, `Sponsor`, `Event`, `Speaker`, `Session`, `Speaker_has_Session`, `SessionLevel` y `SessionType`.

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

Para este caso, el gestor de la base de datos que se empleará es SQL Server, por lo cual, podríamos construir el dashboard inicialmente a nivel local, o considerar algún recurso en la nube como Azure SQL Database, para trabajar con la base de datos. 

El script SQL de esta base de datos es el siguiente: 

```sql
-- -----------------------------------------------------
CREATE TABLE  Event (
  IdEvent INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NOT NULL,
  Description VARCHAR(500) NULL,
  Icon VARCHAR(45) NULL,
  StartDate DATETIME NOT NULL,
  EndDate DATETIME NOT NULL,
  RegistrationLink VARCHAR(45) NULL,
  StreamingLink VARCHAR(45) NULL,
  PRIMARY KEY (IdEvent))
;

-- -----------------------------------------------------
CREATE TABLE  Speaker (
  IdSpeaker INT NOT NULL IDENTITY(1,1),
  FirstName VARCHAR(45) NOT NULL,
  SecondName VARCHAR(45) NULL,
  FirstLastName VARCHAR(45) NOT NULL,
  SecondLastName VARCHAR(45) NULL,
  PhotoLink VARCHAR(45) NULL,
  TwitterLink VARCHAR(45) NULL,
  LinkedInLink VARCHAR(45) NULL,
  IdEvent INT NOT NULL,
  PRIMARY KEY (IdSpeaker),
  INDEX fk_Speaker_Event_idx (IdEvent ASC),
  CONSTRAINT fk_Speaker_Event
    FOREIGN KEY (IdEvent)
    REFERENCES Event (IdEvent)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
;

-- -----------------------------------------------------
CREATE TABLE  SessionType (
  IdSessionType INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NOT NULL,
  Description VARCHAR(45) NULL,
  PRIMARY KEY (IdSessionType))
;

-- -----------------------------------------------------
CREATE TABLE  SessionLevel (
  IdSessionLevel INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NULL,
  Description VARCHAR(45) NULL,
  PRIMARY KEY (IdSessionLevel))
;

-- -----------------------------------------------------
CREATE TABLE  Session (
  IdSession INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NOT NULL,
  Description VARCHAR(500) NULL,
  StartDate DATETIME NULL,
  EndDate DATETIME NULL,
  IconLink VARCHAR(45) NULL,
  IdSessionType INT NOT NULL,
  IdSessionLevel INT NOT NULL,
  PRIMARY KEY (IdSession),
  INDEX fk_Session_SessionType1_idx (IdSessionType ASC),
  INDEX fk_Session_SessionLevel1_idx (IdSessionLevel ASC),
  CONSTRAINT fk_Session_SessionType1
    FOREIGN KEY (IdSessionType)
    REFERENCES SessionType (IdSessionType)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT fk_Session_SessionLevel1
    FOREIGN KEY (IdSessionLevel)
    REFERENCES SessionLevel (IdSessionLevel)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
;

-- -----------------------------------------------------
CREATE TABLE  Speaker_has_Session (
  IdSpeaker INT NOT NULL,
  IdSession INT NOT NULL,
  INDEX fk_Speaker_has_Session_Session1_idx (IdSession ASC),
  INDEX fk_Speaker_has_Session_Speaker1_idx (IdSpeaker ASC),
  CONSTRAINT fk_Speaker_has_Session_Speaker1
    FOREIGN KEY (IdSpeaker)
    REFERENCES Speaker (IdSpeaker)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT fk_Speaker_has_Session_Session1
    FOREIGN KEY (IdSession)
    REFERENCES Session (IdSession)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
;

-- -----------------------------------------------------
CREATE TABLE  Organizer (
  IdOrganizer INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NOT NULL,
  LogoLink VARCHAR(45) NULL,
  WebPage VARCHAR(45) NULL,
  Email VARCHAR(45) NULL,
  Description VARCHAR(45) NULL,
  FacebookLink VARCHAR(45) NULL,
  TwitterLink VARCHAR(45) NULL,
  InstagramLink VARCHAR(45) NULL,
  IdEvent INT NOT NULL,
  PRIMARY KEY (IdOrganizer),
  INDEX fk_Organizer_Event1_idx (IdEvent ASC),
  CONSTRAINT fk_Organizer_Event1
    FOREIGN KEY (IdEvent)
    REFERENCES Event (IdEvent)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
;

-- -----------------------------------------------------
CREATE TABLE  Sponsor (
  IdSponsor INT NOT NULL IDENTITY(1,1),
  Name VARCHAR(45) NOT NULL,
  LogoLink VARCHAR(45) NULL,
  Description VARCHAR(45) NULL,
  WebPage VARCHAR(45) NULL,
  IdEvent INT NOT NULL,
  PRIMARY KEY (IdSponsor),
  INDEX fk_Sponsor_Event1_idx (IdEvent ASC),
  CONSTRAINT fk_Sponsor_Event1
    FOREIGN KEY (IdEvent)
    REFERENCES Event (IdEvent)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
;
```

###**Entorno de la solución del proyecto**

Dentro de la solución en Visual Studio 2019, contaremos con tres proyectos: 

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

+ **DAL (Data Access Layer):** una librería de clases para manejar la conexión y el acceso a la base de datos.
+ **BL (Business Layer):** otra librería de clases para el manejo de los servicios y la lógica del dominio de la aplicación.
+ **APP - Capa de presentación de la aplicación.** En esta sección es donde tenemos los Views y Viewmodels para el diseño de las páginas web con DotVVM.

Muy bien, con esto en cuenta, ahora analicemos los pasos que debemos seguir en estos tres proyectos para la elaboración de nuestro portal web. 

###**DAL – Capa de acceso de datos**

Este primer proyecto corresponde a una librería de clases. En este sentido, como primer punto vamos a relacionar a nuestro proyecto con la base de datos en SQL Server. Para ello, podemos utilizar Entity Framework, un marco de asignación relacional de objetos (ORM) diseñado para crear aplicaciones de acceso a datos mediante la programación en un modelo de aplicación conceptual, en lugar de programar directamente un esquema de almacenamiento relacional.

En Entity Framework existen dos enfoques, el primero Code-First, el cual nos permite generar la base de datos a través de clases, y el segundo, Database-First, que nos permite generar las clases de entidades desde una base de datos existente. Como es de esperarse, en este caso utilizaremos el enfoque Database-First. Para cumplir con este objetivo, será necesario instalar tres paquetes Nuget:

+ `Microsoft.EntityFrameworkCore.Design`
+ `Microsoft.EntityFrameworkCore.Tools`
+ `Microsoft.EntityFrameworkCore.SqlServer`

Luego necesitaremos insertar un comando desde la consola de administración de paquetes. Esta consola la podemos activar desde el Menú de opciones -> Ver -> Otras Ventanas -> Consola de Administración de Paquetes.

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

En esta consola insertaremos el siguiente comando:

```
Scaffold-DbContext "Server=YOUR_SERVER; Database=DATABASE_NAME; Username=YOUR_USERNAME; Password=YOUR_PASSWORD Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir DAL/Entities
```

Donde:

+ **Server**, es el nombre del servidor de donde se encuentra la base de datos. Ejemplo: localhost.
+ **Username**, nombre de usuario de la base de datos.
+ **Password**, contraseña del usuario que va a acceder a la base de datos. 
+ **Database**, nombre de la base de datos. 

Asimismo,

+ **-OutputDir**, con esta abreviación podemos indicar donde serán generadas las clases de las entidades.

+ **-Table**, abreviación adicional en caso de que se deseen indicar las tablas especificas a generar en nuestra capa de acceso de datos. 

Al introducir el comando en esta consola, tendremos algo como esto:

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

Con estos pasos ya tenemos lista la conexión y las configuraciones necesarias para trabajar con la base de datos SQL Server en ASP.NET con la ayuda de Entity Framework. 

###**BL – Capa lógica de negocios**

Ahora nos toca definir los modelos y crear los servicios para manejar la lógica de nuestra aplicación. En este caso, crearemos una segunda librería de clases para tener un listado general de las entidades (`Event`, `Organizer`, `Session`, `Speaker`, `Sponsor`) y la información específica de cada una de ellas.

En la solución dentro de Visual Studio 2019 a la final tendremos algo como esto:

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

Aquí existen dos cosas importantes por mencionar. Como primer punto, en la clase EventId se puede controlar el identificador del evento con el que queremos trabajar. En este caso, la base de datos está diseñada para que podamos trabajar con varios eventos a la vez, por lo cual, en esta sección podemos especificar el identificador del evento al que queremos hacer referencia (a futuro podríamos controlar este identificador a nivel de interfaz web). 

```csharp
public class EventId
{
    private static EventId instance { get; set; } = null;

    public int Id { get; set; } = 1;

    private EventId() { }

    public static EventId GetInstance()
    {
        if (instance == null) {
            instance = new EventId();
        }

        return instance;
    }
}
```

Un segundo aspecto por considerar es que la mayoría de los servicios implementan operaciones CRUD (*Create*, *Read*, *Update*, *Delete*) para tratar los datos para cada uno de los casos. Por ejemplo, para los Organizadores, los métodos son los siguientes:

+ `GetAllOrganizersAsync()`.
+ `GetOrganizerByIdAsync(int IdOrganizer)`.
+ `InsertOrganizerAsync(OrganizerDetailModel Organizer)`.
+ `UpdateOrganizerAsync(OrganizerDetailModel Organizer)`.
+ `DeleteOrganizerAsync(int IdOrganizer)`.

Con esto mencionado, ahora veamos el diseño de nuestro dashboard con DotVVM. 

###**PL – Capa de presentación**

Para el diseño del portal web, aquí es donde DotVVM entra en acción. Cada página en DotVVM consta de dos archivos:

+ **Una View**, que se basa en la sintaxis HTML y describe cómo se verá la página. 
+ **Un ViewModel**, 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 Views y cuatro ViewModels principales para las secciones en el dashboard:

+ **Default**: será la página principal, en este caso solo se visualizará el menú de opciones. 
+ **Create**: una página conformada por un formulario para crear un nuevo registro. 
+ **Detail**: para ver a detalle de un registro. 
+ **Edit**: para modificar la información de un registro o eliminarlo. 
+ **List**: para visualizar el listado de registros para un caso en particular. 

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

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

A continuación, veamos algunos de los componentes principales de este portal administrativo. 

**A. Masterpage**

En DotVVM, las páginas principales o páginas maestras son conocidas como Masterpage, cuyos archivos tienen una extensión `.dotmaster`. En este caso, esta página será de utilidad para establecer nuestro esqueleto HTML, importar nuestros archivos CSS & JavaScript, y definir los contenidos que serán visibles en todas las páginas hijas (listados, formularios, registros).  

El `header` de nuestro HTML se verá así:

```html
<head>
    <meta charset="utf-8" />
    <title>Event Admin</title>

    <meta content='width=device-width, initial-scale=1.0, shrink-to-fit=no' name='viewport' />
    <!--     Fonts and icons     -->
    <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">
    <!-- CSS Files -->

    <dot:RequiredResource Name="Styles" />

</head>
```

Una etiqueta particular es la que se encuentra bajo la sentencia `<dot:RequiredResource Name="Styles" />`. En DotVVM, dentro de la clase de configuración llamada `DotvvmStartUp`, bajo el método `ConfigureResources`, podemos especificar la ubicación de los archivos CSS de nuestro portal administrativo para que se encuentren organizados en esta sección:

```csharp
private void ConfigureResources(DotvvmConfiguration config, string applicationPath)
{
    config.Resources.Register("Styles", new StylesheetResource()
    {
        Location = new UrlResourceLocation("~/assets/css/material-dashboard.css?v=2.1.2")
    });
}
```

El `MasterPage` con su cuerpo HTML, se visualizará de la siguiente manera:

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

Donde, 

+ **Sección 1**, es el header del HTML donde se encuentra el título de la página, las fuentes CSS y archivos JavaScript referenciados, y otras especificaciones. 
+ **Sección 2**, corresponde al logotipo de la página, en otras palabras, el título dentro de esta. 
+ **Sección 3**, es el menú de opciones del portal. 
+ **Sección 4**, es el apartado donde se visualizará todo el contenido de las páginas hijas (listado de speakers, sponsors, sessions, organizers; y los formularios de creación y modificación para cada uno de estos).

En el portal, visualmente los resultados de acuerdo con la numeración de las secciones especificadas previamente son los siguientes:

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

Para el análisis de las páginas hijas, veamos dos casos de ejemplo, el listado de los organizadores y un formulario de registro para dichos organizadores.

**B. List – Organizers** 

Hasta este punto hemos podido analizar la estructura establecida que tendrá nuestro portal, ahora veamos una de las páginas hijas o también llamadas páginas de contenido. 

El objetivo de esta primera página tiene como objetivo visualizar a través de una tabla los organizadores que se encuentren registrados en la base de datos. En este sentido, en la primera parte de esta página nos encontramos con el ViewModel:

```csharp
public class ListViewModel : MasterPageViewModel
{
	public string Title { get; set; }
	public string Subtitle { get; set; }

	private readonly OrganizerService OrganizerService;

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

	public int ContUsers { get; set; }

	public ListViewModel(OrganizerService OrganizerService)
	{
		Title = "Organizer";
		Subtitle = "In this section you can see the list of organizers registered in the database.";

		this.OrganizerService = OrganizerService;
	}

	public override async Task PreRender()
	{
		Organizers = await OrganizerService.GetAllOrganizersAsync();

		await base.PreRender();
	}
}
```

En esta clase del ViewModel definimos el título y subtitulo que va a tener la página hija. Asimismo, tendremos una instancia de `OrganizerService`, la cual nos permitirá acceder a los métodos para recuperar un listado de los organizadores registrados de la base de datos a través del servicio `Organizer` (implementado en el BL).

En esta misma clase podemos ubicar la definición `List<OrganizerListModel> Organizers` de tipo `OrganizerListModel` (definido en las clases de los modelos en el BL), que tendrá el listado de los organizadores (`IdOrganizer`, y `Name`) para cargarlos en una tabla en la página principal de la sección de organizadores. En este apartado, una declaración interesante es `[Bind(Direction.ServerToClient)]`. Este tipo de propiedades permiten especificar qué 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 los organizadores, 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. 

Finalmente en el Viewmodel de List de organizadores, tenemos el método `PreRender()`, que permite realizar cierto tipo de operaciones que serán llevadas a cabo al momento de cargar la página. En este caso, se realizará una consulta a la base de datos a través de la llamada de uno de los métodos del servicio, en este caso `OrganizerService.GetAllOrganizersAsync()` para recuperar el listado de los organizadores en una colección Organizers de tipo `OrganizerListModel`. 

La segunda parte corresponde a la View de esta página. 

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

En la primera parte nos encontramos con la sentencia `<dot:Content ContentPlaceHolderID="MainContent">`, donde se especifica el identificador de esta página de contenido, el cual está siendo referenciado desde el MasterPage a su vez. 

En la segunda parte tenemos un navbar donde se muestra el título y subtitulo de esta página de inicio; y a su vez un botón que redirigirá a un formulario para crear un nuevo organizador: 

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

Finalmente, en un tercer apartado tenemos un contador del número de organizadores registrados y la tabla donde se listan estos registros.

En la sección del contador solo se muestra un contador de la colección `Organizers` establecida en el ViewModel:

```csharp
<p class="card-category"><b>Number of registered organizers</b></p>
<h2 class="card-title">
    {{value: Organizers.Count}}
</h2>	
```

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

La tabla de los organizadores se podrá visualizar de la siguiente manera:

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

Esta tabla es diseñada a través de un 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 el DataSource: `DataSource="{value: Organizers}"`, el cual permite especificar la fuente de datos, en este caso hacemos referencia al listado de organizadores: `Organizers`, el cual fue definido en el Viewmodel como vimos anteriormente.  

```html
<dot:GridView DataSource="{value: Organizers}" class="table">
    <Columns>
        <dot:GridViewTextColumn ValueBinding="{value: IdOrganizer}" HeaderText="ID" HeaderCssClass="text-primary" CssClass="text-primary" />
        <dot:GridViewTextColumn ValueBinding="{value: Name}" HeaderText="Name" HeaderCssClass="text-primary" />
        <dot:GridViewTemplateColumn>
            <dot:RouteLink Text="" RouteName="DetailOrganizer" Param-IdOrganizer="{{value: IdOrganizer}}" class="btn btn-primary btn-link btn-sm">
                <i class="material-icons">visibility</i> Detail
            </dot:RouteLink>
        </dot:GridViewTemplateColumn>
    </Columns>
</dot:GridView>
```
Siguiendo con nuestro análisis, en el `GridView` tenemos las columnas `IdOrganizer`, y `Name` de los organizadores, 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 en detalle el registro de un usuario en particular según su ID: 

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

```

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("DetailOrganizer", "DetailOrganizer/{IdOrganizer}", "Views/Organizers/Detail.dothtml");
```

**C. Formulario de registro – Organizers**

Dentro del portal, existen otras opciones para crear, modificar y eliminar un registro de un organizador determinado. En este caso, para generalizar, vamos a analizar la página de Crear Organizador. Al igual que el `List`, la página `Create` es una que será hija del `MasterPage`. 

Como su nombre lo indica, esta página tiene como objetivo crear a un nuevo organizador a través de un formulario dentro del portal web. El resultado es el siguiente:

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

En este caso, en el formulario todos los componentes corresponden a cajas de texto para el ingreso de datos. Analicemos uno de ellos, por ejemplo, el campo `Name` en el View será de la siguiente manera:

```csharp
<div class="col-md-6">
    <div class="form-group">
        <label class="">Name</label>
        <dot:TextBox Text="{value: Organizer.Name}" class="form-control" />
    </div>
</div>
```

Aquí podemos comentar dos cosas. La primera, que para las cajas de texto podemos utilizar un componente de DotVVM que nos permita asociar un `TextBox` con un atributo en el ViewModel. 

```csharp
public OrganizerDetailModel Organizer { get; set; } = new OrganizerDetailModel();
```

Asimismo, podemos acompañar a este `TextBox` con un control llamado `Validator`, el cual nos permita validar ciertas características, por ejemplo, que el campo no pueda ser registrado como nulo. En estos dos componentes podemos agregar especificaciones adicionales, tales como los estilos correspondientes para cada caso. 

El botón para enviar seguirá la misma lógica: 

```csharp
<dot:Button Text="Insert Organizer" Click="{command: AddOrganizer()}" class="btn btn-primary pull-right" />
```

Este botón, establecido a través de un componente de DotVVM, está llamando a una función establecida en el ViewModel para agregar a un usuario según los datos insertados en el formulario. Esta función se encuentra estructurada de la siguiente manera:

```csharp
public async Task AddOrganizer()
{
    await OrganizerService.InsertOrganizerAsync(Organizer);
    Context.RedirectToRoute("Organizer");
}
```

En este caso, al insertar el registro satisfactoriamente, desde el ViewModel podemos redirigirnos a la página de `List` (página con el listado de organizadores registrados) y visualizar al nuevo organizador insertado en la base de datos y visualizado en el listado general.

Como se mencionó anteriormente, las páginas de detalle y edición siguen la misma lógica de diseño en este caso; de igual manera para las secciones de `Speakers`, `Sessions`, y `Sponsors`. 

A continuación, podemos ver algunas capturas de pantalla adicionales para los formularios de registro de `Speakers`, `Sessions` y `Sponsors` respectivamente.

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

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

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

###**¿Qué sigue?**

Con este articulo tutorial hemos aprendido de manera general como controlar los datos de nuestros eventos, ya sea para conferencias o hackatones, para encuentros virtuales o en persona, a través de ASP.NET 5 & DotVVM.  

El código de este tutorial lo podemos encontrar en el siguiente repositorio en GitHub: [Event Admin]
(https://github.com/esdanielgomez/EventAdmin).

En un siguiente artículo, podremos aprender como utilizar los mismos datos almacenados en la base de datos con SQL Server, pero esta vez para representarlos en un portal web donde los usuarios o asistentes puedan conocer de las sesiones, los speakers, y todo lo demás. Para el caso del listado de los organizadores, en este segundo portal podríamos tener algo como esto:

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

Próximamente también tendremos la explicación de este portal para nuestros asistentes, y por supuesto, el código fuente también. :)

**Recursos adicionales:**

En la actualidad son muchas las aplicaciones que podemos construir en el ecosistema de .NET, y aún más específicamente en el área del desarrollo páginas web. En este sentido, a continuación, se encuentran algunos recursos adicionales para seguir adquiriendo conocimientos en este campo:

+ [.NET 5: Despliegue de aplicaciones web con Azure App Service](https://dev.to/esdanielgomez/net-5-despliegue-de-aplicaciones-web-con-azure-app-service-3i18).
+ [Implementación de aplicaciones web con ASP.NET Core y DotVVM en AWS Elastic Beanstalk](https://dev.to/dotvvm/implementacion-de-aplicaciones-web-con-asp-net-core-y-dotvvm-en-aws-elastic-beanstalk-1ceg).
+ [Trabajando con servicios web a través de ASP.NET Core y DotVVM](https://dev.to/dotvvm/trabajando-con-servicios-web-a-traves-de-asp-net-core-y-dotvvm-4c76).

Muchas gracias por leer, espero que este demo pueda serte de utilidad. Si tienes alguna pregunta o alguna idea que necesites discutir, será un gusto poder colaborarte y juntos intercambiar conocimientos entre sí.

¡Nos vemos en [Twitter](https://twitter.com/esDanielGomez)! O si gustas también puedes escribirme por [Telegram](https://t.me/esdanielgomez). :)