<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Laurent Mondeil</title>
    <description>The latest articles on DEV Community by Laurent Mondeil (@lmondeil).</description>
    <link>https://dev.to/lmondeil</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4121045%2F0fd67c64-0140-438d-aa4d-c2e998e8790b.png</url>
      <title>DEV Community: Laurent Mondeil</title>
      <link>https://dev.to/lmondeil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lmondeil"/>
    <language>en</language>
    <item>
      <title>Concevoir une CLI moderne en .NET : retour d’expérience</title>
      <dc:creator>Laurent Mondeil</dc:creator>
      <pubDate>Tue, 29 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/onepoint/concevoir-une-cli-moderne-en-net-retour-dexperience-27kg</link>
      <guid>https://dev.to/onepoint/concevoir-une-cli-moderne-en-net-retour-dexperience-27kg</guid>
      <description>&lt;p&gt;Dans de nombreux projets, les opérations de maintenance finissent par devenir un sujet à part entière. C’est exactement ce qui nous est arrivé lors d’un projet reposant sur &lt;strong&gt;Azure Cosmos DB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Très vite, nous avons dû mettre en place des opérations récurrentes : suppression de documents selon certains critères, sauvegardes ponctuelles, vérifications ciblées… Autant de tâches qu’il fallait pouvoir exécuter aussi bien depuis nos postes de travail que depuis des serveurs &lt;em&gt;headless&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Dans cet article, je vous propose un retour d’expérience sur la conception d’une &lt;strong&gt;CLI en .NET&lt;/strong&gt;, pensée pour être :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;structurée&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;évolutive&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;simple à distribuer&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;et &lt;strong&gt;confortable à utiliser&lt;/strong&gt; au quotidien.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Du script à l’outil… puis à la complexité
&lt;/h2&gt;

&lt;p&gt;Au départ, la solution semblait évidente : créer une petite application console capable d’exécuter les opérations nécessaires sur la base Cosmos DB.&lt;br&gt;
La première version répondait au besoin de suppression. Une seconde a rapidement vu le jour pour gérer les sauvegardes. Puis une troisième est venue couvrir un besoin plus spécifique.&lt;/p&gt;

&lt;p&gt;Le problème ?&lt;br&gt;
Chaque nouvel outil partageait une large base de code commune. La multiplication de ces applications devenait contre‑productive et difficile à maintenir.&lt;/p&gt;

&lt;p&gt;Nous avons donc décidé de &lt;strong&gt;fusionner l’ensemble dans un seul outil&lt;/strong&gt;. Techniquement, l’objectif était atteint… mais un nouveau problème est apparu : la &lt;strong&gt;lisibilité de l’interface en ligne de commande&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;TheSuperApp &lt;span class="nt"&gt;-operation&lt;/span&gt; save &lt;span class="nt"&gt;-saveoption&lt;/span&gt; file &lt;span class="nt"&gt;-format&lt;/span&gt; json &lt;span class="nt"&gt;-location&lt;/span&gt; &lt;span class="s2"&gt;"c:&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;verything&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s2"&gt;ackup-01.json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-query&lt;/span&gt; &lt;span class="s2"&gt;"SELECT * FROM c WHERE c.objectType = 'obsolete'"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-connectionstring&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;connectionstring&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt; -database &amp;lt;&amp;lt;database&amp;gt;&amp;gt;

TheSuperApp -operation delete &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
  -query "SELECT * FROM c WHERE c.objectType = 'obsolete'" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
  -connectionstring &amp;lt;&amp;lt;connectionstring&amp;gt;&amp;gt; -database &amp;lt;&amp;lt;database&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avec ce type d’appel, il devenait rapidement difficile de savoir :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quels paramètres étaient obligatoires,&lt;/li&gt;
&lt;li&gt;lesquels s’appliquaient à une opération donnée,&lt;/li&gt;
&lt;li&gt;et comment faire évoluer l’outil sans le rendre encore plus illisible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il devenait clair qu’il fallait &lt;strong&gt;repenser l’interface de la CLI&lt;/strong&gt;, pas seulement son implémentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structurer la CLI autour de verbes
&lt;/h2&gt;

&lt;p&gt;La première décision structurante a été de concevoir la CLI autour de &lt;strong&gt;verbes représentant des actions&lt;/strong&gt;, à la manière des commandes Git ou Docker.&lt;/p&gt;

