Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta trucos. Mostrar todas las entradas
Mostrando entradas con la etiqueta trucos. Mostrar todas las entradas
martes, 28 de mayo de 2019
Entity Framework Core Otra característica bastante interesante de Entity Framework Core es que podemos introducir lógica personalizada a la hora de cargar el valor de las propiedades a partir de los datos obtenidos desde el almacén, y viceversa, usando los conversores de valores, o value converters.

Esta capacidad abre posibilidades bastante interesantes,que no eran tan inmediatas (o en algunos casos, ni siquiera posibles de forma directa) en versiones "clásicas" de Entity Framework, o EF Core anterior a 2.1. Gracias a ella podremos, por ejemplo, tener en nuestra entidad una propiedad de tipo enum mapeada a una cadena de caracteres en el almacén de datos, o introducir cualquier lógica de transformación, como podría ser la desencriptación y encriptación de valores, a la hora de leer y persistir información.
martes, 14 de mayo de 2019
Entity Framework Core Seguimos viendo características novedosas de Entity Framework Core, y ahora llega el turno a otro detalle interesante si lo que buscamos es proteger nuestras entidades de modificaciones descontroladas y así asegurar en todo momento su consistencia: los constructores parametrizados.

En versiones anteriores a EF Core 2.1 y todos sus antecesores "clásicos", un problema que era difícilmente salvable era la necesidad de que en las entidades existiera un constructor público sin parámetros.

Esto tenía sentido, pues debíamos proporcionar al framework una vía para crear las instancias al materializarlas desde el almacén de datos. De hecho, al materializar, el marco de trabajo usaba dicho constructor para crear la instancia, y luego iba poblando las propiedades una a una utilizando sus setters (o estableciendo el valor de sus backing fields, como vimos en un post anterior)
martes, 7 de mayo de 2019
Entity Framework Core Para los desarrolladores que hemos utilizado Entity Framework 6.x y anteriores durante años, lo normal es que al dar el salto a Entity Framework Core continuemos utilizando más o menos las mismas cosas que ya conocíamos, y muchas veces no nos detenemos a ver qué novedades interesantes acompañan al nuevo marco de trabajo.

