NET MAUI – 2022 Enero

Este versión está disponible para NET 6, y es el framework para desarrollar aplicaciones multiplataforma. Es parte de dotnet, está disponible en NET 6,0 o NET 7.0 en adelante.

Con el podemos desarrollar aplicaciones nativas para MacOS, iOs, Android y Windows. Si te suena esto, es porque anteriormente teníamos a Xamarín con Xamarín Forms. Con el lanzamiento de Net 6.0, y bajo los nuevos lineamientos que apunta .NET a convertir en el framework multiplataforma mas usado por tener aplicaciones completas y veloces, es que decide evolucionar su herramienta (Xamarin) hacía un nuevo producto: NET MAUI. En el incluirá todo lo aprendido hasta el momento con su framework de NET (microservicios, serverless, opensource, multiplataforma, etc).

Lo primero que debes saber es que está en una versión preview, y solo está disponible por el canal de Visual Studio 2022 preview (en cualquier versión). Pero para el Q1 del 2022 ya tendremos un versión candidad. Y en el Q2 del 2022, ya una versión definitiva.

Lo que no está claro si está en Net 6.0, o será ya parte de Net 7.0 (Nov 2022).

Estas son las últimas novedades, y te muestro como obtener un ejemplo de una arquitectura completa con NET 6.0 (API, Minimal API, FrontEnd y Mobile Apps entre otros):

Net Maui – Enero 2022 – YouTube

Si quieres saber más de NET MAUI, este lo lanzamos apenas ya teníamos las primeras novedades:

NET MAUI – Te cuento todo lo que debes sabes – YouTube

Consultas Linq en Query Syntax y Method Syntax en .net con c#

LINQ : Language Integrated Query – Wikipedia, la enciclopedia libre

Como su nombre lo dice, es lenguaje para realizar consultas integradas, y es provisto y administrado por .NET

Aprende a usarlo, te permitirá recorrer cualquier colección (Enumerables, Dictioraries, Lists, etc)

También cada resultado proveniente de una consulta con el ORM de EF (Entity Framework ) te devolverá un IQueriable (Un tipo de colleción), y su manipulación, sin dudas los harás mediante LINQ.

Para que puedas aprender en el siguiente video vemos varias consultas para que puedas realizar, y sobre todo para que puedas comparar los dos métodos disponibles que tienes:

Method Sintax & Query Sintax

Sin mas nada que decir, aquí está el video:

Consultas Linq en Query Syntax y Method Syntax en .net con c# – YouTube

A connection was successfully established with the server, but then an error occurred during the pre-login handshake

Trabajas con Docker, MSSQL y recibes este error

Para ser mas claro el error que me estuvo volviendo loco fue el siguiente:

A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 – An internal exception was caught

Es difícil de entender y descifrar así te cuento un poco como era mi escenario

Escenario:

Persistencia: MS SQL Server 2018 Express Edition

Aplicación Web: Net 6.0

Contenedor: Docker

Al principio pensé que era una cuestión de conexión al motor, por lo que suele tener las siguientes precauciones:

En mi imagen base suele habilitar algunas herramientas para facilitar las pruebas: Por ejemplo iputils

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base

WORKDIR /app
#si el motor es SQL 2018
RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf 
EXPOSE 80
EXPOSE 443
USER root
RUN apt-get update
RUN apt-get install -y iputils-ping
RUN apt-get install -y nano
RUN apt-get install -y tzdata
RUN apt-get install -y locales
ENV LANG es_AR.UTF-8  
ENV LANGUAGE es_AR:es  
ENV LC_ALL es_AR.UTF-8  
ENV TZ America/Mendoza

Por otro lado en mi aplicación web bajo NET 6.0 suele habilitar la migración por párametros y habilitarlas al lanzamiento de la misma:

Mi Program.cs quería así

....
bool applymigrations = Configuration.GetValue<bool>("Migrations:ApplyMigrations");
string cone_rw6 = Configuration.GetValue<string>("ConnectionStrings:RW6Connection");
string cone_beje = Configuration.GetValue<string>("ConnectionStrings:BejermanConnection");

Console.WriteLine($"BD: Migraciones {applymigrations}, Conection: {cone_rw6} ");
Console.WriteLine($"BD: Migraciones {applymigrations}, Conection: {cone_beje} ");

var scope = app.Services.CreateScope();
if (applymigrations) await Migrations(scope.ServiceProvider);

Console.WriteLine("******************Finalizado configuración de middlewares *******************");

/* ========  Run  =======*/
app.Run();

Y mi función de Migraciones así



async Task Migrations(IServiceProvider services)
{
    //Cadena de conexión de variable externa



    var context_identity = services.GetRequiredService<AppIdentityContext>();
    var context_appdb = services.GetRequiredService<AppDbContext>();
    var conn_appidentity = context_identity.Database.GetDbConnection();
    var conn_appdb = context_appdb.Database.GetDbConnection();

    Console.WriteLine($"Conexión Actual Identity: {conn_appdb.ToString()}  {Environment.NewLine}  {conn_appdb.ConnectionString}");
    Console.WriteLine($"Conexión Actual AppDB: {conn_appdb.ToString()}  {Environment.NewLine}  {conn_appdb.ConnectionString}");
    Console.WriteLine("****************** Probando acceso  *******************");
    try
    {
        Console.WriteLine("Base Disponible de Identity:" + context_identity.Database.CanConnect());
        Console.WriteLine("Base Disponible de AppDb:" + context_appdb.Database.CanConnect());
    }
    catch (Exception ex)
    {
 Console.WriteLine($"------ !!! ERROR connectando: {ex.Message}");
    }

    Console.WriteLine("****************** Aplica Migraciones:  *******************");
    Console.WriteLine(applymigrations);

    if (applymigrations)

    {
        //Aplico la migracion
        Console.WriteLine("******************Aplicando migración*******************");
        Console.WriteLine("Migrando Identity:");
        context_identity.Database.Migrate();
        Console.WriteLine("Migrando Context:");
        context_appdb.Database.Migrate();

        Console.WriteLine("******************Cargando Usuarios:*******************");
        var userManager = services.GetRequiredService<UserManager<MyAUser_Models>>();
        var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();

        await AppIdentity_StartUp.CargarUsuarios(userManager, roleManager);
    }
}

Pero a pesar de todo esto no había caso. La aplicación se conectaba, pero luego la misma se caía.

Por lo que me puse a analizar el error completo:

Microsoft.Data.SqlClient.SqlException (0x80131904): 
A connection was successfully established with the server, 
but then an error occurred during the pre-login handshake. 
(provider: TCP Provider, error: 35 - An internal exception was caught)
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.

Y venía de la mano de SSL. Ya sabiendo por donde orientar mi búsqueda, fue mas sencillo llegar al causar y a la solución:

Para OpenSSL, la mínima versión con la que opera la imagen de .NET es TLS v1.2 en adelante, mientras que el MS SQL Server 2018 express edition, solo soporta hasta TLS v1.0

Solo era cuestión de probar, si la teoría era correcta. Para ello ingresé a la imagen y modifique la configuración del openssl para correr en una versión inferior:

#docker exec -it <name> sh

#cat /etc/ssl/openssl.cnf

.......
[system_default_sect] 
MinProtocol = TLSv1.2 
CipherString = DEFAULT@SECLEVEL=2

Por lo que procedí a modificar mi imagen, y no tener que cambiarlo manualmente en la implementación:

Para ello agregué

RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf

Mi primer parte del dockerfile quedó así

#################################################################################################
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base

WORKDIR /app
#si el motor es SQL 2018
RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf 
EXPOSE 80
EXPOSE 443
USER root
RUN apt-get update
RUN apt-get install -y iputils-ping
RUN apt-get install -y nano
RUN apt-get install -y tzdata
RUN apt-get install -y locales
ENV LANG es_AR.UTF-8  
ENV LANGUAGE es_AR:es  
ENV LC_ALL es_AR.UTF-8  
ENV TZ America/Mendoza