¡Os traigo buenas noticias! Me complace anunciaros que por fin está disponible en el catálogo de CampusMVP el curso en el que he estado trabajando intensamente durante varios meses, y que me consta que muchos estabais esperando: Desarrollo de aplicaciones Web con Blazor.
Su creación ha sido bastante laboriosa porque queríamos ofreceros el mejor y más completo curso sobre Blazor que podéis encontrar en este momento, y no es fácil conseguirlo cuando se trata de una tecnología tan reciente, de la que aún no existe tanta documentación, bibliografía y ejemplos como las hay de otras tecnologías con más años de recorrido. Ha sido duro, pero tanto un servidor como el equipo de producción de CampusMVP que ha participado en su creación, estamos orgullosos del resultado y firmemente convencidos de que lo que hemos logrado: un recorrido práctico, minucioso y profundo del que es, sin duda es el framework que cambiará vuestra forma de desarrollar aplicaciones para la web.
En este post vamos a intentar resolver las siguientes cuestiones:
- ¿Qué es Blazor?
- ¿Me interesa aprender a desarrollar con Blazor?
- Hace poco aprendí ASP.NET Core, ¿significa esto que ya no me valen estos conocimientos?
- ¿En qué consiste el curso de desarrollo con Blazor?
- ¿Cuáles son los contenidos del curso?
- ¿Qué conocimientos previos necesito para seguir el curso?
- Me convence, ¿cuándo empezamos?
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcore, autobombo, blazor, curso
ConfigureServices()
de la clase Startup
con una línea como la siguiente:public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
}
Esto era todo lo que necesitábamos para poder utilizar cualquiera de estas tecnologías en nuestra aplicación. Sin embargo, a partir de ASP.NET Core 3.0, nuestro intellisense se vio inundado de opciones adicionales como AddControllers()
, AddRazorPages()
o AddControllersWithViews()
.¿Para qué sirven, y por qué ha sido necesario introducir estos nuevos extensores?
Hoy vamos a hablar de una de eso pequeños descubrimientos: el rutado dinámico de controladores. Introducido en ASP.NET Core 3, se trata de una característica que no ha sido especialmente publicitada ni comentada, pero puede ser de utilidad en algunas ocasiones, pues permite interferir en la forma de interpretar las rutas de las peticiones entrantes con objeto de decidir en cada caso cómo deben ser procesadas.
El hecho de que en el equipo destino esté preinstalado el runtime es muy interesante, entre otras cosas porque permite asegurar de antemano que en él se encontrarán todas las dependencias (frameworks, bibliotecas, paquetes, metapaquetes) necesarios para una correcta ejecución. Por tanto, para distribuir nuestra aplicación sólo debemos generar lo relativo a nuestro código, el resto ya estará allí.
Esta forma de publicar aplicaciones se denomina framework-dependent, pues dependen de que determinados componentes del framework estén instalado en el destino.Por ejemplo, el paquete de publicación de una aplicación de consola prácticamente vacía, que únicamente muestra el mensaje "Hello world!", ocuparía solo 175K:
D:\MyConsoleApp\output>dir
El volumen de la unidad D es Datos
El número de serie del volumen es: 8CBC-81E3
Directorio de D:\MyConsoleApp\output
09/02/2020 18:47 <DIR> .
09/02/2020 18:47 <DIR> ..
09/02/2020 18:46 428 MyConsoleApp.deps.json
09/02/2020 18:46 4.608 MyConsoleApp.dll
09/02/2020 18:46 169.984 MyConsoleApp.exe
09/02/2020 18:46 668 MyConsoleApp.pdb
09/02/2020 18:46 154 MyConsoleApp.runtimeconfig.json
5 archivos 175.842 bytes
2 dirs 463.058.874.368 bytes libres
D:\MyConsoleApp\output>_
Otra ventaja de este tipo de distribución es que es cross platform pura, es decir, podemos copiar los archivos a cualquiera de los sistemas operativos soportados y, siempre que dispongan del runtime, nuestra aplicación podrá correr sobre ellos sin problema.Y todo esto está muy bien, pero, ¿qué pasa si quiero crear una aplicación portable, de forma que pueda distribuirla y ejecutarla sin necesidad de que el equipo destino tenga nada preinstalado?
Pues eso es lo que veremos en este post ;)
En este post vamos a ver un par de formas de sacar provecho de esta descripción:
- En primer lugar, usaremos Swagger UI para generar un sitio web interactivo de documentación y pruebas de nuestra API.
- Después, veremos cómo generar desde Visual Studio código cliente para acceder a la API.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, openapi, swagger, webapi
El objetivo de OpenAPI es conseguir una fórmula normalizada para describir las capacidades de un servicio REST, independientemente de los lenguajes o tecnologías con las que sea implementado. Esta descripción, normalmente especificada en formato JSON o YAML, permite a clientes (sean humanos o máquinas) descubrir servicios, comprender sus capacidades y conocer sus detalles de funcionamiento sin necesidad de tener acceso a su código fuente o documentación textual.
Esta especificación formal abre interesantes posibilidades, pues a partir de la definición estandarizada de servicios es posible, entre otros,
- disponer de herramientas de diseño y modelado de servicios,
- generar automáticamente páginas de documentación,
- generar automáticamente código cliente y servidor para dichos servicios para distintos, frameworks y lenguajes de programación,
- generar automáticamente código de testing y validaciones,
- o generar automáticamente mocks de servicios.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, openapi, swagger, webapi
Sin embargo, es menos conocido el hecho de que estas bibliotecas pueden incluir también recursos estáticos como imágenes, hojas de estilo o scripts, lo que resulta bastante interesante a la hora de crear componentes totalmente autosuficientes y muy reutilizables.
En este post vamos a ver cómo crear una RCL redistribuible que definirá el tag helper
<mario>
, cuya inclusión en una página hará que ésta muestre el conocido personaje correteando por la pantalla, como se muestra en la siguiente captura:Días atrás veíamos lo sencillo que resultaba crear un servicio gRPC que hacía uso de streaming unidireccional de mensajes, en sentido servidor-cliente. Como recordaréis, en este post implementábamos un servicio generador de números al que los clientes podrían conectarse para recibir una secuencia de enteros a intervalos periódicos.
Hoy vamos a complicar algo el escenario, pues veremos una opción aún más potente: la creación de streaming bidireccional, es decir, cómo cliente y servidor pueden enviar mensajes al lado opuesto de forma asíncrona, una vez establecida la comunicación entre ambos.
Para ello, implementaremos un sencillo juego, con las siguientes funcionalidades:
- El lado cliente, una aplicación de consola, abrirá una conexión con el servicio. En ese momento, el servidor elegirá un número al azar (1-100) y lanzará un mensaje de bienvenida, que deberá ser mostrado en el lado cliente.
- Desde el lado cliente solicitaremos al usuario un número entero, que enviaremos al servidor por el stream ascendente.
- El servidor recibirá el número y lo comparará con el que tiene almacenado, enviando por el canal descendente el resultado de la comparación.
- El proceso finalizará cuando el número sea encontrado.
Hace bien poco hablábamos de la introducción en .NET/C# de la interfaz
IAsyncEnumerable
, y de las interesantes posibilidades que abría vistas a la producción y consumo de streams de mensajes.También hace unos días dimos un repaso al soporte para la implementación de clientes y servidores gRPC lanzado con ASP.NET Core 3. En dicho post hacíamos una pequeña introducción al desarrollo de este tipo de servicios, basados en HTTP/2 y el estándar protobuf.
Como ya comentábamos entonces, a diferencia de los tradicionales servicios tipo REST (HTTP/JSON), gRPC soporta el intercambio de datos en modo streaming, tanto unidireccional como bidireccionalmente. Es decir, en el escenario más complejo podríamos abrir un canal gRPC a través del cual un cliente podría ir enviando paquetes de información de forma asíncrona, y al mismo tiempo utilizarlo para recibir un flujo de datos continuo desde el servidor.
Hoy vamos a ver cómo se combinan estas características viendo un ejemplo de implementación de un servicio gRPC que utiliza streaming unidireccional, en sentido servidor-cliente.
Para ello crearemos un servicio generador de números en streaming que funcionará de la siguiente forma:
- Los clientes invocarán un procedimiento del servidor suministrándole un número entero inicial y un delay expresado en segundos.
- Como respuesta, el servidor creará un stream por el que irá enviando, según la periodicidad indicada, números consecutivos partiendo del especificado en la llamada inicial.
- El proceso finalizará, cerrando en stream, cuando el servidor haya generado 10 números, o bien cuando el cliente sea detenido.
Como recordaréis, esta directiva era bastante útil para simplificar el código de las vistas y mejorar su legibilidad, pues permitía crear funciones reutilizables que mezclaban HTML y código de servidor, como en el siguiente ejemplo:
@* File: Test.cshtml *
@Multiplication(2)
@Multiplication(3)
@helper Multiplication(int x)
{
<h2>Multiplication table of @x</h2>
<ul>
@for (var i = 1; i <= 10; i++)
{
<li>@x * @i = @(x * i)</li>
}
</ul>
}
Hasta la versión 2.2, teníamos que conformarnos con apaños como los descritos en aquél post si queríamos emular lo que llevábamos tantos años utilizando con MVC 5 y anteriores. Y también entonces comentamos que había ciertas posibilidades de que en algún momento volviera a la vida, y éstas se han materializado, por fin, en ASP.NET Core 3.0.Aunque quizás más bien habría que hablar de reencarnación...
SetCompatibilityVersion()
que veíamos en la plantilla de proyectos ASP.NET Core MVC y Razor Pages desde la versión 2.1:public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Esta llamada era incluida de serie en los nuevos proyectos ASP.NET Core desde la versión 2.1, pero en la versión 3.0 ya no aparece. Y probablemente también os llame la atención a quienes ya habéis trabajado con ASP.NET Core 2.x, así que he pensado que sería interesante comentarlo por aquí.En este post echaremos un vistazo a gRPC y su uso en la nueva versión del framework.
Bueno, la cuestión es que en ASP.NET Core 3.0 ha vuelto a cambiar, y esperemos que sea por última vez ;)
Veamos en qué han consistido estos cambios.
Seguramente estáis al tanto de que Microsoft acaba de anunciar la disponibilidad de .NET Core 3.0 y tecnologías relacionadas, incluyendo ASP.NET Core 3.0.
Pues bien, me complace anunciaros que, sólo un día más tarde, desde CampusMVP hemos lanzado el nuevo curso Desarrollo Web con ASP.NET Core 3 MVC.
Se trata de una gran revisión del curso de desarrollo con ASP.NET Core MVC que ha formado ya con éxito a cientos de desarrolladores de todo el mundo, en la que hemos introducido los cambios y novedades que han venido de la mano de la nueva versión del framework.
En este post intentaremos responder a las siguientes preguntas:
Publicado por José M. Aguilar a las 8:09 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, autobombo, curso
- .NET Core 3.0
- C# 8
- ASP.NET Core 3.0
- Blazor server-side
- Entity Framework Core 3.0
- Entity Framework 6.3 (sí, ¡compatible con .NET Core!)
- SignalR 3.0
- ML.NET
- Soporte WinForms y WPF para .NET Core 3
- Visual Studio 2019 16.3
- Simplificación del archivo de proyecto
.csproj
- Uso del host genérico
- Introducción del endpoint routing
- Mayor modularidad en el registro de servicios de MVC
- Nuevo serializador/deserializador JSON (bye bye, JSON.NET!)
- Compatibilidad exclusivamente con .NET Core (bye bye, target .NET Framework!)
- Limpieza de
Microsoft.AspNetCore.App
- Cambios en la compilación de vistas
- Soporte para gRPC
- Y, por supuesto, muchas otras mejoras...
Novedades de ASP.NET Core 3.0
¡No os lo perdáis!
Publicado en: www.variablenotfound.com.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, noticias, novedades
El problema
En el escenario concreto que planteaba nuestro querido lector era que quería proporcionar, a los componentes que necesitaban acceder a datos en su aplicación, un acceso limitado al contexto de datos de Entity Framework.Para ello, por un lado definía una interfaz parecida a la siguiente, que proporcionaba acceso a los repositorios:
public interface IUserRepositories
{
DbSet<User> Users { get; }
...
}
Por otra parte, definía otra interfaz que era la que permitía comprometer cambios (adiciones, supresiones, modificaciones) realizadas en el contexto de datos:public interface IUnitOfWork
{
int SaveChanges();
}
La idea de esta separación es que si un componente necesitaba exclusivamente consultar datos relativos a usuarios, sólo recibiría mediante inyección de dependencias la instancia de IUserRepositories
, mientras que si necesitaba persistir datos, recibiría adicionalmente un objeto IUnitOfWork
, por ejemplo:public class UserServices
{
...
public UserServices(IMapper mapper, IUserRepositories userRepos, IUnitOfWork uow)
{
_mapper = mapper;
_userRepos = userRepos;
_uow = uow;
}
public async Task Update(int id, UserDto user)
{
var user = await _userRepos.Users.FirstOrDefaultAsync(u=>u.Id == id);
if(user == null)
throw new Exception();
_mapper.Map(userDto, user);
await _uof.SaveChangesAsync();
}
}
Bien, pues el problema lo tenía precisamente a la hora de registrar las dependencias de forma apropiada.En este post vamos a ver cómo conseguirlo, y un caso práctico de uso de esta técnica en un escenario muy frecuente.
Publicado por José M. Aguilar a las 8:30 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, patrones, trucos
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// Hacer algo antes de pasar el control al siguiente middleware
await _next(context); // Pasar el control al siguiente middleware
// Hacer algo después de ejecutar el siguiente middleware
}
}
Estas clases no heredan de ninguna otra ni implementan interfaces proporcionadas por el framework, aunque atienden a convenciones simples, como la recepción en el constructor del delegado al siguiente middleware en el pipeline, o la existencia de un método Invoke()
o InvokeAsync()
, que es donde introduciremos nuestra lógica, recibiendo el contexto HTTP.La ausencia de interfaces o clases base aporta flexibilidad, pero elimina las ayudas propias del tipado fuerte y puede ser fuente de problemas si no atendemos a estas convenciones con cuidado. Es decir, si en lugar del método
Invoke()
por error escribimos Invke()
, nada fallará en compilación. Tendremos que esperar a ejecutar la aplicación para que explote.También es importante tener en cuenta que una clase middleware sólo es instanciada una vez, cuando la aplicación está arrancando; luego, en cada petición será ejecutado su método
Invoke()
sobre la misma instancia, lo que es a priori muy poco intuitivo y puede causarnos algún dolor de cabeza si no somos cuidadosos.Como recordaréis, se trata de una mejora en el módulo ASP.NET Core (ANCM) que hace que las aplicaciones se ejecuten directamente dentro del worker del servidor web (w3wp.exe), evitando las llamadas internas producidas cuando IIS actuaba como un mero proxy inverso.
Por verlo gráficamente, el siguiente diagrama muestra la arquitectura tradicional de un sistema ASP.NET Core funcionando sobre IIS. En el modelo out-of-process utilizado hasta ASP.NET Core 2.1, cada petición realizada desde el exterior era capturada por IIS, que a su vez lanzaba una petición HTTP local con los mismos contenidos hacia Kestrel, que era quien la procesaba ejecutando nuestro código. La respuesta generada desde Kestrel era enviada de vuelta a IIS como respuesta a la petición, quien la retornaba al agente de usuario:
En este modelo de funcionamiento, por tanto, cada petición HTTP entrante generaba otra petición HTTP interna que, aunque estuviera dentro de la misma máquina, imponía una penalización importante en el rendimiento de las aplicaciones.
El hosting in-process, aparecido con ASP.NET Core 2.2, cambia las reglas del juego eliminando esas peticiones HTTP internas y los retardos que implicaban, lo que ha posibilitado el espectacular incremento en el rendimiento que su uso ofrece. Ahora, el módulo para IIS ejecuta internamente la aplicación y se comunica con ella de forma directa:
Este es el modo por defecto de los nuevos proyectos ASP.NET Core creados usando las plantillas estándar, algo que podemos claramente ver si echamos un vistazo al archivo
.csproj
de un proyecto recién creado, ya sea desde Visual Studio o desde .NET Core CLI: <PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
Si tenéis un proyecto ASP.NET Core 2.1 o anterior y lo actualizáis a 2.2, tendréis que añadir el elemento <AspNetCoreHostingModel>
de forma manual para usar este modelo de ejecución.
Sin embargo, estas mejoras espectaculares no son gratis del todo, y creo que es interesante conocer un poco mejor lo que implica asumir este nuevo modo de funcionamiento de nuestras aplicaciones y responder a algunas preguntas que podríamos hacernos por el camino._ViewImports.cshtml
y _ViewStart.cshtml
se emplean para introducir directivas y código de inicialización para todas las vistas o páginas que se encuentren por debajo en la estructura de carpetas del proyecto.Es decir, si el archivo
_ViewImports.cshtml
se encuentra en la carpeta /Views
, todas las vistas que hay por debajo heredarán las directivas que hayamos especificado en su interior, de la misma forma que /Pages/_ViewImports.cshtml
afectara a las páginas presentes en la carpeta /Pages
y descendientes.Pues bien, hace unos días, un alumno del curso de ASP.NET Core en CampusMVP (que, por cierto, ha sido recientemente actualizado a la versión 2.2 del framework) planteaba una duda sobre un escenario algo más complejo. Si una aplicación tenía vistas en la carpeta
/Views
, también tenía vistas en áreas (carpeta /Areas
), e incluso pudiera tener algunas páginas Razor en /Pages
, la pregunta era si existía algún mecanismo para hacer que todas las vistas o páginas definidas en dichas carpetas compartieran directivas (como using
o importaciones de tag helpers) sin tener que duplicar este código en sus respectivos _ViewImports.cshtml
para cada una de ellas.A priori pensé que quizás el planteamiento sería retocar ligeramente el motor de vistas, pero, tras estudiarlo un rato, vi que el tema era mucho más sencillo :)