&lt;p&gt;Plutôt qu’un bloc unique de paramètres, nous avons opté pour :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;des &lt;strong&gt;commandes de premier niveau&lt;/strong&gt; (&lt;code&gt;save&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;puis des &lt;strong&gt;sous-commandes&lt;/strong&gt; pour préciser le comportement (&lt;code&gt;to-file&lt;/code&gt;, &lt;code&gt;to-litedb&lt;/code&gt;, etc.),&lt;/li&gt;
&lt;li&gt;chacune disposant de &lt;strong&gt;ses propres paramètres&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette approche permet :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une meilleure lisibilité,&lt;/li&gt;
&lt;li&gt;une aide en ligne plus claire,&lt;/li&gt;
&lt;li&gt;et une évolutivité bien plus saine.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;---
config:
  look: neo
  theme: forest
  layout: elk
  wrap: true
---
flowchart LR

    classDef cli fill:lightgreen
    classDef parameter fill:#bcbccc
    classDef globalparameter fill:#feaf5f
    classDef value fill:#75757f,color:white

    cli:::cli --&amp;gt; save &amp;amp; select &amp;amp; delete
    save --&amp;gt; to-file &amp;amp; to-litedb
    to-file --&amp;gt; query1&amp;gt;query]:::parameter &amp;amp; location&amp;gt;location]:::parameter &amp;amp; format&amp;gt;format]
    format:::parameter --&amp;gt; json((json)):::value &amp;amp; yaml((yaml)):::value
    to-litedb --&amp;gt; query2&amp;gt;query]:::parameter &amp;amp; litedbconnectionstring&amp;gt;litedbconnection-string]:::parameter
    select --&amp;gt; query3&amp;gt;query]:::parameter
    delete --&amp;gt; query4&amp;gt;query]:::parameter

    select &amp;amp; delete &amp;amp; to-file &amp;amp; to-litedb ----&amp;gt; cnx&amp;gt;connection-string]:::parameter
    select &amp;amp; delete &amp;amp; to-file &amp;amp; to-litedb ----&amp;gt; db&amp;gt;database]:::parameter&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Implémentation avec CommandLineUtils
&lt;/h2&gt;

&lt;p&gt;Afin de ne pas repartir de zéro, nous avons choisi d’utiliser &lt;a href="https://github.com/natemcmaster/CommandLineUtils" rel="noopener noreferrer"&gt;&lt;strong&gt;CommandLineUtils de McMaster&lt;/strong&gt;&lt;/a&gt;. La bibliothèque coche rapidement toutes les cases :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hiérarchisation naturelle des commandes et sous‑commandes,&lt;/li&gt;
&lt;li&gt;génération automatique de l’aide,&lt;/li&gt;
&lt;li&gt;intégration avec le &lt;a href="https://learn.microsoft.com/dotnet/core/extensions/generic-host" rel="noopener noreferrer"&gt;&lt;strong&gt;Generic Host&lt;/strong&gt;&lt;/a&gt; de .NET.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La définition de la hiérarchie et des paramètres se fait directement via des attributs : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attributs de classe pour a hiérarchie (Command, Subcommand)&lt;/li&gt;
&lt;li&gt;Attributs de propriété pour les paramètres (Option, Argument)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"save"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Subcommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ToFileCommand&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ToLiteDbCommand&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SaveCommand&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"to-file"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToFileCommand&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Location&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Résultat : une aide en ligne claire, générée automatiquement, et sans effort supplémentaire à maintenir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;TheSuperCli &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: TheSuperCli [command] [options]

Options:
  -f|--full-help  FullHelp
  -?|-h|--help    Show help information.

Commands:
  delete
  select
  save

Run 'TheSuperCli [command] -?|-h|--help' for more information about a command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Distribuer l’outil simplement : le choix du .NET Tool
&lt;/h2&gt;

&lt;p&gt;Une fois l’outil fonctionnel, s’est posée la question de sa diffusion. &lt;/p&gt;

&lt;p&gt;Nous avons opté pour &lt;a href="https://learn.microsoft.com/dotnet/core/tools/global-tools" rel="noopener noreferrer"&gt;&lt;strong&gt;.NET Tool&lt;/strong&gt;&lt;/a&gt; qui est un format de package nuget particulier.&lt;/p&gt;

&lt;p&gt;Ce choix apporte des bénéfices immédiats :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La distribution s'effectue depuis &lt;strong&gt;n'importe quel feed nuget&lt;/strong&gt; - nous avons choisi nuget.org,&lt;/li&gt;
&lt;li&gt;L'installation ne nécessite &lt;strong&gt;pas de privilège administrateur&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;L'installation et la mise à jour s'effectuent &lt;strong&gt;en ligne de commande&lt;/strong&gt; (pratique pour les systèmes headless),&lt;/li&gt;
&lt;li&gt;L'obtention d'une &lt;strong&gt;version spécifique est un argument&lt;/strong&gt; de la ligne de commande,&lt;/li&gt;
&lt;li&gt;Il n'y a pas d'autre &lt;strong&gt;prérequis&lt;/strong&gt; que &lt;strong&gt;.NET Tool&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;TheSuperApp&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt;
dotnet tool update &amp;lt;&amp;lt;TheSuperApp&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le packaging est simple : quelques propriétés suffisent dans le fichier projet pour transformer une application console en outil installable globalement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;

    [...]

    &lt;span class="nt"&gt;&amp;lt;PackAsTool&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PackAsTool&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ToolCommandName&amp;gt;&lt;/span&gt;contacts&lt;span class="nt"&gt;&amp;lt;/ToolCommandName&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageOutputPath&amp;gt;&lt;/span&gt;./nupkg&lt;span class="nt"&gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gérer le contexte