Ya llevamos varios posts dedicados a comentar algunas características novedosas o que cambian bastante respecto a las versiones anteriores (como la evaluación en cliente o las shadow properties, y vamos a continuar en esta línea presentando ahora otra interesante novedad: el soporte para campos de respaldo o backing fields.
miércoles, 24 de abril de 2019
.NET Core Al hilo del post de ayer, uno de los problemas que vamos a encontrar cuando instalemos versiones preliminares del SDK de .NET Core es que éstas se establecen como las versiones por defecto para el equipo.

Por ejemplo, si instalamos en nuestro equipo la preview del SDK 3.0, a partir de ese momento todos los comandos de la CLI se ejecutarán utilizando esta versión preliminar, como cuando creamos un nuevo proyecto usando dotnet new; en este caso, el proyecto se construirá usando la plantilla proporcionada por la versión más actual del framework (aunque sea preview), lo cual puede resultar molesto en algunas ocasiones.

En este post vamos a ver cómo el propio SDK dispone de mecanismos que nos permiten seleccionar una versión específica, para lo cual trataremos varios comandos útiles de la CLI.
martes, 23 de abril de 2019
Visual Studio 2019 Como sabréis, C# 8 está aún en preview, pero hace ya tiempo que estamos recibiendo información sobre las novedades que traerá la nueva versión de nuestro querido lenguaje: rangos, tipos referencia anulables, mejoras en patrones, más usos de using... pinta divertido, sin duda :D

En el futuro iremos comentando las características más interesantes, pero, de momento, este post vamos a dedicarlo exclusivamente a ver cómo podemos comenzar a probar C# 8 desde nuestro flamante Visual Studio 2019 u otros entornos, como VS Code o incluso la CLI.
Si aún no habéis tenido el ratillo para instalar la última versión de Visual Studio, ya estáis tardando ;D
El único problema es que necesitamos compiladores que entiendan la sintaxis de C# 8, y de momento esto sólo es posible usando la preview de .NET Core 3. Pero vaya, nada que no podamos solucionar en un par de minutos; veamos cómo.
martes, 2 de abril de 2019
ASP.NET Core Hace unas semanas, el amigo J. Roldán escribía un comentario en el post Inyección de dependencias en ASP.NET Core con una pregunta interesante sobre la forma de registrar determinados componentes en el inyector de dependencias. Básicamente la duda que planteaba era cómo asociar distintas interfaces a una única instancia, algo que, aunque no es complicado de conseguir, tampoco tiene una solución precisamente intuitiva.

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.

martes, 19 de marzo de 2019
Entity Framework Core Vamos con un post rapidito, al hilo de una pregunta que planteaba el amigo Javier  R. vía el formulario de contacto del blog, sobre el artículo donde veíamos las shadow properties de Entity Framework Core, cuya respuesta creo que puede ser interesante para alguien más.

En concreto, la duda le surgía al combinar las capacidades de data seeding de EF con las propiedades ocultas, y básicamente era:
Dado que las shadow properties no existen en la entidad, ¿cómo podemos establecerlas en el momento del seeding de datos?
Bien, aunque no es fácil de descubrir de forma intuitiva, la solución es bastante sencilla. Si nos fijamos en el intellisense del método que utilizamos para el poblado de datos, HasData(), podemos observar varias sobrecargas; por ejemplo, en la siguiente captura se puede ver la información mostrada al invocar este método para la entidad Friend:

Intellisense en HasData()


martes, 12 de marzo de 2019
Entity Framework Core Seguimos hablando de características interesantes de Entity Framework Core, y en esta ocasión nos detendremos en las shadow properties, o propiedades ocultas.
A grandes rasgos, se trata de la capacidad de este framework para gestionar propiedades de una entidad que existen en el almacén datos pero no en la clase .NET que la representa en el mundo de los objetos.

De forma intuitiva podemos saber que esto ya existía en las versiones clásicas de Entity Framework. Por ejemplo, cuando introducíamos una propiedad de navegación entre dos entidades sin usar una clave foránea de forma explícita, el propio marco de trabajo creaba, por convención, una columna en la base de datos para almacenar dicha referencia, como en el siguiente escenario:
public class Friend
{
    public int Id { get; set; }
    public string Name { get; set; }
                                            // Se crea una columna CountryId en la base de datos,
    public Country Country { get; set; }    // pero no existe en la entidad.

}
El valor de dicha columna CountryId no podía ser manipulada de forma directa porque se trataba de información usada internamente para gestionar las relaciones y su valor era obtenido y actualizado de forma transparente para nosotros.

Pues bien, Entity Framework Core aporta la capacidad para gestionar este tipo de campos "ocultos" para servir a nuestros propios intereses. De esta forma, podríamos añadir a una entidad propiedades que no tienen por qué estar visibles en la clase .NET en forma de propiedades; un ejemplo podría ser el clásico "IsDeleted" cuando implementamos borrado lógico, o información de auditoría como los tradicionales "UpdatedAt" o "UpdatedBy".
martes, 26 de febrero de 2019
Entity Framework Core Sin duda, Entity Framework Core es un gran marco de trabajo para implementar el acceso a datos de nuestras aplicaciones, pues es rápido, potente y nos ahorra una gran cantidad de esfuerzo, sobre todo en proyectos muy centrados en datos.

Sin embargo, estas ayudas tienen un coste importante, al igual que ocurre con casi cualquier framework al que vayamos a confiar parte de nuestro sistema, sea del ámbito que sea: hay que conocerlo bien para no dispararse en un pie en cuanto apretemos el gatillo.

Hace muuuuchos, muchos años hablamos de cómo aplicar convenientemente la carga anticipada (eager loading) en Entity Framework podía acelerar de forma drástica el rendimiento en el acceso a datos de nuestras aplicaciones, evitando el temido problema denominado "SELECT N+1". Esto continúa siendo válido para la versión Core de este marco de trabajo: sigue existiendo el extensor Include() e incluso variantes de éste como ThenInclude() que permiten incluir en una única consulta las entidades que sabemos que vamos a necesitar.

Pues bien, en EF Core hay otro detalle que, si bien utilizándolo con conocimiento puede resultar muy interesante, es fácil que nos tumbe el rendimiento de nuestros accesos a datos si no lo usamos con cuidado: la evaluación en cliente.
martes, 19 de febrero de 2019
ASP.NET Core MVCUna gestión apropiada de la caché es muchas veces el secreto para los sistemas de alto rendimiento o que tienen que atender a una gran carga de peticiones por segundo, y el framework ASP.NET Core MVC incluye numerosas herramientas para ello.

Entre otras, el tag helper <cache> es especialmente útil para almacenar en la memoria del servidor el resultado de procesar una porción de página o vista, de forma que pueda ser reutilizada en peticiones siguientes. Un ejemplo bastante básico, pero que deja bastante clara su utilidad y forma de uso, podría ser el siguiente:
<h1>Atomic clock</h1>
<cache expires-after="@TimeSpan.FromSeconds(30)">
    @{
        await Task.Delay(3000);
    }
    Current time: @DateTime.UtcNow.ToLongTimeString()
</cache>
Al acceder a una vista con el código anterior por primera vez, se producirá un retardo forzado de tres segundos, se renderizará el interior del tag <cache> mostrando la hora actual, y el resultado será almacenado en memoria durante 30 segundos.

Si volvemos a acceder a la misma vista durante esos 30 segundos, el resultado será mostrado inmediatamente (sin esperar los tres segundos) y el cliente recibirá el contenido que se cacheó durante la primera visita. Al transcurrir este plazo, en la siguiente petición se volverá a procesar el contenido del tag helper, ejecutándose la espera y volviendo a generar y cachear el resultado enviado al cliente.

Pero podemos tener ejemplos algo más complejos, como el siguiente. En el interior del tag helper hemos insertado un view component que podría mostrar las últimas noticias obtenidas de una base de datos, en lo que podría ser la página de inicio de un servicio de información on-line:
<section>
    <h1>Latest news</h1>
    <cache expires-after="@TimeSpan.FromSeconds(30)">
        <vc:latest-news></vc:latest-news>
        <p>
            Updated at: @DateTime.UtcNow.ToLongTimeString()
        </p>
    </cache>
</section>
Fijaos que en este caso el tiempo de invalidación del contenido almacenado en caché no tendría sentido que fuera periódico: ¿para qué invalidar la caché y volver a obtener las noticias de la base de datos cada X segundos si quizás no se ha introducido ninguna nueva desde la última vez que se renderizó la página? ¿No sería mejor recargar la caché sólo cuando se hayan modificado las noticias?
martes, 12 de febrero de 2019
ASP.NET Core Como sabemos, ASP.NET Core incluye un sistema de inyección de dependencias que, aunque es algo simple comparado con otras alternativas más veteranas, cubre la mayoría de necesidades habituales. Por ejemplo, un aspecto que no es muy conocido y que puede ser útil en muchas ocasiones es su capacidad para registrar y recuperar múltiples implementaciones de una misma interfaz.

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.
martes, 15 de enero de 2019
ASP.NET Core Como sabemos, los archivos Razor _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 :)
martes, 18 de diciembre de 2018
ASP.NET Core MVC Hace unos días publicaba un post sobre la mala idea que era tener controladores con demasiadas responsabilidades y cómo un constructor con demasiadas dependencias podía ser una señal (un 'code smell') de que las cosas no estaban yendo bien en ese sentido.

