ASP .Net core RC2 - Nouveautés et migrations d'un projet DNX

Depuis quelques jours ASP NET core RC2 est disponible, cette version a pour vocation de remplacer la version ASP NET core RC1. Cette nouvelle version amène son lot de nouveautés et il est maintenant nécessaire de migrer nos applications RC1 en RC2. Ce blog post a pour objectif de faire un tour d’horizon des nouveautés de la version RC2 et de partager les différentes étapes qui ont été nécessaire pour effectuer la migration d’un projet ASP NET core RC1.

Overview des changements apportées par la version RC2

  • Console App : Dans la version RC1, une application ASP NET était une « class library dnx » utilisant un fichier « startup.cs » pour se lancer. Dans la version RC2 L’application ASP NET est une application console : Un fichier « Program.cs » est maintenant présent à la racine du projet et contient méthode « main » qui a pour responsabilité de créer un « WebHost ».
  • Support de IIS : Un nouveau module “AspNetCoreModule” peut maintenant être utilisé dans le web.config pour que IIS héberge l’application.
  • .NET CLI : Avec cette version rc2, les outils DNX (DNVM « Dotnet version Manager », DNU « Dotnet Developer Utility », et DNX « Dotnet Execution Runtime ») sont remplacés par un outil unique : « .net command line interface »

 

DnxVSnetCli

 

Les commandes « dna wrap », « dnu commands », « dnu install » ne sont plus supportées par .net cli.

*(1) Avec un « dnx run » aucune compilation en mémoire n’était effectuée, avec « dotnet run », le code est d’abord compilé en bytecode avec « dotnet build » avant d’être exécuté.

*(2) : Une application peut être publiée dans deux modes :

« Portable » : L’application utilise une runtime .net Core installée sur la machine et partage cette runtime avec d’autre applications. Si la version de la runtime n’est pas installée sur la machine, l’application ne peut pas s’exécuter et lèvera une exception lors de l’utilisation de la commande « dotnet run ».

« Standalone » : L’application embarque la runtime .net Core dans son package, la runtime n’a donc pas besoin d’être installée au préalable sur la machine d’exécution.

*(3) Le concept de « commande globale » n’est plus supporté avec la RC2, il n’est plus possible de définir des commandes au niveau de la machine puis de les exécuter.

 

Installation

Pour pouvoir utiliser la RC2 sans conflits de version, il faut d’abord désinstaller la / les versions de DNX précédemment installée(s). En effectuant cette opération depuis l’interface « Programs and Features » de Windows, il peut être nécessaire de spécifier l’emplacement de l’exécutable d’installation pour effectuer la désinstallation. Si cet exécutable n’est plus présent ou inaccessible sur la machine, il suffit de télécharger l’exécutable original et de spécifier son path pour effectuer la désinstallation. Pour récupérer l’exécutable de la rc2, c’est par ici

 

Migration RC1 – RC2

Dans cet exemple de migration, le projet original est un projet ASP NET core RC1, ce projet utilise le framework .net 4.5.1 sans aucune version d’Entity Framework.

 

1. Les fichiers de configurations : global.json, launchSettings.json, project.json

 

global.json

Lors de la création d’un projet ASP NET core RC1, un fichier « global.json » est créé à la racine de la solution. Ce dernier permet de spécifier la version de la runtime « dnx.exe » à utiliser pour les projets de la solution (puisque plusieurs versions peuvent être installées sur une machine). Il faut donc modifier ce fichier global.json pour qu’il n’utilise plus une runtime « rc1 » mais la runtime rc2 par défaut. Pour cela il suffit de supprimer le nœud « sdk » :

// RC1 
{
  "projects": [ "myProjectName" ],
  "sdk":{ 
      "version" : "1.0.0-rc1-update1"
  }
}

// RC2
{
  "projects": [ "wrap" ]
}

Il est tout à fait possible de spécifier explicitement une version de la RC2 installée sur la machine. La version actuelle de la RC2 ciblée par défaut par les « templates » de projets Visual Studio est la suivante : 1.0.0-preview1-002702

 

launchSettings.json