&lt;/h2&gt;

&lt;p&gt;À l’usage, un point d’irritation est vite apparu : devoir ressaisir à chaque exécution la chaîne de connexion et le nom de la base de données.&lt;/p&gt;

&lt;p&gt;Grâce à l’utilisation du &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host" rel="noopener noreferrer"&gt;&lt;strong&gt;Generic Host&lt;/strong&gt;&lt;/a&gt;, nous avons pu exploiter les fichiers de configuration tels que  &lt;code&gt;appsettings.json&lt;/code&gt;.&lt;br&gt;
Dans une CLI (sans &lt;code&gt;IOptions&lt;/code&gt;), ces fichiers sont lus au démarrage et peuvent être modifiés par l’application elle‑même par la suite.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxpl5dasavbosideewk6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxpl5dasavbosideewk6r.png" alt="decrit l'utilisation de l'appsettings pour le generic host" width="361" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(1) Lecture des valeurs de &lt;code&gt;connection-string&lt;/code&gt; et &lt;code&gt;database&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nous avons donc ajouté des commandes permettant :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;d’enregistrer la chaîne de connexion,&lt;/li&gt;
&lt;li&gt;de stocker le nom de la base de données,&lt;/li&gt;
&lt;li&gt;et de les afficher à tout moment.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;TheSuperCli settings &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--connection-string&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;connectionstring&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt;
TheSuperCli settings set --database &amp;lt;&amp;lt;database&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1entsbse4g1we5shwys3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1entsbse4g1we5shwys3.png" alt="décris comment persister des paramètre de commande en appsettings" width="291" height="73"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;TheSuperCli settings show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous n'avions alors plus à saisir ces paramètres en ligne de commande une fois enregistrés.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un pas de plus : la gestion multi‑environnements
&lt;/h2&gt;

&lt;p&gt;Un dernier point est apparu assez naturellement : un seul jeu de paramètres ne suffisait plus.&lt;br&gt;
Nous travaillions sur &lt;strong&gt;trois environnements distincts&lt;/strong&gt; (dev, rec, prd).&lt;/p&gt;

&lt;p&gt;Nous avons donc introduit la notion d’&lt;strong&gt;environnement&lt;/strong&gt;, chacun d'entre eux correspondant à un fichier &lt;code&gt;appsettings.&amp;lt;env&amp;gt;.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flc3cw8ia5v25f29ke23b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flc3cw8ia5v25f29ke23b.png" alt="1 appsettings par environnement" width="412" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les commandes inhérentes aux settings ont ainsi été enrichies de sorte à accueillir le paramètre &lt;code&gt;--env&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;exemple&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;TheSuperCli settings &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--database&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt; --env prod
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selon la présence ou non du paramètre &lt;code&gt;--env&lt;/code&gt;, la CLI :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;met à jour &lt;code&gt;appsettings.json&lt;/code&gt; (&lt;code&gt;--env&lt;/code&gt; absent),&lt;/li&gt;
&lt;li&gt;ou cible un fichier &lt;code&gt;app.settings.&amp;lt;&amp;lt;env&amp;gt;&amp;gt;.json&lt;/code&gt; spécifique à l’environnement (&lt;code&gt;--env&lt;/code&gt; présent).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Des commandes supplémentaires permettent :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;de sauvegarder un environnement,&lt;/li&gt;
&lt;li&gt;de basculer de l’un à l’autre,&lt;/li&gt;
&lt;li&gt;ou de supprimer une configuration devenue inutile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus de détails au paragraphe Gestion multi-environnements&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;---
config:
  look: neo
  theme: forest
  layout: elk
  wrap: true