Por ejemplo, echando un vistazo al siguiente controlador podemos ver claramente una violación del Principio de Responsabilidad Única (SRP) en un controlador que conoce demasiados detalles sobre la forma de proceder al registrar un pedido:
public class OrderController: Controller
{
    private readonly IOrderServices _orderServices;
    [...] // Other private members

    public OrderController(IOrderServices orderServices, IUserServices userServices, 
        IMailingService mailingServices, ISmsServices smsServices, 
        IPdfGenerator pdfGenerator, IMapper mapper
    )
    {
        _orderServices = orderServices;       
        _userServices = userServices;
        [...] // Other assignments...
    }

    [HttpPost]
    public Task<IActionResult> Submit(OrderViewModel orderViewModel)
    {
        if(!ModelState.IsValid)
        {
            return View(orderViewModel);
        }
        var orderDto = _mapper.Map<OrderDto>(orderViewModel);
        var orderResult = await _orderServices.AddAsync(orderDto);
        if(!orderResult.Success)
        {
            return RedirecToAction("OrderError", new { error = orderResult.Error }));
        }
        var userPreferences = await _userServices.GetNotificationPreferencesAsync(CurrentUserId);
        var pdfUrl = await _pdfGenerator.GenerateOrderAsync(orderResult.Details);
        if(userPreferences.NotificationMode == NotificationMode.Sms)
        {
            await _smsServices.NotifyNewOrderAsync(orderResult.Details, pdfUrl);
        } 
        else 
        {
            await _mailingServices.NotifyNewOrderAsync(orderResult.Details);
        }
        return RedirectToAction("ThankYou", new { id = orderResult.Details.OrderId } );
    }
    ...
}
En dicho post comentaba también algunas cosas que podíamos hacer para solucionar el problema, así como una recomendación de lo que no debíamos hacer: disimular dependencias instanciando directamente componentes o utilizando otros "sabores" de inyección que hicieran menos evidente la relación de nuestra clase con otras de las que depende.

