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!
martes, 6 de febrero de 2018
.NET Core Todos tenemos bibliotecas o componentes escritos para .NET Framework que nos gustaría portar de alguna forma a .NET Core para poder utilizarlos en nuevos proyectos, o, al menos, saber si esto sería posible en un futuro.

Aunque de forma intuitiva muchas veces tenemos la respuesta (sobre todo cuando ésta es un rotundo “no” ;D), sin duda es interesante disponer de información objetiva que indique cómo de lejos estamos de conseguirlo y cuáles son los principales impedimentos.

Para ayudarnos con esta tarea, tenemos a nuestra disposición la herramienta .NET Portability Analyzer, cuyo objetivo es ayudar a detectar APIs usadas por las aplicaciones objeto del análisis que no están disponibles en otras plataformas .NET (Windows, .NET Core, Mono, Xamarin…). También podría utilizarse para comprobar incompatibilidades debidas a breaking changes si hacemos un retarget a una versión distinta del framework .NET.
Nota: en el momento de escribir estas líneas, sólo han sido liberadas versiones “alfa”, lo que quiere decir que esta herramienta se encuentra aún en desarrollo y puede que cambien algunas cosas antes de disponer de la versión final.
.NET Portability Analyzer se distribuye en dos “sabores” distintos:
Por comodidad, utilizaremos la extensión de Visual Studio en el resto del post, aunque la versión de línea de comandos funciona mejor y está actualizada a las últimas revisiones.

Analizar un proyecto

Una vez instalada la extensión desde el propio entorno (podéis buscarla con la denominación “.NET Portability Analyzer”), para analizar la portabilidad de un proyecto bastará con abrir el menú contextual en el explorador de soluciones y buscar la opción “Analyze project portability”. También podemos analizar un ensamblado independiente utilizando el menú “Analyze > Analyze Assembly Portability”.

Menú Analyze Assembly Portability

Tras ello, el analizador generará un informe mostrando la compatibilidad de nuestro proyecto con las plataformas .NET Core 2.0 y .NET Framework 4.7, pero abriendo los settings de la herramienta (en Analyze > Portability Analyzer Settings) podemos especificar tanto el formato de salida -Excel, HTML u otros- como las plataformas que deseamos comprobar expresamente:

Settings del analizador de portabilidad

De esta forma podremos generar informes muy claros sobre la compatibilidad real del proyecto con los distintos frameworks que hayamos indicado:

Resultado del análisis de un ensamblado en el se muestra que el grado de compatibilidad con distintas plataformas es del 100%

Y si esta herramienta dice que mi biblioteca es 100% portable a .NET Core, ¿me encontraré algún problema?

Lo que indica el analizador es que no dará errores al compilar y al finalizar tendrás un ensamblado listo para utilizar en tus aplicaciones .NET Core, por lo que no debería aparecer ningún problema a la hora de portar la biblioteca. Hasta ahí, todo bien.

El detalle a tener en cuenta es que el hecho de que el 100% de las APIs usadas por nuestra biblioteca estén disponibles en el framework al que queremos portarla no implica que vaya a ejecutarse bien en todos los entornos. Por ejemplo, imaginad una biblioteca para .NET Framework 4.6.1, con un código como el siguiente:
using System;
using Microsoft.Win32;

public class RegistryUtils
{
    public static string GetArrowIcon()
    {
        var icon = (string)Registry.GetValue(
            @"HKEY_CURRENT_USER\Control Panel\Cursors", "Arrow", null);
        return icon;
    }
}
Si realizamos el test de portabilidad a esta biblioteca, el resultado que podríamos obtener sería el siguiente:

Resultado de portabilidad en el que se indica que la biblioteca es 100% compatible con .NET Framework 4.7.1, .NET Core 2.0, pero no con .NET Standard 2.0

Según este informe, nuestra biblioteca es 100% portable a .NET Framework 4.7.1 y a .NET Core 2.0. En la práctica, indica que este código podríamos compilarlo con el target .NET Framework 4.7.1 sin problema porque no ha aparecido ningún breaking change en el API desde la versión 4.6.1, y también compilaría con éxito para .NET Core 2.0.

Sin embargo, en el informe anterior también veíamos que nuestra biblioteca es compatible sólo en un 94,12% con .NET Standard, e incluso algo más abajo veíamos que los problemas están centrados en el uso de System.Win32.Registry, y es en esta parte donde debemos tener cuidado: el acceso al registro de Windows, como su propio nombre indica, no es multiplataforma.

Si este código lo ejecutamos sobre .NET 4.7.1 o .NET Core 2.0 en Windows, todo funcionará bien porque la implementación de ambos frameworks para este sistema operativo incluye los componentes de acceso al registro.

Pero si el mismo código nos lo llevamos a Linux, compilará también sin problemas; así nos lo había adelantado .NET Portability Analyzer al indicar que es 100% portable a .NET Core, que está disponible en este sistema operativo. Las sorpresas llegarán al ejecutarlo, pues obtendremos una estrepitosa excepción de tipo PlatformNotSupportedException, porque en este sistema operativo no existe -obviamente- el registro de Windows:
jmaguilar@laptop:~/console$ cat Program.cs
using System;
using Microsoft.Win32;

namespace console
{
   class Program
   {
      static void Main(string[] args)
      {
         var icon = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Control Panel\Cursors", "Arrow", null);
         Console.WriteLine("Icon: " + icon);
         Console.ReadLine();
      }
   }
}

jmaguilar@laptop:~/console$ dotnet build
Microsoft(R) Build Engine versión 15.4.8.50001 para .NET Core
Copyright (C) Microsoft Corporation. Todos los derechos reservados.

  console -> /home/jmaguilar/console/bin/Debug/netcoreapp2.0/console.dll

Compilación correcta.
    0 Advertencia(s)
    0 Errores

Tiempo transcurrido: 00:00:02:01

jmaguilar@laptop:~/console$ dotnet run

Unhandled Exception: System.TypeInitializationException: 
  The type initializer for 'Microsoft.Win32.Registry' threw an exception. 
  ---> System.PlatformNotSupportedException: Registry is not supported on this platform.
   at Microsoft.Win32.RegistryKey.OpenBaseKeyCore(RegistryHive hKey, RegistryView view)
   at Microsoft.Win32.Registry..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.Win32.Registry.GetBaseKeyFromKeyName(String keyName, String& subKeyName)
   at Microsoft.Win32.Registry.GetValue(String keyName, String valueName, Object defaultValue)
   at console.Program.Main(String[] args) in /home/jmaguilar/console/Program.cs:line 10
jmaguilar@laptop:~/console$ _
En conclusión, el hecho de que el Portability Analyzer nos indique que una biblioteca es 100% portable a .NET Core no quiere decir que ésta vaya a ejecutarse correctamente en todos los entornos en los que puede correr dicho framework (Mac, Linux, Windows). Si nuestra intención es que esta biblioteca sea cross-platform, será mejor que nos centremos en conseguir un 100% de portabilidad a .NET Standard, o bien tendremos que introducir en ella código condicional en función de las distintas plataformas a las que nos dirijamos.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!