Les “lauchsettings.json” sont des fichiers utilisés par « Visual Studio » pour persister des « profils de lancement de l’application ». Ces profils définissent les variables d’environnements à utiliser au lancement de l’application, ces variables d’environnements permettent de charger un fichier de configuration « appsettings.{environnemnt}.json » spécifique. La définition de ces profils a légèrement évolué :

//RC1 
"profiles": {
  "IIS Express": {
    "commandName": "IISExpress",
    "launchBrowser": true,
    "launchUrl": "api/values",
    "environmentVariables": {
      "Hosting:Environment": "Development"
    }
  },
  "web": {
    "commandName": "web",
    "environmentVariables": {
      "Hosting:Environment": "Development"
    }
  }
}

// RC2
"profiles": {
  "IIS Express": {
    "commandName": "IISExpress",
    "launchUrl": "api/values",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  },
  "web": {
    "commandName": "Project",
    "environmentVariables": {
      "BRISTOL_HideHost": "false",
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  }
}

Toutes les variables d’environnement doivent maintenant être préfixé par un _ et non par :

De plus, valeur « Project » dans la propriété « commandName » est importante, car elle permet à Visual Studio d’utiliser le profil au lancement de l’application.

Pour lancer l’application dans une configuration spécifique en ligne de commande, il faut tout d’abord définir la variable d’environnement puis exécuter l’application :

env

 

project.json

Maintenant que le projet utilise par défaut la runtime RC2, il est nécessaire d’effectuer des modifications dans le fichier « project.json ». Ce dernier utilise un nouveau shema de référence.

 

     1. Métadonnées du fichier

Toutes les métadonnées du projet.json (tags, projectUrl, licenceUrl…) doivent être placé à l’intérieur d’un nœud  « packoptions » :

//RC1
"tags": [ "aspnetcore", "rc2" ],
"projectUrl": "C:/Users/ThibautRanise/..."

// RC2
"packOptions": {
   "tags": [ "aspnetcore", "rc2" ],
   "projectUrl": "C:/Users/ThibautRanise/..."
}

 

     2. Information de compilation et point d’entrée

L’application utilise un fichier startup.cs pour être démarrée, ce point d’entrée est spécifié par une propriété « compilationOptions » qu’il modifier en « buildOption » :

// RC1
"compilationOptions": {
   "emitEntryPoint": true
},
  
// RC2 
"buildOptions": {
   "emitEntryPoint": true,
   "preserveCompilationContext": true
}

Pour une application MVC qui n’expose pas uniquement des API et qui utilise des « views » il est nécessaire de spécifier l’option : "preserveCompilationContext": true dans ce nœud « BuildOptions », sinon une exception sera levée :

preserveCompilationContextError

 

     3. Framework

Dans notre cas, la modification du Framework est relativement simple puisque nous profitons de cet update pour cibler la version 4.5.2 du framework .net :

// RC1
 "frameworks": {
    "dnx451": { }
  },

// RC2  
"frameworks": {
    "net452": { }
}

 

Si nous voulions cibler .net core depuis notre application, la syntaxe a également évolué, de plus, il est nécessaire d’ajouter la runtime dans les dépendances :

// RC1 
"frameworks": {
   "dnxcore50": { }
}

//RC2
"dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0-rc2-3002702",
      "type": "platform"
    }
        
"frameworks": {
 "netcoreapp1.0": {
   "imports": [
      "dotnet5.6",
      "dnxcore50",
      "portable-net45+win8"
   ]
 }
}

 

Pour toute « class library » qui ciblaient auparavant « dnxcore50 », il faut maintenant cibler la librairie .net standard :

"dependencies": {
  "NETStandard.Library": "1.5.0-rc2-24027"
},

"frameworks": {
  "netstandard1.5": {
    "imports": "dnxcore50"
  }
}

 

     4. Dépendances

L’ensemble des packages doivent être passés en RC2, deux opération sont à effectuer :

  1. Changer les noms de package contenant « AspNet » en « AspNetCore »
  2. Changer le contenu du numéro version en rc2

 

     5. Informations de publications

Les configurations relatives à la publication de l’application doivent maintenant être contenu dans une propriété : « PublishOptions », il d’ailleurs possible de spécifier les fichiers à exclure et à inclure lors de la publication :

// RC1
"publishExclude": [
   "**.user",
   "**.vspscc"
]