---
flowchart LR

    classDef cli fill:lightgreen
    classDef parameter fill:#bcbccc
    classDef globalparameter fill:#feaf5f
    classDef value fill:#75757f,color:white

    envparam&amp;gt;env]:::parameter

    TheSuperCli:::cli --&amp;gt; settings --&amp;gt; set --&amp;gt; connection-string &amp;amp; database
    connection-string --&amp;gt; cnxstringparam&amp;gt;connection-string]:::parameter
    connection-string ---&amp;gt; envparam
    database --&amp;gt; databaseparam&amp;gt;database]:::parameter
    database ---&amp;gt; envparam

    TheSuperCli --&amp;gt; show ---&amp;gt; envparam
    TheSuperCli --&amp;gt; save ---&amp;gt; envparam
    TheSuperCli --&amp;gt; switchto ---&amp;gt; envparam
    TheSuperCli --&amp;gt; delete ---&amp;gt; envparam&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Démarrer plus vite grâce à un template
&lt;/h2&gt;

&lt;p&gt;Pour capitaliser sur ce travail, un &lt;a href="https://www.nuget.org/packages/lmondeil.cli.template" rel="noopener noreferrer"&gt;&lt;strong&gt;template de CLI .NET&lt;/strong&gt;&lt;/a&gt; disponible sur nuget.org intègre &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;CommandLineUtils,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;le packaging en .NET Tool,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;et la gestion des environnements.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;lmondeil.cli.template
dotnet new lmondeil.cli &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;cli&lt;/span&gt;&lt;span class="sh"&gt; name&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En quelques minutes, vous disposez d’un socle propre, structuré et prêt à être spécialisé.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ce retour d’expérience montre qu’une CLI est bien plus qu’un simple point d’entrée technique.&lt;br&gt;
Lorsqu’elle est pensée comme un &lt;strong&gt;produit à part entière&lt;/strong&gt;, elle apporte :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;de la clarté,&lt;/li&gt;
&lt;li&gt;de la robustesse,&lt;/li&gt;
&lt;li&gt;et un vrai confort d’usage au quotidien.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Annexes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Gestion multi-environnements
&lt;/h3&gt;

&lt;p&gt;Le fonctionnement des commandes de gestion de environnement est le suivant&lt;/p&gt;
&lt;h4&gt;
  
  
  settings set
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;TheSuperCli settings set --connection-string &amp;lt;&amp;lt;connectionstring&amp;gt;&amp;gt; --env &amp;lt;&amp;lt;env&amp;gt;&amp;gt;&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;TheSuperCli settings set --database &amp;lt;&amp;lt;database&amp;gt;&amp;gt; --env &amp;lt;&amp;lt;env&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Définit les paramètres de l'environnement actuel (non nommé)&lt;/p&gt;

&lt;p&gt;⇒ enregistre le paramètre spécifié dans le fichier appsettings.&amp;lt;&amp;gt;.json &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrwmueq8h55yxi17p0qg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrwmueq8h55yxi17p0qg.png" alt="schéma descriptif de la persistence d'un paramètre dans l'appsettings.json" width="291" height="73"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  settings save
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;TheSuperCli settings save --env &amp;lt;&amp;lt;env&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enregistre les paramètres actuels en tant que paramètres d'un environnement spécifique&lt;/p&gt;

&lt;p&gt;⇒ copie &lt;code&gt;app.settings.json&lt;/code&gt; en &lt;code&gt;app.settings.&amp;lt;&amp;lt;env&amp;gt;&amp;gt;.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcfdme10edi0pjwznq5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcfdme10edi0pjwznq5s.png" alt="décrit la copie de l'appsettings spécifique à l'environnement en appsettings par défaut" width="351" height="66"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  settings switchto
&lt;/h4&gt;

&lt;p&gt;Charge les paramètres de l'environnement &lt;code&gt;&amp;lt;&amp;lt;env&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;⇒ remplace le contenu de&lt;code&gt;appsettings.json&lt;/code&gt; par celui de &lt;code&gt;app.settings.&amp;lt;&amp;lt;env&amp;gt;&amp;gt;.json&lt;/code&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suppression de appsettings.json&lt;/li&gt;
&lt;li&gt;copie de &lt;code&gt;appsettings.&amp;lt;&amp;lt;env&amp;gt;&amp;gt;.json&lt;/code&gt; en &lt;code&gt;appsettings.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2t52fh5pal19ec9pof91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2t52fh5pal19ec9pof91.png" alt="décrit la suppression d'un appsettings en vue de son remplacement" width="351" height="185"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Publier un package nuget sur nuget.org
&lt;/h3&gt;

&lt;p&gt;Cette opération nécessite que vous ayez obtenu une clé d'api auprès de nuget.org&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet nuget push &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;PathToPackage&lt;/span&gt;&lt;span class="sh"&gt;&amp;gt;&amp;gt;.nupkg --source https://api.nuget.org/v3/index.json --api-key &amp;lt;&amp;lt;NuGet API key&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>architecture</category>
      <category>cli</category>
      <category>dotnet</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