Pues bien, como hoy estamos algo rebeldes, vamos a ver las técnicas que nos permitirían hacerlo cuando sea necesario o, dicho de otra forma, qué alternativas tenemos para usar dependencias desde los controladores sin que estas sean suministradas mediante inyección en su constructor.
Precaución: estas técnicas son consideradas antipatrones o, como mínimo, code smells en la mayoría de los escenarios, así que usadlas poco, con precaución y siempre con conocimiento de causa ;)
martes, 20 de noviembre de 2018
ASP.NET Core MVC Seguramente habréis notado que en proyectos ASP.NET Core 2.1 la compilación de las vistas funciona de forma ligeramente diferente a como se hacía en versiones anteriores. Hasta ahora las vistas sólo eran compiladas al publicar el proyecto pero, a partir de esta versión, este proceso se realizará antes, durante la compilación.

En este artículo vamos a ver cómo aprovechar las ventajas de la precompilación, y al mismo tiempo mantener la flexibilidad que nos ofrece la compilación en tiempo de ejecución que tradicionalmente hemos disfrutado en proyectos ASP.NET y ASP.NET Core.

martes, 13 de noviembre de 2018
ASP.NET Core MVCEn el post anterior veíamos cómo personalizar los mensajes de error generados durante el proceso de binding, y cómo aprovechar las posibilidades que nos brinda el framework para introducir textos localizados en esos puntos.

Hoy seguiremos profundizando en este tema, pero esta vez nos centraremos en modificar los textos por defecto de las anotaciones de datos y hacer que simplemente decorando una propiedad con un atributo como Required consigamos obtener mensajes de error localizados y a nuestro gusto, sin necesidad de establecer el ErrorMessage de forma explícita y, en algunos casos, ni siquiera tener que indicar dicho atributo.
martes, 6 de noviembre de 2018
ASP.NET Core MVCEn el framework ASP.NET Core MVC es muy sencillo establecer los mensajes de error de validación de campos utilizando propiedades de las data annotations, como en el siguiente ejemplo:
namespace LocalizationDemo.ViewModels
{
    public class PersonViewModel
    {
        [Required(ErrorMessage ="The name is required")]
        public string Name { get; set; }
    }
}
Incluso es bastante fácil hacer que este texto aparezca traducido atendiendo a la cultura del hilo actual. Para ello, simplemente debemos configurar los servicios de localización apropiadamente, e indicar en la propiedad ErrorMessage la clave del mensaje de error en el archivo RESX asociado a la clase:
// En Startup.cs:
public void ConfigureServices()
{
    ...
    services.AddLocalization(opt=>opt.ResourcesPath="Resources");
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddViewLocalization()
        .AddDataAnnotationsLocalization(); // Esta línea añade la localización 
                                           // de data annotations 
    }

// En el view model:
namespace LocalizationDemo.ViewModels
{
    public class PersonViewModel
    {
        // El mensaje de error está definido 
        // en Resources/ViewModels.PersonViewModel.resx
        [Required(ErrorMessage ="NameIsRequired")] 
        public string Name { get; set; }
    }
}
Esto es así de fácil para las validaciones que declaramos de forma explícita en nuestro código, mediante atributos de anotaciones de datos. Sin embargo, existen otro tipo de validaciones que se realizan de forma implícita (por ejemplo, cuando en un campo entero introducimos un valor no numérico) y cuyos mensajes de error no son tan sencillos de establecer.
martes, 23 de octubre de 2018
ASP.NET Core MVC Desde que comenzamos a programar con ASP.NET MVC tenemos interiorizado que si en nuestras acciones vemos código repetido, suele ser un candidato a ser implementado en forma de filtro. Así, durante años hemos implementado en forma de action filters asuntos transversales de nuestras aplicaciones, como la seguridad, logging, caching y otros aspectos.

Sin embargo, a veces olvidamos que estas mismas técnicas podemos utilizarlas para simplificar código en la implementación de convenciones o funciones más ligadas al negocio o a funcionalidades de nuestra aplicación. Por ejemplo, ¿no estáis aburridos de escribir acciones como las siguientes?
public class FriendsController: Controller
{
    public IActionResult View(int id) 
    {
        var friend = _friendServices.GetById(id);
        if(friend == null)
           return NotFound();
        ... // Prepare and show "View" view
    }

    public IActionResult Edit(int id) 
    {
        var friend = _friendServices.GetById(id);
        if(friend == null)
           return NotFound();
        ... // Prepare and show "Edit" view
    }

    public IActionResult Delete(int id) 
    {
        var friend = _friendServices.GetById(id);
        if(friend == null)
           return NotFound();
        ... // Prepare and show "Delete" view
    }

    ...
}
Pues bien, vamos a aplicar el mismo principio para simplificar este código y eliminar duplicidades extrayendo las porciones comunes a un filtro, resultando en algo así de bonito:

[Autoload(typeof(Friend))] // ¡Magia!
public class FriendsController: Controller
{
    public Task<IActionResult> View(Friend friend) 
    {
        ... // Prepare and show "View" view
    }

    public Task<IActionResult> Edit(Friend friend) 
    {
        ... // Prepare and show "Edit" view
    }

    public Task<IActionResult> Delete(Friend friend) 
    {
        ... // Prepare and show "Delete" view
    }
    ...
}
No sé lo útil que podrá resultar en la práctica pero, como mínimo, nos ayudará a conocer mejor cómo funciona por dentro el framework ASP.NET Core MVC.
martes, 18 de septiembre de 2018
Tools Los que peinamos ya bastantes canas recordamos con nostalgia aquellas intensas sesiones de programación intentando arañar ciclos de CPU y bytes en nuestro flamante Spectrum para sacar el máximo provecho de, en el mejor de los casos, un Zilog Z80A a 3,5MHz con 48K de memoria RAM. ¡Ah, el maravilloso mundo de las micro-optimizaciones!