// RC2
"publishOptions": {
   "include": [
     "wwwroot",
     "Views",
     "appsettings.json"
   ]
}

 

2. Ajout du point d’entrée : « Program.cs »

En RC1, le point d’entrée de l’application était définie dans le fichier « startup.cs » par la ligne de code suivante :

OldStartup

 

En RC2, cette ligne n’est plus nécessaire, il faut donc la supprimer et créer un fichier « Program.cs », à la racine du projet. Le point d’entrée est une méthode statique « main » classique. Cette dernière a pour role de créer un « web host » et de l’exécuter par l’intermédiaire d’un object de type IWebHostBuilder. Il est possible de passer une configuration spécifique à ce builder, par exemple exécuter l’application web sur un port spécifique :

public static void Main(string[] args)
{
    var basePath = Directory.GetCurrentDirectory();

    using (var host
        = new WebHostBuilder()
        .UseContentRoot(basePath)
        .UseKestrel()
        .UseStartup<Startup>()
        .UseUrls($"http://localhost:5001")
        .Build()
        )
    {
        Console.WriteLine("-- Web app is running --");
        host.Run();
        Console.ReadLine();
    }
}

 

3. Modification du code

Quelques modifications sont à effectuer au niveau du code : Des namespaces ont été renommés, l’interface ILogger et le fichier startup doivent être complétés :

 

     1. Startup.cs

Une petite modification doit être effectuée dans le constructeur du « startup.cs ». Il faut spécifier au « configurationBuilder » le path à utiliser pour charger les fichiers de configurations « appsettings.*.json » :

RC1 : 
public Startup(IHostingEnvironment env)
{
       // Set up configuration sources.
      var builder = new ConfigurationBuilder()
                 .AddJsonFile("appsettings.json")
                 .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                 .AddEnvironmentVariables();
      
       builder.Build();
}

//RC2
public Startup(IHostingEnvironment env)
{
      // Set up configuration sources.
      var builder = new ConfigurationBuilder()
             .SetBasePath(env.ContentRootPath)
             .AddJsonFile("appsettings.json")
             .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
             .AddEnvironmentVariables();

      builder.Build();
}

 

     2. Namespace “AspNetCore”

Tout comme les noms de packages dans le fichier « project.json », les namespaces contenant « AspNet » ont été modifié pour contenir « AspNetCore ». Tous les namespaces de types « using *AspNet* » doivent être renommés en « using *AspNetCore* ».

 

     3.  Logging

Le niveau de log « Debug » n’existe plus, et puisqu’il est défini en tant que niveau de log par défaut dans le fichier « appSettings.json », il est nécessaire de changer le niveau de log par défaut par « information » par exemple.

La signature de la méthode « Log » de l’interface « ILogger » a également été modifiée :

// RC1
void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)

// RC2 
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);

Enfin, le paramètre « formatter » de cette méthode « Log » provoquera une exception s’il est null. Il est donc essentiel de lui passer une « func » non null :

Func<LogModel, Exception, string> formatter = ((model, exception) => $"{model} - {exception}");

 

 

Quelques changement à prendre en compte au niveau du Framework MVC

 

Modification du « validation Sumary »

En RC1, l’attribut HTML « asp-validation-summary » avait pour valeur "validationSummary.All », la syntaxe est maintenant plus légère et moins verbeuse :

<div asp-validation-summary="All" class="text-danger"></div>

 

Des objets anonymes pour invoquer les « View Component »

Pour « invoquer » un view component, il est maintenant nécessaire d’utiliser un objet anonyme comme paramètre de la méthode « @Component.Invoke » :

@Component.InvokeAsync<MyComponent>(new { name = "MyComponent" })

 

La fin du .xproj et du project.json

De façon à pouvoir conserver le partage du code avec tous les types d’applications .net, le « project.json » que nous avons maintenant l’habitude d’utiliser dans les projets .net core va disparaitre ! De même les projets .net core utiliseront maintenant un fichier .csproj à la place du .xproj et le contenu du project.json sera présent au sein du .csproj. Cette modification est annoncée mais il va falloir attendre encore un peu avant qu’elle ne soit mise en place ! pour plus d’information sur ce point, c’est par ici !

 

Happy coding Sourire

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus