ASP.Net y SignalR

SignalR es una componente de Microsoft para ASP.Net, su objetivo es agregar funcionalidad web en tiempo real a las aplicaciones, de tal forma que permite hacer que el código del servidor actualice o inserte contenido en los clientes conectados en tiempo real, similar a una consola de chat.

Es una abstracción de múltiples tecnologías PUSH, soporta diferentes protocolos de comunicación, los cuales negocia y utiliza en base a las características y/o limitaciones del servidor y el navegador web.

Soporta 4 protocolos de trasporte:

  • webSockets
  • foreverFrame
  • serverSentEvents
  • longPolling

Y maneja una arquitectura como la siguiente, donde HubsAPI y PersistentConnection API son 2 modelos diferentes de implementación del lado del Servidor.

Diagrama arquitectonico de SignalR

En general, simplifica mucho el proceso de realizar notificaciones Push en aplicaciones web y elimina la dependencia con servicios Push de terceros como IBM Bluemix o Google Firebase.

Para implementaciones en Azure, existe un componente llamado Azure SignalR el cual provee integración con componentes montados en Azure y resuelve algunos problemas de escalamiento horizontal que se generan al utilizar SignalR en Azure.

Para mayor información respecto a implementaciones dejo un ejemplo de implementación con .Net Framework y algunas referencias para .Net Core y .Net Framework.

Por el momento es todo, deja un comentario si te fue de utilidad.

Referencias:

.Net Core

https://learn.microsoft.com/es-mx/aspnet/core/signalr/introduction?view=aspnetcore-7.0&WT.mc_id=dotnet-35129-website

https://learn.microsoft.com/es-mx/aspnet/core/tutorials/signalr?view=aspnetcore-7.0&tabs=visual-studio

.Net Framework

https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/

Otros

https://en.wikipedia.org/wiki/Push_technology

Reproducir videos con audio EAC3, DTS o TrueHD en un NAS Synology

Actualización: Synology dejo de soportar la transcodificación de audio para el formato AAC a partir de la versión 7.1.1 del DSM (2022-09-27), el parche de AlexPresso plantea soportarlo, sin embargo aun no hay fecha de implementación.

Hace algún tiempo, después de comprar mi primer NAS Synology, me entere que la Video Station (la consola multimedia del NAS) no soporta la reproducción de videos que usen el formato de audio EAC3, DTS o TrueHD, así que me dí a la tarea de investigar la razón y una posible solución.

Encontré que la razón del porque no reproduce esos formatos de audio es muy simple, porque el NAS Synology no incluye los codecs para esos formatos, ya que son formatos propietarios y tendrían que pagar algo por incluirlos (por el precio del NAS creo que deberían incluirlos).

Así que buscando posibles soluciones y encontré 2 opciones, ambas instalando e utilizando el paquete FFmpeg de SynoCommunity.

La primera opción es transcodificar los archivos de video a otro formato de audio que si sea soportado por el NAS Synology, y la segunda opción, remplazar el transcodificador de audio de la Video Station para que transcodifique los video al vuelo sobre demanda.

Las ventajas de la primera opción es que la operación es muy sencilla, solo instalas el FFmpeg y ejecutas un comando.

El comando para convertir tus videos a formato AC3 seria algo similar a esto:

/path/to/ffmpeg -i "input.mkv" -map 0 -c:s copy -c:v copy -c:a ac3 -b:a 640k "output.mkv"

Es requerido utilizar la versión del FFmpeg de SynoCommunity. Aquí el proceso de instalación.

La desventaja de esta opción es que si tienes muchos archivos de video con formatos no soportados, la tarea de identificar y transcodificar cada archivo sera ardua.

Ahora, para la segunda opción, la ventaja es que es sobre demanda, y si tienes muchos archivos, para ti sera transparente, la Video Station hará el trabajo de transcodificar los videos al vuelo al momento de la reproducción, la desventaja es que la tarea de transcodificación consumirá recursos de tu NAS, principalmente uso de procesador, lo cual podrá tener efectos en el rendimiento de tu NAS.

El proceso consiste en remplazar la versión del paquete FFmpeg de la Video Station por la version 4 de SynoCommunity.

Primeramente, como se indico anteriormente, hay que instalar la versión 4 del paquete FFmpeg del repositorio de SynoCommunity.

Posteriormente sera necesario conectarnos via SSH al NAS. Se habilita desde el panel de control.

Una vez conectado, se utiliza el comando “sudo -i” para cambiar a usuario Root.

y por ultimo utilizas el siguiente comando para remplazar el paquete FFmpeg.

curl https://raw.githubusercontent.com/AlexPresso/VideoStation-FFMPEG-Patcher/main/patcher.sh | bash

si requieres quitar el parche y volver a la versión original del paquete FFmpeg

curl https://raw.githubusercontent.com/AlexPresso/VideoStation-FFMPEG-Patcher/main/patcher.sh | bash -s -- -a unpatch

Y ahora si, a disfrutar tu contenido multimedia.

Referencias:

synocommunity.com

VideoStation-FFMPEG-Patcher

converting eac3 to aac with ffmpeg

Operaciones con objetos DateTime y TimeSpan

En esta ocasión les comparto unos ejemplos muy básicos de comparación de fechas y horas.

Le mostrare una pequeña aplicación de consola que realice para unas pruebas.

El objetivo es comparar 2 horas con minutos, tipo 13:30, guardarlos en un TimeSpan y compararlos.

Console.WriteLine("Escribe una hora en formato HH:mm");
Console.WriteLine("Por ejemplo: 13:30");
TimeSpan time1 = TimeSpan.Parse(Console.ReadLine());
Console.WriteLine("Escribe otra hora en formato HH:mm");
TimeSpan time2 = TimeSpan.Parse(Console.ReadLine());

if (time1 > time2)
	Console.Write("La 1ra hora es mayor que la 2da");
else if (time1 < time2)
	Console.Write("La 2da hora es mayor que la 1ra");
else
	Console.Write("La 1ra hora es igual que la 2da");

Console.Read();

Entrega una salida como la siguiente:

Incluye una segunda opción donde obtiene la diferencia en horas entre dos fechas.

Console.WriteLine("Escribe un dia del mes:");
int dia1 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Escribe otro dia del mes:");
int dia2 = Convert.ToInt32(Console.ReadLine());

DateTime date1 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, dia1);
DateTime date2 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, dia2); 

int total = Convert.ToInt32(date2.Subtract(date1).TotalHours);

Console.WriteLine("Horas de diferencia entre las 2 fechas: " + total.ToString());

Console.Read();

La segunda parte quedaría así:

Por el momento es todo, posteriormente iré agregando mas ejemplos a esta publicación.

Son unos ejemplos simples, espero a alguien le sean útiles.

Código de ejemplo:

https://pabloroman.mx/ejemplos/Test_DateTime&TimeSpan.zip

Recomendaciones al comprar un NAS Synology de gama baja

Les comparto algunos consejos a la hora de elegir un NAS marca Synology.

Cabe mencionar que esto es solo una recomendación de la gama baja de Synology, es totalmente basada en mi experiencia personal, les recomiendo encarecidamente buscar otras fuentes de información y formar su propio criterio.

Una vez dicho lo anterior, empezamos…

La gama baja de Synology se clasifica en DS J Series, DS Value Series y DS Plus Series.

DS J Series (DS120j, DS220j) – Es el equipo mas esencial, incluye procesadores de bajo desempeño y poca memoria RAM, su uso principal es el de un NAS Personal con algunas funciones de Cloud, como podría ser compartir archivos multimedia, sincronización de fotos.

DS Value Series (DS218, DS223) – Incluye lo mismo que el anterior con algunas mejoras en procesamiento, memoria y aplicaciones, algunas versiones incluyen opciones para ampliar la memoria RAM provista de fabrica. La biblioteca de aplicaciones es un poco mayor y permite activar mayores funcionalidades a nuestro NAS, como por ejemplo: funciona como DVR con la aplicación Survilance Station o como reproductor de contenido multimedia con la aplicación Video Station por decir algunas. Existe una versión DS Value Play que tiene un enfoque en transmisión de contenido multimedia e incluye un chip para decodificación de video, sin embargo, antes de adquirirlo, es importante informarse de que formatos de video admite cada versión.