Hoy en día, salvo en contadas ocasiones, ha dejado de tener sentido invertir demasiado tiempo en estas labores. Tenemos máquinas potentes, con micros cuya velocidad se mide en GHz capaces de ejecutar bastantes tareas de forma concurrente, y muchos Gigabytes libres de memoria RAM en los que guardar información. Además, los frameworks actuales como .NET permiten despreocuparse de asuntos como la reserva o liberación de memoria porque ya hay sistemas de más bajo nivel que se encargan de eso por nosotros.

Indudablemente es un gran avance, pero esto ha llevado a que, con el tiempo, se nos esté atrofiando ese sentido arácnido que antes nos disparaba las alertas cuando cierto código podía ser optimizado para consumir menos recursos.

En la mayoría de escenarios, y sobre todo cuando trabajamos en entornos empresariales, aplicaciones de escritorio o webs de poca carga, está bien así. Sin embargo, es cierto también que las necesidades han cambiado.

Por ejemplo, ahora creamos frecuentemente aplicaciones mucho más complejas que pueden ser utilizadas a través de Internet por miles de usuarios de forma simultánea y todos ellos esperan respuestas rápidas. Estas aplicaciones se ejecutan en servidores cuyos recursos son compartidos entre todos los usuarios que pueden llegar a tener un coste importante y debemos exprimir al máximo. Aquí, y en otros escenarios similares, es donde aparece de nuevo la necesidad de introducir optimizaciones en el código.

En este post vamos a hacer una introducción al uso de BenchmarkDotNet, una magnífica herramienta que nos permitirá medir el rendimiento de nuestro código .NET para hacerlo más eficiente en términos de uso de procesador y memoria.

Pero antes de empezar, no olvidéis la famosa frase de Donald Knuth:
“Los programadores consumen una gran cantidad de tiempo pensando, o preocupándose, sobre la velocidad de partes no críticas de sus programas, y esos intentos de mejorar la eficiencia tienen posteriormente un gran impacto negativo sobre la facilidad de depuración o mantenimiento. Deberíamos olvidarnos de las pequeñas mejoras de eficiencia, digamos en un 97% de los casos: la optimización prematura es el origen de todos los males. Sin embargo, no debemos dejar pasar la oportunidad de mejorar ese crítico 3% restante”
martes, 26 de junio de 2018
ASP.NET Core Con ASP.NET MVC 5 y anteriores solíamos utilizar @helper para crear porciones de HTML reutilizables en el interior de nuestras vistas, tanto para conseguir un código más limpio como para abrazar el principio DRY (Don’t Repeat Yourself), tan frecuentemente apaleado en la capa de presentación.

Recordaréis que esta directiva permitía crear “funciones” en cuyo interior podíamos escribir código Razor (mezclando marcado y C#) que podía ser invocado desde distintos puntos:
@* 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>
}
Además de definirlos y consumirlos desde la misma vista, también era posible crear helpers globales introduciéndolos en App_Code, aunque esta opción me gustaba menos y creo que no la utilicé jamás en aplicaciones reales.
Pues bien, por algunas extrañas razones, en ASP.NET Core la directiva @helper no está disponible, lo que puede complicarnos un poco a la hora de portar vistas antiguas o, simplemente, cuando queramos usarla para mejorar la legibilidad de nuestro código.
martes, 19 de junio de 2018
ASP.NET Core MVC Como sabéis, por defecto las acciones MVC y Web API en ASP.NET Core retornan un IActionResult (o un Task<IActionResult> en caso de ser asíncronas). Este interfaz, cuya única misión es definir el método de ejecución asíncrona del resultado, ExecuteResultAsync(), es implementado por la gran variedad de tipos de resultado ofrecidos de serie por el framework, como ViewResult, RedirectResult o FileResult.

Sin embargo, no es esta la única opción disponible a la hora de especificar el resultado de una acción, como veremos a continuación.