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, nuestro primer objetivo es implementar la lógica de la aplicación de un sistema determinado que acceda a la base de datos y provea servicios web para la manipulación de estos datos. A partir de esto, el segundo objetivo es emplear estos servicios, con la finalidad de preocuparse únicamente por el diseño del de las páginas web correspondientes en esta instancia.
Servicios web con SOAP
Los servicios web SOAP, o servicios web "big", son aquellos que utilizan mensajes XML para intercomunicarse, que siguen el estándar SOAP (Simple Object Access Protocol), un lenguaje XML que define la arquitectura y formato de los mensajes. Dichos sistemas normalmente contienen una descripción legible por una máquina de la descripción de las operaciones ofrecidas por el servicio, escrita en WSDL (Web Services Description Language), el cual, es un lenguaje basado en XML para definir las interfaces sintácticamente.
Idea general de la solución:
Este artículo cuenta con dos objetivos principales. El primero es sobre la construcción de una aplicación orientada a servicios con WCF – Windows Communication Foundation, que permitirá trabajar con datos alojados en una base de datos MySQL como mensajes asíncronos de un punto de conexión de servicio a otro. El segundo objetivo consiste en utilizar estos servicios en un segundo proyecto a través de una página web implementada con DotVVM sobre ASP.NET Core.
Nota: el código fuente empleado en este articulo está disponible en GitHub en los siguientes repositorios: WCF (Windows Communication Foundation) Service on ASP.NET Framework y Consuming WCF Service in DotVVM with ASP.NET Core.
Actividades:
El articulo tendrá dos partes importantes:
- Parte 1: Implementación de un servicio web con SOAP
- Parte 2: Consumiendo servicios SOAP 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 Framework a través de Entity Framework.
- 3. Establecer el contrato del servicio y sus operaciones.
Como caso de estudio para este tutorial se manejarán los datos 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 está conformada por una única tabla llamada: User, con los atributos: Id
, FirstName
, LastName
, Username
, Password
y EnrrollmentDate
.
La sentencia SQL para la creación de la tabla User
es la siguiente:
CREATE TABLE `user` (
`Id` INT NOT NULL 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
);
Muy bien, con la base de datos lista ya podemos comenzar con la implementación del primer proyecto para el desarrollo de servicios SOAP.
Proyecto WCF Service Application
En Visual Studio 2019 lo primero que haremos es crear un nuevo proyecto de tipo WCF Service Application (dentro de la categoría C# - Web):
Con este proyecto crearemos el acceso a la base de datos, la definición de la interfaz del servicio y la implementación correspondiente, en este caso, para la entidad 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 con 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 esté 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.
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:
Comando:
Scaffold-DbContext "server=servername;port=portnumber;user=username;password=pass;database=databasename" MySql.Data.EntityFrameworkCore -OutputDir DAL/Entities -f
El resultado es el siguiente:
Aquí, la clase User
está definida de la siguiente manera:
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 y nos permitirá realizar las operaciones correspondientes sobre esta.
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 DTO - Objetos de Transferencia de Datos
Con el objetivo de transportar los datos entre los procesos para el manejo de la base de datos y las operaciones 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 definiremos una nueva carpeta dentro del proyecto llamada DTO y crearemos una clase llamada UserDTO
, cuyos atributos serán los mismos que la clase User definida en la sección Entities
anteriormente:
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; }
}
Contratos del servicio
Lo fundamental en un servicio web SOAP es el contrato que existe entre cliente y servicio, es decir, el documento WSDL. Por lo tanto, a la hora de crear servicios web es recomendable empezar definiendo dicho contrato. De esta forma tendremos mayor control sobre los datos que se serializan durante la invocación del servicio, con lo que podremos definir las estructuras de datos que consideremos más adecuadas para el intercambio. En este caso, definiremos las operaciones a implementar en el servicio web a través de una interface. El resultado será el siguiente:
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IService
{
[OperationContract]
List<UserDTO> Get();
[OperationContract]
UserDTO GetUserById(int Id);
[OperationContract]
bool InsertUser(UserDTO User);
[OperationContract]
void UpdateUser(UserDTO User);
[OperationContract]
void DeleteUser(int Id);
}
Luego debemos realizar la implementación de esta interface, en la cual, se encontrarán los métodos establecidos, junto con las operaciones necesarias para recibir y enviar los datos correspondientes. En este caso el archivo lleva por extensión .svc y el código de las operaciones serán de la siguiente manera:
Definición inicial:
public class Service : IService
{
private readonly DBContext DBContext = new DBContext();
...
A. Obtener el listado de todos los usuarios registrados
public List<UserDTO> Get()
{
return DBContext.User.Select(
s => new UserDTO
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
Username = s.Username,
Password = s.Password,
EnrollmentDate = s.EnrollmentDate
}
).ToList();
}
B. Obtener los datos de un usuario especifico según su Id
public UserDTO GetUserById(int Id)
{
return DBContext.User.Select(
s => new UserDTO
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
Username = s.Username,
Password = s.Password,
EnrollmentDate = s.EnrollmentDate
})
.FirstOrDefault(s => s.Id == Id);
}
C. Insertar un nuevo usuario
public bool 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);
DBContext.SaveChangesAsync();
return true;
}
D. Actualizar los datos de un usuario especifico
public void UpdateUser(UserDTO User)
{
var entity = DBContext.User.FirstOrDefault(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;
DBContext.SaveChangesAsync();
}
E. Eliminar a un usuario según su Id
public void DeleteUser(int Id)
{
var entity = new User()
{
Id = Id
};
DBContext.User.Attach(entity);
DBContext.User.Remove(entity);
DBContext.SaveChangesAsync();
}
El archivo de la interfaz (.cs) y de la implementación (.svc) se verán de la siguiente manera dentro de la solución de Visual Studio:
Con estos pocos pasos seguidos hasta el momento, el servicio SOAP para el manejo de datos de usuarios se encuentra listo para ponerse en ejecución.
Probar el servicio SOAP implementado
Para probar el servicio implementado disponemos de varias formas, una de ellas es situarnos en el archivo de la implementación del servicio SVC y ejecutar el proyecto. En esta ejecución, el Cliente de Prueba WCF será ejecutado y será el que nos ayudará a probar las funciones implementadas.
En un primer ejemplo podemos probar el método Get() y visualizar el listado de usuarios registrados, tal y como podemos apreciar en la siguiente imagen:
Otro caso puede consistir en obtener a un usuario en específico según su Id, para esto en seleccionaremos a la operación GetUserById(), y en esta selección el cliente de prueba WCF nos indicara los parámetros de entrada que debemos especificar. El resultado es el siguiente:
Como ejemplo adicional, podemos analizar el caso de la creación de un nuevo usuario a través de la operación InsertUser(), junto con sus parámetros de entrada correspondientes:
Muy bien, otra de las formas con las que podemos probar nuestros servicios SOAP implementados es a través de la herramienta Postman. En este caso, ejecutaremos el proyecto WCF situándonos en cualquier archivo dentro de Visual Studio 2019 que no sea el de la implementación del servicio SVC. Al realizar esta ejecución se abrirá el navegador con la información de este servicio.
En Postman tendremos que especificar tres cosas, por un lado, el enlace del servicio, en este ejemplo será: http://localhost:16260/Service.svc, cuya selección será en un método POST. Luego debemos especificar los headers según la operación a probar, en este caso ejemplificaremos el consumo con la operación GetUserById():
Content-Type
: text/xml
SOAPAction
: http://Microsoft.ServiceModel.Samples/IService/GetUserById
Luego deberemos especificar el body XML con los parámetros de entrada a enviar, en este caso el Id del usuario:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetUserById xmlns="http://Microsoft.ServiceModel.Samples">
<Id>1</Id>
</GetUserById>
</s:Body>
</s:Envelope>
Finalmente, al realizar la solicitud al servicio SOAP obtendremos la respuesta XML. Los campos y los resultados en Postman se pueden visualizar en la siguiente imagen:
El llamado de las demás operaciones las veremos al utilizar estos servicios directamente desde una aplicación web con DotVVM.
Parte 2: Consumiendo servicios WCF con DotVVM
En esta segunda parte tendremos dos partes importantes:
- 1. Establecer los métodos para la comunicación con los servicios SOAP definidos anteriormente.
- 2.Implementar las vistas y los modelos de estas vistas para el desarrollo de la página web correspondiente.
El objetivo de estas páginas web consiste en dar a conocer los datos resultado, del consumo de las operaciones CRUD: crear, leer, actualizar y borrar establecidas en el contrato de servicio SOAP.
Proyecto DotVVM con ASP.NET Core
Para este nuevo propósito, en Visual Studio 2019 lo primero que haremos es crear un nuevo proyecto de tipo DotVVM Web Application (.NET Core):
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.
Establecer la referencia con WCF Web Service
Como primer punto lo que necesitaremos realizar, es establecer una referencia al servicio WCF implementado anteriormente. Para este objetivo dentro de la solución de Visual Studio, podemos dirigirnos a la sección Connected Services
y seleccionar Add Connected Service
:
En la sección de perfiles de servicios conectados seleccionaremos la opción: Microsoft WCF Web Service Reference Provider
:
A partir de esta selección el asistente de configuración de WCF Web Service Reference
será inicializado, y en esta sección deberemos especificar el URL con los metadatos WSDL del servicio SOAP, para esto nuestro proyecto WCF deberá estar en ejecución.
En este ejemplo, el URL correspondiente es: http://localhost:16260/Service.svc?wsdl. Al cargarlo, el asistente de configuración identificara al servicio en cuestión y las operaciones incluidas. En este caso, lo último por definir será el nombre del namespace en donde se generarán las clases y los archivos dentro del proyecto para acceder a este servicio.
Al seleccionar en Finalizar, el asistente realizara un proceso de scaffolding, es decir, generara los archivos correspondientes para acceder a este servicio.
Al finalizar la operación, veremos algunas clases y archivos como estos:
Modelos y lógica de la aplicación
A continuación, debemos implementar los modelos y los servicios que consumirán las operaciones implementadas en SOAP 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 ello, como primer punto definiremos los modelos:
A. UserListModel
public class UserListModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
B. UserDetailModel
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 la lógica de nuestra aplicación. En este caso tenemos el servicio de usuarios que nos permitirá establecer las operaciones CRUD de acuerdo con los contratos de servicio en SOAP.
Para la inicialización del servicio User, se puede comenzar con una definición como esta:
ServiceClient reference = new ServiceClient();
Esta sentencia corresponde a la instanciación de una de las clases generadas anteriormente. A partir de este objeto base para el consumo de servicios SOAP, a continuación, se muestran los métodos correspondientes:
A. Obtener el listado de todos los usuarios registrados
public List<UserListModel> GetAllUsers()
{
return reference.GetAsync().Result.Select(
s => new UserListModel
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName
}).ToList();
}
B. Obtener los datos de un usuario especifico según su Id
public async Task<UserDetailModel> GetUserByIdAsync(int Id)
{
var result = await reference.GetUserByIdAsync(Id);
return new UserDetailModel()
{
Id = result.Id,
FirstName = result.FirstName,
LastName = result.LastName,
Username = result.Username,
Password = result.Password,
EnrollmentDate = result.EnrollmentDate
};
}
C. Insertar un nuevo usuario
public async Task InsertUserAsync(UserDetailModel user)
{
UserDTO NewUser = new UserDTO() {
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Username = user.Username,
Password = user.Password,
EnrollmentDate = user.EnrollmentDate
};
await reference.InsertUserAsync(NewUser);
}
D. Actualizar los datos de un usuario especifico
public async Task UpdateUserAsync(UserDetailModel user)
{
UserDTO UserToUpdate = new UserDTO()
{
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Username = user.Username,
Password = user.Password,
EnrollmentDate = user.EnrollmentDate
};
await reference.UpdateUserAsync(UserToUpdate);
}
E. Eliminar a un usuario según su Id
public async Task DeleteUserAsync(int Id)
{
await reference.DeleteUserAsync(Id);
}
Vistas y vistamodelos
Ahora que ya se tienen definidos los métodos que consumen los servicios en formato XML, ahora solo nos queda 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.
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, 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 usuarios registrados.
- Create: una página conformada por un formulario para crear nuevos usuarios.
- 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, analicemos a detalle el View y Viewmodel de Default
y sus componentes.
Viewmodel del Default
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 = userService.GetAllUsers();
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> Students
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.
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
@viewModel DotVVM.ViewModels.DefaultViewModel, DotVVM
@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 estudiantes: 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.
Siguiendo con nuestro análisis, en el GridView
tenemos las columnas Id
, FirstName
y LastName
de los usuarios, pero adicionalmente, también podemos agregar 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 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 SOAP a través de un proyecto WCF, 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 el servicio SOAP y consumirlo a su vez. A continuación, podemos ver algunas capturas sobre la aplicación web implementada:
Crear un nuevo registro
Obtener el detalle de un registro especifico
Listado general de los usuarios
¿Qué sigue?
Con este articulo hemos aprendido paso a paso como implementar servicios SOAP 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.
El código fuente de los dos proyectos utilizados en este articulo están disponibles en los siguientes repositorios: WCF (Windows Communication Foundation) Service on ASP.NET Framework y Consuming WCF Service in DotVVM with ASP.NET Core.
Gracias por leer:
Espero que te haya gustado el artículo. Para estar al tanto sobre futuras contribuciones o si tienes alguna inquietud, puedes seguirme en Twitter: twitter.com/esDanielGomez.
¡Hasta pronto!
Top comments (0)