DS Plus Series (DS218+, DS220+) – Es el mejor equipado de los 3 en cuestiones de memoria, procesamiento, incluye mejoras en puertos red y USB, permite la actualización de la memoria de fabrica e incluye un mayor numero de aplicaciones que brindan una funcionalidad muy completa para un Nube `Privada.

Como acceder al Disk Station Manager de Synology mediante un subdominio

En esta ocasión les comparto como configurar un subdominio para acceder al DSM de Synology.

El primer paso es ingresar al panel de control de su dispositivo Synology, a la opción “Portal de Inicio”.

En la sección Dominio, ingresa tu subdominio personalizado, por ejemplo: MiSubdominio.MiSitio.com

Posteriormente, en el panel de control dirígete a la opción “Seguridad / Certificado”.

Selecciona la opción “Agregar” para agregar o importar un certificado para el subdominio que configuraste a tu DSM.

Puedes crearlo con Let’s Encrypt o importarlo, en caso de que ya cuentes con un certificado para ese dominio.

También requerirás crear el subdominio con la herramienta de tu proveedor de DNS y esperar al menos 24 horas a que se propague por internet la dirección del nuevo subdominio.

Una configuración similar aplica para asignar un subdominio a la consola de alguna aplicación de nuestra DiskStation, por ejemplo: Download Station, Video Station, etc…

Espero les sea de utilidad.

Verificar la existencia de objetos en SQL Server

En esta ocasión les comparto un script para verificar la existencia de objetos en SQL Server, es muy útil para validar procesos de despliegue entre ambientes.

Solo necesitan agregar en la primera seccion los objetos a validar.

-----(Script to verify deploy DB objects...)-----

declare @TblResultset table 
(schema_name sysname null, 
ItemName sysname null, 
ParentName sysname null,	--apply for indexes and columns
ItemType int not null,		--1 for tables, 2 for udtt, 3 for stored procedures, 4 for functions, 5 for indexes & 6 for columns.
create_date datetime, 
modify_date datetime, 
StatusMsg nvarchar(256))

set nocount on 

--/* update here with the objects to search.... !!!! --*/
insert @TblResultset (schema_name, ItemName,ItemType,ParentName)
--tables (schema, TableName, 1, null)
select 'dbo', 'MyTable1' ,1,null union all
select 'dbo', 'MyTable2',1,null union all
--user defined table types (schema, UdtName, 2, null)
select 'dbo', 'udt_1',2,null union all
select 'dbo', 'udt_2',2,null union all
--stored procedures (schema, SpName, 3,null)
select 'dbo', 'MyProcedure1' ,3,null union all 
select 'dbo', 'MyProcedure2' ,3,null union all 
--functions (schema, FunctionName, 4,null)
select 'dbo', 'fn_MyFuntion1' ,4,null union all
--indexes (schema, idxName, 5, ParentTableName)
select 'MySchema', 'MyIndexName' ,5,'MyParentTable' union all
--columns  (schema, ColumnName, 6, ParentTableName)
select 'MySchema','MyColumnName',6,'MyParentTable'

update RS
set RS.create_date = ST.create_date,
	RS.modify_date = ST.modify_date, 
	RS.StatusMsg = 
		case
			when ST.name is not null then 'Exists in database.'
			else 'Do not exists in database.'
		end
from @TblResultset RS 
	left outer join sys.tables ST 
		on RS.ItemName = ST.name
where RS.ItemType = 1 --tables

update RS
set RS.create_date = AO.create_date,
	RS.modify_date = null,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  sys.table_types ST 
		on RS.ItemName = ST.name
	left outer join sys.all_objects AO 
		on ST.type_table_object_id = AO.object_id
where RS.ItemType = 2 --user defined table type


update RS
set RS.create_date = ST.created,
	RS.modify_date = ST.LAST_ALTERED,
	RS.StatusMsg = 
	case
		when ST.SPECIFIC_NAME is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join INFORMATION_SCHEMA.ROUTINES ST 
		on RS.ItemName = ST.SPECIFIC_NAME
where RS.ItemType in(3,4) --stored procedures & functions

update RS
set RS.create_date = AO.create_date,
	RS.modify_date = AO.modify_date,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  sysindexes ST 
		on RS.ItemName = ST.name
	left outer join sys.all_objects AO 
		on ST.id = AO.object_id
where RS.ItemType = 5 --index

update RS
set RS.create_date =null,
	RS.modify_date = null,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  syscolumns ST 
		on RS.ItemName = ST.name and object_name(ST.id) = RS.ParentName
where RS.ItemType = 6 --columns

-----(((Output resultset)))-----
select schema_name, ItemName as ObjectName, 
case
	when ItemType = 1 then 'Table'
	when ItemType = 2 then 'UDTT'
	when ItemType = 3 then 'Stored Procedure'
	when ItemType = 4 then 'Function'
	when ItemType = 5 then 'Index'
	when ItemType = 6 then 'Column'
	else 'UFO'
end as ObjectType,
isnull (cast(create_date as varchar),'NA') as CreateDate, 
isnull(cast(modify_date as varchar),'NA') as ModifyDate,
StatusMsg
from @TblResultset

set nocount off
go

La salida del script seria asi:

Espero les sea de utilidad…

Programar Policies para Endpoints de Azure API Management

Iniciando en este mundo de las Policies en API Managment tuve algunos problemas en encontrar una forma medianamente optima para programar dichas policies de una forma no tan tediosa o arcaica.

El mejor editor que encontré y recomiendo es Visual Studio Code en conjunto con la extensión Azure API Managment. Te permite conectarte directamente con tu instancia de API Management e interactuar con los objetos. Tiene algunas pequeñas fallas en la interfaz, pero es mucho mejor que otros editores.

https://learn.microsoft.com/en-us/azure/api-management/visual-studio-code-tutorial

Incluso puedes llegar a debugear una Policy, aunque el proceso de debugeo deja algo que desear.

https://learn.microsoft.com/en-us/azure/api-management/api-management-debug-policies

Para iniciar en la programación recomiendo las siguientes lecturas:

https://learn.microsoft.com/en-us/azure/api-management/api-management-policies

https://learn.microsoft.com/en-us/azure/api-management/api-management-advanced-policies

https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions

Para información mas general de API Management consultar:

https://learn.microsoft.com/en-us/azure/api-management/

Herramientas online para programadores

En esta ocasión les comparto algunas herramientas que uso en mi día a día, espero les sean de utilidad.

Webtoolkit Online

Conjunto muy completo de herramientas en un solo sitio

www.webtoolkitonline.com

Code Beautify

Otro sitio con un conjunto de herramientas muy completo

codebeautify.org

XML Viewer

Permite visualizar, formatear, convertir, minificar archivos XML, entre otras cosas

https://codebeautify.org/xmlviewer

JSON Viewer

Permite visualizar y formatear datos en formato JSON

Online JSON Viewer (stack.hu)

JSON Formatter

Para formatear y validar datos en formato JSON

https://jsonformatter.curiousconcept.com

DotNetFiddle

Compilador de codigo C# en línea

C# Online Compiler | .NET Fiddle (dotnetfiddle.net)

C# Viewer

Visualizador, compilador y minificador de codigo C#

https://codebeautify.org/csharpviewer

Excel To Json

Como su nombre lo dice, permite convertir la informacion de un excel a JSON

Excel To Json Converter – BeautifyTools.com

APP Diagrams

Herramienta para generar diversos tipos de diagramas (Mapas mentales, UML, etc, etc).

diagrams.net

BPMN Sketch Miner

Herramienta para generar diagramas mediante la Notacion de Modelado de Procesos de Negocios (BPMN)

BPMN Sketch Miner (bpmn-sketch-miner.ai)

Web Sequence Diagrams

Otra herramienta para generar diagramas de forma fluida

https://www.websequencediagrams.com/

PlantText

Herramienta para generar diagramas UML

https://www.planttext.com/

https://plantuml.mseiche.de/

Agregar documentación en formato Swagger en API’s de .Net Core 3.1

En esta ocasión les mostrare como integrar documentación en formato swagger a nuestra API de forma automática.

Iniciamos instalando el siguiente paquete Nuget:

Swashbuckle.AspNetCore -Version 5.0.0-rc2

Posteriormente, en el archivo Startup.cs, incluyes el generador de Swagger en el metodo “ConfigureServices”.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    // Register the Swagger generator, defining 1 or more Swagger documents
    services.AddSwaggerGen(c =>
    {                
        c.SwaggerDoc("v1", new OpenApiInfo
            {
                Version = "v1",
                Title = "API Product",
                Description = "API Products B2B"
            }
        );

        // Set the comments path for the Swagger JSON and UI.
        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
        c.IncludeXmlComments(xmlPath);
    });
}

Y en el método “Configure” habilitas el middleware de Swagger

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    // Enable middleware to serve generated Swagger as a JSON endpoint.
    app.UseSwagger();

    // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
    // specifying the Swagger JSON endpoint.
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "API Product V1");
    });

    app.UseHttpsRedirection();
    app.UseMvc();
}

Ahora agregaremos cierta configuración al proyecto de tipo API.

Da clic derecho en el proyecto de tipo API y elige la opción “Edit Project File”, posteriormente, agregar las siguientes líneas:

    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>

Quedaría de la siguiente forma:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\ARCA.B2B.Product.Domain\ARCA.B2B.Product.Domain.csproj" />
  </ItemGroup>

</Project>

Ahora necesitamos agregar etiquetas de documentación a los controladores y métodos.

En la clase del controlador se agregara el atributo [Produces] para indicar el formato de entrada y salida del API.

    [Produces("application/json")]
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase

En los métodos del controlador se agregaran comentarios en formato de triple diagonal

/// <summary>
/// Get all products, optional filter by segment
/// </summary>
/// <remarks>
/// Sample request:
///
///     GET: api/Product
///
/// </remarks>
/// <param name="Segment">1 - Favoritos, 2 - Destacados</param>
/// <response code="200">Return a list of products</response>
/// <response code="204">Products not found</response>
/// <response code="400">Invalid data</response>
// GET: api/Product
[HttpGet]
[ProducesResponseType(typeof(List<Domain.Entities.Product>), 200)]
public IActionResult Get([FromQuery]Domain.Entities.ProductSegment? Segment)
{
    List<Domain.Entities.Product> _products = new List<Domain.Entities.Product>();

    if (Segment != null)
        _products.Add(new Domain.Entities.Product() { ProductId = "001", ShortDesciption = "Coca Cola 600 ml", Favorite = true });
    else
        _products.Add(new Domain.Entities.Product() { ProductId = "001", ShortDesciption = "Coca Cola 600 ml" });

    return Ok(_products);
}

También se agregara la etiqueta [ProducesResponseType] en los métodos que retornen algún tipo de dato, especificando en tipo de dato a retornar y el código HTTP a responder, tiene que corresponder con alguno de los “Response Code” que se agregaron en los comentarios.

[HttpGet]
[ProducesResponseType(typeof(List<Domain.Entities.Product>), 200)]
public IActionResult Get([FromQuery]Domain.Entities.ProductSegment? Segment)

También es recomendable agregar Data Anotation en las entidades, lo cual hace mas descriptiva la presentación de las entidades en Swagger.

using System.ComponentModel.DataAnnotations;


namespace ARCA.B2B.Product.Domain.Entities
{
    public class Favorite
    {
        [Required]
        public int ClientId { get; set; }
        [Required]
        public string ProductId { get; set; }
    }
}

Por ultimo, para consultar la UI de Swagger, partiendo de la siguiente URL del API:

https://localhost:[Port]/api/Product

Se le agrega la siguiente ruta “Swagger/Indez.html” a la URL Root del API

https://localhost:[Port]/Swagger/Index.html

La especificación en formato JSON se consulta igualmente desde el Root agregando “swagger/v1/swagger.json”

https://localhost:[Port]/swagger/v1/swagger.json

Con esto finalizamos, espero les sea de utilidad.

Habilitar CORS en .Net Core 3.1

En esta ocasión les comparto un poco de código sobre como habilitar el intercambio de recursos de origen cruzado (CORS) en un API de .Net Core.

Se habilita agregando las siguientes lineas a los métodos ConfigureServices y Configure del archivo Startup.cs del proyecto.

        public void ConfigureServices(IServiceCollection services)
        {          
            services.AddCors(options =>
            {
                options.AddPolicy("AllowAllOrigin", builder =>
                    builder.AllowAnyHeader()
                           .AllowAnyMethod()
                           .AllowAnyOrigin()
                );
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseCors("AllowAllOrigin");
        }

-Referencia
https://anexsoft.com/netcore-api-vuejs-spa-cors-y-nuestro-primer-listar-con-element-ui