error MSB3030: No se pudo copiar el archivo

Desde hace tiempo que vengo trabajando con Net MAUI para el desarrollo de aplicaciones móviles. Pero algunos paquetes de Google Firebase han empezado a presentar inconvenientes, sobre todo cuando trabajas con iOs. Por ello decidí empezar a probar la versión Net 8.0 preview con VS2022 preview. Algo tan simple puede volverse un dolor de cabeza…

C:\Program Files\dotnet\sdk\8.0.100-rc.1.23455.8\Microsoft.Common.CurrentVersion.targets(4879,5): error MSB3030: No se
pudo copiar el archivo "D:\NUGET\xamarin.firebase.ios.cloudfirestore\8.10.0.3\lib\net6.0-ios15.4\Firebase.CloudFirestor
e.resources\grpcpp.xcframework\ios-arm64_x86_64-simulator\grpcpp.framework\PrivateHeaders\src\core\ext\upb-generated\en
voy\config\filter\network\http_connection_manager\v2\http_connection_manager.upb.h" porque no se encontró. [D:\source\r
epos\globalassistgroup\metroin.app-trabajador\metroin.app-trabajador\metroin.app-trabajador.csproj::TargetFramework=net
8.0-ios]
Error Could not find a part of the path 'C:\Users\vivek.nuget\packages\xamarin.firebase.ios.core\8.10.0.1\lib\xamarinios10\Firebase.Core.resources\GoogleUtilitiesComponents.xcframework\ios-arm64_i386_x86_64-simulator\GoogleUtilitiesComponents.framework\PrivateHeaders\GULCCComponentContainerInternal.h'.

¡

¿Porqué ocurre?

Al parecer por el largo de la ruta que se genera dinámicamente, la cuál no es soportado en Windows.

Solución:

Acortar la ruta. Para ello podemos modificar dos paramétros

  • Ruta de los paquetes nugets
  • Habilitar que Windows maneje rutas largas

Ruta de paquetes nugets

Debemos editar las variables de entorno y agregar dos nuevas entradas

Largo de rutas en windows

Debes crear un registro en Regedit

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001

Maximum Path Length Limitation – Win32 apps | Microsoft Learn

¿Me cuentas si te funcionó?

Failed to execute operation: No such file or directory

¿Porque un systemctl enable myservice.service da este error?

Hoy es unos de esos días donde algo tan simple deja de funcionar. Te pongo en contexto: Un worker service creado en .NET 7.0 encargado de interactuar con un servicio de telefónia Asteriks y una API de .NET

Este servicio funciona correctamente en el servidor linux que cuenta con CentOS y una distro de “FreePBX”. Por lo que se decidió pasar a servicio la ejecución del mismo.

Para ello, de acuerdo a la documentación de .NET tan solo con el siguiente archivo de texto es suficiente:

Host ASP.NET Core on Linux with Nginx | Microsoft Learn

[Unit]
Description=Example .NET Web API App running on Linux
After=network.target

[Service]
WorkingDirectory=/var/ami
ExecStart=/usr/bin/dotnet /var/ami/WorkerServiceAMI.dll
Restart=always

Restart service after 10 seconds if the dotnet service crashes:

RestartSec=10

KillSignal=SIGINT
SyslogIdentifier=dotnet-ami
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target

Una vez generado el mismo tan solo al ejecutar systemctl enable myservice.service debería funcionar pero recibía constantemente este error:

Failed to execute operation: No such file or directory

Luego de romperme la cabeza y no encontrar la solución en Google me puse analizar el porque no encontraba el archivo. Y entendí lo siguiente: Systemctl es parte del administrador de servicios del sistema, por lo tanto busca los archivos de forma predeterminada en /etc/systemd/system o /usr/lib/systemd/system.

Solución:

Copia el archivo dentro de esa ubicación y asunto arreglado.

Espero que te sirva este dato.

Maui con Mysql

Sino sábes lo que es net MAUI te dejo un video donde está bien explicado.

La historia corta es que es el framework de Microsoft para desarrollar aplicaciones móviles nativas para iOs, Android, MacOs y Windows.

En caso de que requieras persistencias historicamente se ha trabajado siempre con el motor SQLLite de forma local. Pero si quieres algo más robutos te conviene buscar un motor de bases de datos como PostgreSQL, MySQL, MSSQl, etc.

Pero la cuestión aquí es: “Puedo conectarme directamente a MySQL desde Net MAUI?

La respuesta corta es: “No, Lamentablemente no es posible”. El problema está en la forma en que se realiza la encriptación de la conexión al motor, y el sistema operativo que la intenta hacer, por ejemplo Android.

Tuve varias horas cacharreando con el asunto, y cada paso que avanzaba, se compensaba con dos mas de retroceso.

¿Porque lo intente hacer?

A fines educativo, ya que no es la manera correcta.

¿Como deberías hacerlo?

Siempre a través de un servicio web (WebService Rest Ful). Es la mejor alternativa ya que simplemente con un cliente web (http client) podrás realizar la integración de una manera standarizada.

No uses Newtonsoft

Seguramente para serializar / deserializar tus json ocupas la librería externa de Newtonsoft Json.NET – Newtonsoft

Esta librería nos acompaña desde el 2011, y durante todo el furor de .NET framework fue la mejor alternativa que teníamos disponible.

Luego con la aparición de Net Core (2016) y su enfoque en el rendimiento estaba claro que había que tener algo nativo para el trabajo con JSON ya que es un estándar a la hora de pasar información entre servicios web del tipo WebAPI. Por ello surgió la clase nativa System.Text.Json. De manera muy tímida fue adquiriendo mayor funcionalidad, pero sobre todo un mejor rendimiento en performance frente a su competidor mas estable.

Al día de hoy tenemos Net 7.0 y en la puerta Net 8.0 que será liberado en noviembre del 2023. Por ello me propuse realizar una comparativa para ver que tan cierto era esto de que System.Text.Json es 100% más rapido que NetwtonSoft.

Aquí te dejo el video y los resultados te sorprenderán

Para ello haremos un #benchmark usando los frameworks de #net7 y #net8.

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

API rest con Net 5.0 y MongoDB

Desde que salío Net 5.0 (Nov 2020) empezamos a migrar todos nuestros proyectos a esta nueva versión. Muy similar a Net Core, pero con algunos cambios, sobre todo haciendo foco en Microservicios. Por otro lado con el crecimiento de popularidad de las bases No SQL decide armar esta tutorial con el producto mas conocidos: Mongo DB.

Esta base no relacional presenta ciertas diferencias con respecto a sus antecesoras relacionales como MS SQL Server. Por lo que en los siguientes videos te vamos a mostar

  • Como instalar desde 0 tu motor MongoDB
  • Crear la Base de Datos
  • Crear la colección
  • Realizar las primeras consultas por consola
  • Realizar las primeras consultas por Compass

En esta segunda parte ya nos enfocamos en trabajar con Net 5.0 y crear nuestro servicio web

  • Creamos el proyecto
  • Armamos nuestro modelo
  • Incorporamos Swagger y MongoDB Driver
  • Creamos la estructura para empezar a trabajar

En esta tercer parte ya estamos listo para nuestro servicio

  • Creamos el CRUD (Create, Recovery, Update y Delete)
  • Creamos el controlador
  • Probamos nuestro API Rest

En el siguiente video estaremos viendo los siguientes temas

  • Crear filtros avanzados
  • Usando Docker