DEV Community: Ricardo The latest articles on DEV Community by Ricardo (@rmaurodev). https://dev.to/rmaurodev https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F254246%2F9ab89083-8324-454c-b9a9-48948303a30e.jpeg DEV Community: Ricardo https://dev.to/rmaurodev en Logging Performance Improvements with Source Generators in C# .NET Ricardo Wed, 16 Jul 2025 10:00:00 +0000 https://dev.to/rmaurodev/logging-performance-improvements-with-source-generators-in-c-net-531c https://dev.to/rmaurodev/logging-performance-improvements-with-source-generators-in-c-net-531c <p>In recent .NET releases, source generators have become a game-changer, offering new possibilities for compile-time code generation. One area where source generators have made a significant impact is logging.</p> <p>In this post, we will explore how to use source generators for logging, the improvements and benefits they bring, and some practical tips to get the most out of this feature.</p> <h2> Logging with Source Generators </h2> <p>Using source generators for logging in .NET can help streamline logging calls, reduce overhead, and provide more efficient, type-safe logging.</p> <p>The <code>Microsoft.Extensions.Logging</code> namespace has integrated support for source-generated logging, which we will explore in detail.</p> <h3> Setting Up Source-Generated Logging </h3> <p>To get started, you need to ensure you have the necessary packages. Typically, you would include the following in your project:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>dotnet add package Microsoft.Extensions.Logging dotnet add package Microsoft.Extensions.Logging.Abstractions </code></pre> </div> <p>Next, you need to enable the logging source generator. This is done by defining logging methods in a static partial class with the <code>LoggerMessage</code> attribute.</p> <p>Here’s an example:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">Log</span> <span class="p">{</span> <span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span><span class="n">EventId</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span> <span class="n">Message</span> <span class="p">=</span> <span class="s">"Processing item {ItemId}"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItem</span><span class="p">(</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">);</span> <span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span><span class="n">EventId</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span> <span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span> <span class="n">Message</span> <span class="p">=</span> <span class="s">"Failed to process item {ItemId}: {ErrorMessage}"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItemFailed</span><span class="p">(</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">errorMessage</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h3> Using the Generated Logging Methods </h3> <p>With the logging methods defined, you can use them in your application as follows:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">ProcessItem</span><span class="p">(</span><span class="kt">int</span> <span class="n">itemId</span><span class="p">)</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="c1">// Processing logic here</span> <span class="n">Log</span><span class="p">.</span><span class="nf">ProcessingItem</span><span class="p">(</span><span class="n">_logger</span><span class="p">,</span> <span class="n">itemId</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span> <span class="p">{</span> <span class="n">Log</span><span class="p">.</span><span class="nf">ProcessingItemFailed</span><span class="p">(</span><span class="n">_logger</span><span class="p">,</span> <span class="n">itemId</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2> Behind the Scenes: The Generated Code </h2> <p>During the compilation of the project the partial class for <code>Log</code> will generated and compiled along with the source code.</p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctz2pwke1oswzajjhjz7.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctz2pwke1oswzajjhjz7.png" alt="Logging Performance Improvements with Source Generators in C# .NET" width="800" height="317"></a></p> <p>Observe the generated code for the logging methods.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">partial</span> <span class="k">class</span> <span class="nc">Log</span> <span class="p">{</span> <span class="c1">/// &lt;summary&gt;</span> <span class="c1">/// Logs "Processing item {ItemId}" at "Information" level.</span> <span class="c1">/// &lt;/summary&gt;</span> <span class="p">[</span><span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">CodeDom</span><span class="p">.</span><span class="n">Compiler</span><span class="p">.</span><span class="nf">GeneratedCodeAttribute</span><span class="p">(</span><span class="s">"Microsoft.Gen.Logging"</span><span class="p">,</span> <span class="s">"8.2.0.0"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItem</span><span class="p">(</span><span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(!</span><span class="n">logger</span><span class="p">.</span><span class="nf">IsEnabled</span><span class="p">(</span><span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">))</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="kt">var</span> <span class="n">state</span> <span class="p">=</span> <span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">LoggerMessageHelper</span><span class="p">.</span><span class="n">ThreadLocalState</span><span class="p">;</span> <span class="n">_</span> <span class="p">=</span> <span class="n">state</span><span class="p">.</span><span class="nf">ReserveTagSpace</span><span class="p">(</span><span class="m">2</span><span class="p">);</span> <span class="n">state</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"ItemId"</span><span class="p">,</span> <span class="n">itemId</span><span class="p">);</span> <span class="n">state</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"{OriginalFormat}"</span><span class="p">,</span> <span class="s">"Processing item {ItemId}"</span><span class="p">);</span> <span class="n">logger</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="k">nameof</span><span class="p">(</span><span class="n">ProcessingItem</span><span class="p">)),</span> <span class="n">state</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="p">[</span><span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">CodeDom</span><span class="p">.</span><span class="n">Compiler</span><span class="p">.</span><span class="nf">GeneratedCodeAttribute</span><span class="p">(</span><span class="s">"Microsoft.Gen.Logging"</span><span class="p">,</span> <span class="s">"8.2.0.0"</span><span class="p">)]</span> <span class="k">static</span> <span class="kt">string</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">itemId</span> <span class="p">=</span> <span class="n">s</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">Value</span><span class="p">;</span> <span class="k">return</span> <span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">FormattableString</span><span class="p">.</span><span class="nf">Invariant</span><span class="p">(</span><span class="s">$"Processing item </span><span class="p">{</span><span class="n">itemId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="p">});</span> <span class="n">state</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span> <span class="p">}</span> <span class="c1">/// &lt;summary&gt;</span> <span class="c1">/// Logs "Failed to process item {ItemId}: {ErrorMessage}" at "Error" level.</span> <span class="c1">/// &lt;/summary&gt;</span> <span class="p">[</span><span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">CodeDom</span><span class="p">.</span><span class="n">Compiler</span><span class="p">.</span><span class="nf">GeneratedCodeAttribute</span><span class="p">(</span><span class="s">"Microsoft.Gen.Logging"</span><span class="p">,</span> <span class="s">"8.2.0.0"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItemFailed</span><span class="p">(</span><span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">errorMessage</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">state</span> <span class="p">=</span> <span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">LoggerMessageHelper</span><span class="p">.</span><span class="n">ThreadLocalState</span><span class="p">;</span> <span class="n">_</span> <span class="p">=</span> <span class="n">state</span><span class="p">.</span><span class="nf">ReserveTagSpace</span><span class="p">(</span><span class="m">3</span><span class="p">);</span> <span class="n">state</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">2</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"ItemId"</span><span class="p">,</span> <span class="n">itemId</span><span class="p">);</span> <span class="n">state</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"ErrorMessage"</span><span class="p">,</span> <span class="n">errorMessage</span><span class="p">);</span> <span class="n">state</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"{OriginalFormat}"</span><span class="p">,</span> <span class="s">"Failed to process item {ItemId}: {ErrorMessage}"</span><span class="p">);</span> <span class="n">logger</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="k">global</span><span class="p">::</span><span class="n">Microsoft</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span> <span class="k">new</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="k">nameof</span><span class="p">(</span><span class="n">ProcessingItemFailed</span><span class="p">)),</span> <span class="n">state</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="p">[</span><span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">CodeDom</span><span class="p">.</span><span class="n">Compiler</span><span class="p">.</span><span class="nf">GeneratedCodeAttribute</span><span class="p">(</span><span class="s">"Microsoft.Gen.Logging"</span><span class="p">,</span> <span class="s">"8.2.0.0"</span><span class="p">)]</span> <span class="k">static</span> <span class="kt">string</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">itemId</span> <span class="p">=</span> <span class="n">s</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">Value</span><span class="p">;</span> <span class="kt">var</span> <span class="n">errorMessage</span> <span class="p">=</span> <span class="n">s</span><span class="p">.</span><span class="n">TagArray</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">Value</span> <span class="p">??</span> <span class="s">"(null)"</span><span class="p">;</span> <span class="k">return</span> <span class="k">global</span><span class="p">::</span><span class="n">System</span><span class="p">.</span><span class="n">FormattableString</span><span class="p">.</span><span class="nf">Invariant</span><span class="p">(</span><span class="s">$"Failed to process item </span><span class="p">{</span><span class="n">itemId</span><span class="p">}</span><span class="s">: </span><span class="p">{</span><span class="n">errorMessage</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="p">});</span> <span class="n">state</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2> Improvements and Benefits </h2> <ol> <li><p><strong>Performance</strong> : Source-generated logging methods are more performant than traditional logging methods because the code is generated at compile time, avoiding reflection and dynamic code generation at runtime.</p></li> <li><p><strong>Type Safety</strong> : The generated methods provide type-safe logging. If you change the parameters or the message template, you will get compile-time errors instead of runtime issues.</p></li> <li><p><strong>Reduced Boilerplate</strong> : You write the logging methods once, and the source generator takes care of the rest, reducing the amount of repetitive code in your project.</p></li> <li><p><strong>Consistency</strong> : Ensures that logging messages are consistent across the application, as they are defined in one place.</p></li> </ol> <h2> Practical Tips </h2> <ul> <li><p><strong>Use Descriptive Names</strong> : When defining logging methods, use descriptive names and message templates to make it clear what each log entry represents.</p></li> <li><p><strong>Keep It Organized</strong> : Group related logging methods in the same partial class to keep your code organized.</p></li> <li><p><strong>Event IDs</strong> : Use unique event IDs for each logging method to help with filtering and analyzing logs.</p></li> <li><p><strong>Log Levels</strong> : Carefully choose the appropriate log level (e.g., Information, Error, Warning) to avoid cluttering your logs with unnecessary information.</p></li> <li><p><strong>Template Matching</strong> : Ensure that the message templates match the method parameters exactly to avoid runtime errors.</p></li> </ul> <h3> Keeping Organized </h3> <p>Group related logging methods in the same partial class to keep your code organized.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span> <span class="k">namespace</span> <span class="nn">TodoList</span><span class="p">;</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">Log</span> <span class="p">{</span> <span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span><span class="n">EventId</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span> <span class="n">Message</span> <span class="p">=</span> <span class="s">"Processing item {ItemId}"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItem</span><span class="p">(</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">);</span> <span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span><span class="n">EventId</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span> <span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span> <span class="n">Message</span> <span class="p">=</span> <span class="s">"Failed to process item {ItemId}: {ErrorMessage}"</span><span class="p">)]</span> <span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">ProcessingItemFailed</span><span class="p">(</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">,</span> <span class="kt">int</span> <span class="n">itemId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">errorMessage</span><span class="p">);</span> <span class="p">}</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">Todo</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Todo</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span> <span class="p">{</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">ProcessItem</span><span class="p">(</span><span class="kt">int</span> <span class="n">itemId</span><span class="p">)</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="c1">// Processing logic here</span> <span class="n">Log</span><span class="p">.</span><span class="nf">ProcessingItem</span><span class="p">(</span><span class="n">logger</span><span class="p">,</span> <span class="n">itemId</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span> <span class="p">{</span> <span class="n">Log</span><span class="p">.</span><span class="nf">ProcessingItemFailed</span><span class="p">(</span><span class="n">logger</span><span class="p">,</span> <span class="n">itemId</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2> Conclusion </h2> <p>Source generators provide a powerful way to improve logging in .NET applications.</p> <p>By generating logging code at compile time, you can achieve better performance, type safety, and consistency. Incorporate source-generated logging into your projects to take advantage of these benefits and streamline your logging implementation.</p> <p>If you have any questions or would like to see more examples, feel free to reach out. Happy coding!</p> <p>For more detailed articles and hands-on tutorials on .NET and C#, check out my blog at <a href="proxy.php?url=https://rmauro.dev/" rel="noopener noreferrer">rmauro.dev</a>. Subscribe to my newsletter, Developers Garage, for weekly updates and curated content.</p> csharp dotnet logging Make Your Machine Talk: Piper TTS Offline Ricardo Tue, 08 Jul 2025 00:26:01 +0000 https://dev.to/rmaurodev/make-your-machine-talk-piper-tts-offline-36da https://dev.to/rmaurodev/make-your-machine-talk-piper-tts-offline-36da <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff28uw91iej6t4bf2uxsn.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff28uw91iej6t4bf2uxsn.png" alt="Make Your Machine Talk: Piper TTS Offline" width="800" height="533"></a></p> <p>Want to make your computer <em>talk</em> — using real, natural-sounding voices without needing the cloud?</p> <p>In this tutorial, we’ll set up <a href="proxy.php?url=https://github.com/rhasspy/piper" rel="noopener noreferrer"><strong>Piper TTS</strong></a> on a local system and give it a voice. It’s fast, offline, and perfect for voice assistants, robotics, Raspberry Pi projects, or just impressing your friends.</p> <h2> What You’ll Need </h2> <ul> <li>Python 3 installed (recommended: Python 3.8 or newer)</li> <li>Linux environment (x86_64 or ARM64) - or Windows with WSL</li> <li>Internet connection (just for setup)</li> <li>A speaker or headphones</li> <li>Terminal access</li> </ul> <blockquote> <p>I have tested on Raspberry Pi OS 64-bit (Bookworm), Ubuntu 22.04, and Windows 11 using WSL. Works on most modern 64-bit Linux environments.</p> </blockquote> <h2> Install Piper Using pip </h2> <p>Piper can now be installed easily via pip, but to avoid issues with system-managed Python environments, it’s best to use a virtual environment.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>python3 <span class="nt">-m</span> venv .venv <span class="nb">source</span> .venv/bin/activate pip <span class="nb">install </span>piper-tts </code></pre> </div> <h2> Add a Voice </h2> <p>Piper uses ONNX-based voice models and needs both the <code>.onnx</code> model <strong>and</strong> its matching <code>.json</code> config file.</p> <p>You can browse the full list of supported voices here:</p> <ul> <li><a href="proxy.php?url=https://github.com/rhasspy/piper/blob/master/VOICES.md" rel="noopener noreferrer">https://github.com/rhasspy/piper/blob/master/VOICES.md</a></li> <li><a href="proxy.php?url=https://rhasspy.github.io/piper-samples/" rel="noopener noreferrer">https://rhasspy.github.io/piper-samples/</a></li> </ul> <p>For this example, let's download the English voice "Amy".<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Download the model file</span> wget <span class="s2">"https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/amy/medium/en_US-amy-medium.onnx?download=true"</span> <span class="se">\</span> <span class="nt">-O</span> en_US-amy-medium.onnx <span class="c"># Download the corresponding config file</span> wget <span class="s2">"https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/amy/medium/en_US-amy-medium.onnx.json?download=true"</span> <span class="se">\</span> <span class="nt">-O</span> en_US-amy-medium.onnx.json </code></pre> </div> <p>Place both files in the same folder. Piper uses the <code>.json</code> file to determine things like speaking speed, noise level, and phoneme mappings.</p> <h2> Try It Out </h2> <p>Time for the magic. Let’s speak some text. You can generate a WAV file or stream it directly to your speakers.</p> <h3> Option 1: Save to file and play </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">echo</span> <span class="s2">"Hello from your machine!"</span> | piper <span class="nt">-m</span> en_US-amy-medium.onnx <span class="nt">--output_file</span> hello.wav aplay hello.wav <span class="c"># or use your system's audio player</span> </code></pre> </div> <h3> Option 2: Stream directly to <code>aplay</code> </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">echo</span> <span class="s2">"This sentence is spoken first. This sentence is synthesized while the first sentence is spoken."</span> <span class="se">\</span> | piper <span class="nt">-m</span> en_US-amy-medium.onnx <span class="nt">--output-raw</span> <span class="se">\</span> | aplay <span class="nt">-r</span> 22050 <span class="nt">-f</span> S16_LE <span class="nt">-t</span> raw - </code></pre> </div> <p>This streams raw audio directly to your speakers in real-time. Make sure the sample rate and format match your voice model.</p> <blockquote> <p>On WSL, use <code>wmplayer hello.wav</code> or <code>powershell -c (New-Object Media.SoundPlayer 'hello.wav').PlaySync()</code>.</p> </blockquote> <h2> Use a GPU (Optional) </h2> <p>If you'd like to use a GPU, install the <code>onnxruntime-gpu</code> package inside your virtual environment. Then run Piper with the <code>--cuda</code> flag.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># installs the onnxruntime-gpu package</span> .venv/bin/pip3 <span class="nb">install </span>onnxruntime-gpu <span class="c"># runs piper using CUDA</span> <span class="nb">echo</span> <span class="s2">"Hello from your machine!"</span> | piper <span class="nt">-m</span> en_US-amy-medium.onnx <span class="nt">--cuda</span> | aplay </code></pre> </div> <blockquote> <p>You’ll need a working CUDA environment, such as what comes with NVIDIA's PyTorch containers.</p> </blockquote> <h2> Fine-Tune the Voice (Optional) </h2> <p>Too fast or too slow? Edit the <code>en_US-amy-low.onnx.json</code> config file and tweak this value:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="nl">"phoneme_duration_scale"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.2</span><span class="w"> </span></code></pre> </div> <p>Or for more fine-tuning:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="nl">"inference"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"length_scale"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.2</span><span class="p">,</span><span class="w"> </span><span class="nl">"noise_scale"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"noise_w"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.6</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <blockquote> <p>Higher <code>length_scale</code> = slower speech. Try <code>1.2</code> to <code>1.5</code>.</p> </blockquote> <h2> Wrap Up </h2> <p>You now have a fast, local, private TTS engine — no internet, no API keys. Just raw, talking hardware.</p> <p>Next steps?</p> <ul> <li>Use it in your scripts or Python apps</li> <li>Hook it into a voice assistant</li> <li>Clone your own voice (stay tuned for Tutorial #2!)</li> </ul> <p>If this worked, give your machine a high-five — or better yet, have it say "Thank you!"</p> ai opensource piper tts Run Docker on WSL2 Without Docker Desktop 🐳 Ricardo Tue, 01 Jul 2025 17:00:24 +0000 https://dev.to/rmaurodev/run-docker-on-wsl2-without-docker-desktop-3jh2 https://dev.to/rmaurodev/run-docker-on-wsl2-without-docker-desktop-3jh2 <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0ok9xjunkukqfowyd8v.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0ok9xjunkukqfowyd8v.png" alt="Run Docker on WSL2 Without Docker Desktop 🐳" width="800" height="800"></a></p> <p>In this tutorial let’s install Docker inside a WSL2 Linux distro, configure it to run without <code>sudo</code>, set up auto-start on launch, and enable log rotation to keep things clean—all without the bloated Docker Desktop.</p> <p>Docker Desktop isn’t just unnecessary—it’s heavy, intrusive, and now comes with licensing restrictions. Running Docker natively inside WSL2 is faster, lighter, and fully under your control. Let’s set it up the right way.</p> <h2> Prerequisites ✅ </h2> <ul> <li>WSL2 already installed</li> <li>Ubuntu (or another Linux distro Debian based for this article) running under WSL2</li> </ul> <h2> Install Docker (Inside WSL2) 🔧 </h2> <p>To install the Docker Community Engine, let's run the following script.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Update your package lists and upgrade existing packages </span> <span class="nv">$ </span><span class="nb">sudo </span>apt update <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt upgrade <span class="nt">-y</span> <span class="c"># Install necessary packages to allow apt to use repositories over HTTPS</span> <span class="nv">$ </span><span class="nb">sudo </span>apt <span class="nb">install </span>apt-transport-https ca-certificates curl software-properties-common <span class="nt">-y</span> <span class="c"># 🐳 Download and save Docker’s official GPG key</span> <span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://download.docker.com/linux/ubuntu/gpg | <span class="se">\</span> <span class="nb">sudo </span>gpg <span class="nt">--dearmor</span> <span class="nt">-o</span> /usr/share/keyrings/docker-archive-keyring.gpg <span class="c"># 🐳 Add Docker’s stable repository to your APT sources list</span> <span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] </span><span class="se">\</span><span class="s2"> https://download.docker.com/linux/ubuntu </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="s2"> stable"</span> | <span class="se">\</span> <span class="nb">sudo tee</span> /etc/apt/sources.list.d/docker.list <span class="o">&gt;</span> /dev/null <span class="c"># Update package index again, now with Docker packages included</span> <span class="nv">$ </span><span class="nb">sudo </span>apt update <span class="c"># 🐳 Finally, install the Docker Engine</span> <span class="nv">$ </span><span class="nb">sudo </span>apt <span class="nb">install </span>docker-ce <span class="nt">-y</span> </code></pre> </div> <h3> 🛠️ Running Docker Without SUDO </h3> <p>Nobody want's to type <code>sudo</code> for every command in our development machine.<br><br> Here is how set up.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Create the 'docker' group (if it doesn't already exist)</span> <span class="c"># This group allows running Docker commands without 'sudo'</span> <span class="nv">$ </span><span class="nb">sudo </span>groupadd docker <span class="c"># Add your current user to the 'docker' group</span> <span class="nv">$ </span><span class="nb">sudo </span>usermod <span class="nt">-aG</span> docker <span class="nv">$USER</span> <span class="c"># Apply the new group membership immediately</span> <span class="c"># This avoids the need to log out and back in</span> <span class="nv">$ </span>newgrp docker </code></pre> </div> <h3> 🚀 Wrapping up </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">sudo </span>service docker start <span class="nv">$ </span>docker run hello-world </code></pre> </div> <p>That is it. It’s done! Congratulations! Amazing! Yeah!</p> <h3> ⚙️ Optional: Auto-Start Docker on WSL Launch </h3> <p>Append this to <code>~/.bashrc</code> or <code>~/.profile</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Check if the current environment is WSL2</span> <span class="k">if </span><span class="nb">grep</span> <span class="nt">-q</span> <span class="s2">"</span><span class="se">\-</span><span class="s2">WSL2"</span> /proc/version <span class="o">&gt;</span> /dev/null 2&gt;&amp;1<span class="p">;</span> <span class="k">then</span> <span class="c"># Check if the Docker service is not running</span> <span class="k">if </span>service docker status 2&gt;&amp;1 | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s2">"is not running"</span><span class="p">;</span> <span class="k">then</span> <span class="c"># Start the Docker service as root using WSL's built-in mechanism</span> <span class="c"># This avoids needing sudo manually each time</span> wsl.exe <span class="nt">--distribution</span> <span class="s2">"</span><span class="k">${</span><span class="nv">WSL_DISTRO_NAME</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--user</span> root <span class="se">\</span> <span class="nt">--exec</span> /usr/sbin/service docker start <span class="o">&gt;</span> /dev/null 2&gt;&amp;1 <span class="k">fi fi</span> </code></pre> </div> <p>Checks if the docker service isn't running and start it.</p> <blockquote> <p>This code runs on the bash startup.</p> </blockquote> <h2> Configuring Docker: Daemon Settings + Log Rotation </h2> <p>To prevent Docker logs from eating up your disk space, configure log rotation:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">sudo </span>nano /etc/docker/daemon.json </code></pre> </div> <p>Paste this:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="nl">"host"</span><span class="p">:</span><span class="w"> </span><span class="s2">"unix:///var/run/docker.sock"</span><span class="p">,</span><span class="w"> </span><span class="nl">"log-driver"</span><span class="p">:</span><span class="w"> </span><span class="s2">"json-file"</span><span class="p">,</span><span class="w"> </span><span class="nl">"log-opts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"max-size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10m"</span><span class="p">,</span><span class="w"> </span><span class="nl">"max-file"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <ul> <li> <code>max-size</code>: rotates when logs hit 10MB</li> <li> <code>max-file</code>: keeps 3 files max, older logs get purged</li> </ul> <p>Then restart Docker:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">sudo </span>service docker restart </code></pre> </div> <blockquote> <p>You can adjust size or count based on your storage needs.</p> </blockquote> <h3> 🛠️ Common Issues </h3> <ul> <li> <strong>Permission denied on <code>docker</code></strong> → You forgot the group step or didn't apply it (<code>newgrp docker</code>)</li> <li> <strong>Daemon not running</strong> → Run <code>sudo service docker start</code> </li> </ul> <p><strong>WSL version issues</strong><br><br> → Check:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>$ wsl --list --verbose </code></pre> </div> <h2> ✅ Summary </h2> <div class="table-wrapper-paragraph"><table> <thead> <tr> <th>Task</th> <th>Command</th> </tr> </thead> <tbody> <tr> <td>Install Docker</td> <td>Add repo &amp; install <code>docker-ce</code> </td> </tr> <tr> <td>Start Docker daemon</td> <td><code>sudo service docker start</code></td> </tr> <tr> <td>Run as non-root</td> <td><code>usermod -aG docker $USER &amp;&amp; newgrp docker</code></td> </tr> <tr> <td>Test Docker</td> <td><code>docker run hello-world</code></td> </tr> <tr> <td>Configure log rotation</td> <td>Edit <code>/etc/docker/daemon.json</code> and restart daemon</td> </tr> <tr> <td>Auto-start Docker on launch</td> <td>Add script to <code>.bashrc</code> or <code>.profile</code> </td> </tr> </tbody> </table></div> <p>No Docker Desktop. No junk. Just containers running clean and fast inside WSL2 — with logs under control.</p> docker wsl2 linux devops Running llama.cpp in Docker on Raspberry Pi Ricardo Sat, 14 Jun 2025 16:47:32 +0000 https://dev.to/rmaurodev/running-llamacpp-in-docker-on-raspberry-pi-4g29 https://dev.to/rmaurodev/running-llamacpp-in-docker-on-raspberry-pi-4g29 <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far4mw84tiadijcuihaum.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far4mw84tiadijcuihaum.png" alt="Running llama.cpp in Docker on Raspberry Pi" width="800" height="533"></a></p> <p>Running large language models on a Raspberry Pi isn’t just possible—it’s fun. Whether you're a hacker exploring local AI, a developer prototyping LLM workflows, or just curious about how far you can push a Pi, this tutorial is for you.</p> <p>We’ll show you how to build and run <code>llama.cpp</code> in Docker on an ARM-based Pi to get a full LLM experience in a tiny, reproducible container. No weird dependencies. No system pollution. Just clean, fast, edge-side inference.</p> <blockquote> <p>If you are looking for a bare metal installation on the Raspberry Pi. Check this <a href="proxy.php?url=https://rmauro.dev/running-llm-llama-cpp-natively-on-raspberry-pi/" rel="noopener noreferrer">https://rmauro.dev/running-llm-llama-cpp-natively-on-raspberry-pi/</a></p> </blockquote> <h2> Dockerfile </h2> <p>The following <em>Dockerfile</em> builds <code>llama.cpp</code> from source within an Ubuntu 22.04 base image. It includes all required dependencies and sets the container <em>entrypoint</em> to the compiled CLI binary.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight docker"><code><span class="k">FROM</span><span class="s"> ubuntu:22.04</span> <span class="k">ENV</span><span class="s"> DEBIAN_FRONTEND=noninteractive</span> <span class="k">RUN </span>apt update <span class="o">&amp;&amp;</span> apt upgrade <span class="nt">-y</span> <span class="o">&amp;&amp;</span> <span class="se">\ </span> apt <span class="nb">install</span> <span class="nt">-y</span> <span class="nt">--no-install-recommends</span> <span class="se">\ </span> ca-certificates git build-essential cmake wget curl <span class="se">\ </span> libcurl4-openssl-dev <span class="o">&amp;&amp;</span> <span class="se">\ </span> apt clean <span class="o">&amp;&amp;</span> <span class="nb">rm</span> <span class="nt">-rf</span> /var/lib/apt/lists/<span class="k">*</span> <span class="k">WORKDIR</span><span class="s"> /opt</span> <span class="k">RUN </span>git clone https://github.com/ggerganov/llama.cpp.git <span class="k">WORKDIR</span><span class="s"> /opt/llama.cpp</span> <span class="k">RUN </span>cmake <span class="nt">-B</span> build <span class="k">RUN </span>cmake <span class="nt">--build</span> build <span class="nt">--config</span> Release <span class="nt">-j</span><span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span> <span class="k">WORKDIR</span><span class="s"> /opt/llama.cpp/build/bin</span> <span class="k">ENTRYPOINT</span><span class="s"> ["./llama-cli"]</span> </code></pre> </div> <h2> Build the Docker Image </h2> <p>Run the following command in the same directory as your Dockerfile to build the image:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>docker build <span class="nt">-t</span> llama-cpp-pi <span class="nb">.</span> </code></pre> </div> <h2> Download a Quantized Model (on Host) </h2> <p>You need a quantized <code>.gguf</code> model to perform inference. Run this command from your host system:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">mkdir</span> <span class="nt">-p</span> models wget <span class="nt">-O</span> models/tinyllama-1.1b-chat-v1.0.Q4_0.gguf <span class="se">\</span> https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q4_0.gguf </code></pre> </div> <p>This creates a <code>models</code> directory and downloads a compact version of TinyLlama suitable for edge devices.</p> <h2> Run Inference from Docker </h2> <p>Mount the <code>models</code> directory and run the container, specifying the model and prompt:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>docker run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="se">\</span> <span class="nt">-v</span> <span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span>/models:/models <span class="se">\</span> llama-cpp-pi <span class="se">\</span> <span class="nt">-m</span> /models/tinyllama-1.1b-chat-v1.0.Q4_0.gguf <span class="nt">-p</span> <span class="s2">"Hello from Docker!"</span> </code></pre> </div> <p>To use a different model:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nv">MODEL</span><span class="o">=</span>your-model-name.gguf docker run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="se">\</span> <span class="nt">-v</span> <span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span>/models:/models <span class="se">\</span> llama-cpp-pi <span class="se">\</span> <span class="nt">-m</span> /models/<span class="nv">$MODEL</span> <span class="nt">-p</span> <span class="s2">"Hello with custom model!"</span> </code></pre> </div> <h2> Conclusion </h2> <p>This Docker-based setup enables efficient deployment of <code>llama.cpp</code> on ARM-based devices like the Raspberry Pi.</p> <p>It abstracts away system-level configuration while preserving the flexibility to swap models, test prompts, or integrate with other AI pipelines.</p> <p>For developers, researchers, and students, this is an ideal workflow to explore the capabilities of local LLM inference.</p> raspberrypi llm docker llamacpp Running LLM llama.cpp Bare Metal on Raspberry Pi Ricardo Sat, 14 Jun 2025 16:33:30 +0000 https://dev.to/rmaurodev/running-llm-llamacpp-bare-metal-on-raspberry-pi-7c4 https://dev.to/rmaurodev/running-llm-llamacpp-bare-metal-on-raspberry-pi-7c4 <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxyq7ievdqvpr0rw1qwp.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxyq7ievdqvpr0rw1qwp.png" alt="Running LLM llama.cpp Natively on Raspberry Pi" width="800" height="533"></a></p> <p>For developers and hackers who enjoy squeezing maximum potential out of compact machines, getting a large language model like <code>llama.cpp</code> running natively on a Raspberry Pi is a rewarding challenge. This guide walks you through compiling llama.cpp from source, downloading a model, and running inference - all on the Pi itself.</p> <h2> Prerequisites </h2> <h3> Hardware </h3> <ul> <li>Raspberry Pi 4, 5, or newer</li> <li>64-bit Raspberry Pi OS</li> <li>4GB RAM minimum (8GB+ recommended)</li> <li>Heatsink or fan recommended for cooling</li> </ul> <h3> Software </h3> <ul> <li>Git</li> <li>CMake (v3.16+)</li> <li>GCC or Clang</li> <li>Python 3 (optional, for Python bindings)</li> </ul> <h2> Step-by-Step Guide </h2> <h3> Install required tools </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">sudo </span>apt update <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt upgrade <span class="nt">-y</span> <span class="c"># 👇 install dependencies and tools to build</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> git build-essential cmake python3-pip libcurl4-openssl-dev </code></pre> </div> <h3> Clone and Build llama.cpp </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>git clone https://github.com/ggerganov/llama.cpp.git <span class="nb">cd </span>llama.cpp cmake <span class="nt">-B</span> build cmake <span class="nt">--build</span> build <span class="nt">--config</span> Release <span class="nt">-j</span><span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span> </code></pre> </div> <p>This step takes sometime. Here we're compiling <code>llama-cpp</code> software.</p> <h3> Download a Quantized Model </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">mkdir</span> <span class="nt">-p</span> models <span class="o">&amp;&amp;</span> <span class="nb">cd </span>models wget https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q4_0.gguf <span class="nb">cd</span> .. </code></pre> </div> <p>Let's use the model <a href="proxy.php?url=https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF" rel="noopener noreferrer">https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF</a> for testing.</p> <h3> 4. Run Inference </h3> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>./build/bin/llama-cli <span class="se">\</span> <span class="nt">-m</span> ./models/tinyllama-1.1b-chat-v1.0.Q4_0.gguf <span class="se">\</span> <span class="nt">-p</span> <span class="s2">"Hello, Raspberry Pi!"</span> </code></pre> </div> <h2> Optional: Python Bindings </h2> <blockquote> <p>Note: The Python bindings have been moved to a separate repository.<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>git clone https://github.com/abetlen/llama-cpp-python.git <span class="nb">cd </span>llama-cpp-python python3 <span class="nt">-m</span> pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt python3 <span class="nt">-m</span> pip <span class="nb">install</span> <span class="nb">.</span> </code></pre> </div> <p>Use in Python:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight python"><code><span class="c1"># Use in Python: </span> <span class="kn">from</span> <span class="n">llama_cpp</span> <span class="kn">import</span> <span class="n">Llama</span> <span class="n">llm</span> <span class="o">=</span> <span class="nc">Llama</span><span class="p">(</span><span class="n">model_path</span><span class="o">=</span><span class="sh">"</span><span class="s">./models/tinyllama-1.1b-chat-v1.0.Q4_0.gguf</span><span class="sh">"</span><span class="p">)</span> <span class="nf">print</span><span class="p">(</span><span class="nf">llm</span><span class="p">(</span><span class="sh">"</span><span class="s">Hello from Python!</span><span class="sh">"</span><span class="p">))</span> </code></pre> </div> <h2> Conclusion </h2> <p>Running llama.cpp natively on a Raspberry Pi is a geeky thrill. It teaches you about compiler optimizations, quantized models, and pushing hardware to the edge—literally. Bonus points if you run it headless over SSH.</p> ai raspberrypi llamacpp llm Building Your Own wc Tool in C# Code Challenge Ricardo Mon, 14 Oct 2024 23:51:21 +0000 https://dev.to/rmaurodev/building-your-own-wc-tool-in-c-code-challenge-1p16 https://dev.to/rmaurodev/building-your-own-wc-tool-in-c-code-challenge-1p16 <p>The Unix <code>wc</code> (word count) tool is a classic command-line utility that counts the number of lines, words, and characters in a text file. In this post, we'll work on a minimalistic version of this tool using C#.</p> <p>For this project let's focus on clarity over optimization, so even beginners can follow along.</p> <h2> Introduction </h2> <p>The <code>wc</code> tool is quite useful for analyzing text files in the terminal. It provides simple statistics:</p> <ul> <li>Number of lines</li> <li>Number of words</li> <li>Number of characters</li> <li>Number of bytes</li> </ul> <p>This blog post is based on a <a href="proxy.php?url=https://codingchallenges.fyi/challenges/challenge-wc/" rel="noopener noreferrer">code challenge</a> where the goal is to recreate the functionality of the <code>wc</code> tool in your language of choice.</p> <p>Below, we’ll build a minimal version of <code>wc</code> using C#.</p> <h2> Step-by-Step Implementation </h2> <p>First, let's break down what we'll need.</p> <ol> <li> <strong>Reading the file</strong> : We’ll use a stream to read the contents of the file.</li> <li> <strong>Counting lines, words, and characters</strong> : We'll write simple methods to count these.</li> <li> <strong>Handling command-line arguments</strong> : We'll add options to count specific things (lines, words, bytes, etc.).</li> </ol> <p>Here’s the code for our minimal <code>wc</code> tool:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System.IO</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span> <span class="k">using</span> <span class="nn">static</span> <span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">Length</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span> <span class="nf">PrintUsage</span><span class="p">();</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="kt">var</span> <span class="n">parsedArgs</span> <span class="p">=</span> <span class="n">ArgumentsParser</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">args</span><span class="p">);</span> <span class="k">try</span> <span class="p">{</span> <span class="k">using</span> <span class="nn">var</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StreamReader</span><span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">FilePath</span><span class="p">);</span> <span class="kt">long</span> <span class="n">lineCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">wordCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">charCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">byteCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="kt">string</span><span class="p">?</span> <span class="n">line</span><span class="p">;</span> <span class="k">while</span> <span class="p">((</span><span class="n">line</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nf">ReadLine</span><span class="p">())</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountLines</span><span class="p">)</span> <span class="n">lineCount</span><span class="p">++;</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountCharacters</span><span class="p">)</span> <span class="n">charCount</span> <span class="p">+=</span> <span class="n">line</span><span class="p">.</span><span class="n">Length</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span> <span class="c1">// +1 for newline character</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountWords</span><span class="p">)</span> <span class="n">wordCount</span> <span class="p">+=</span> <span class="nf">CountWords</span><span class="p">(</span><span class="n">line</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountBytes</span><span class="p">)</span> <span class="n">byteCount</span> <span class="p">+=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountLines</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">lineCount</span><span class="p">}</span><span class="s"> "</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountWords</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">wordCount</span><span class="p">}</span><span class="s"> "</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountBytes</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">byteCount</span><span class="p">}</span><span class="s"> "</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">CountCharacters</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">charCount</span><span class="p">}</span><span class="s"> "</span><span class="p">);</span> <span class="nf">WriteLine</span><span class="p">(</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">FilePath</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">FileNotFoundException</span><span class="p">)</span> <span class="p">{</span> <span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Error: The file '</span><span class="p">{</span><span class="n">parsedArgs</span><span class="p">.</span><span class="n">FilePath</span><span class="p">}</span><span class="s">' does not exist."</span><span class="p">);</span> <span class="p">}</span> <span class="k">static</span> <span class="kt">long</span> <span class="nf">CountWords</span><span class="p">(</span><span class="kt">string</span> <span class="n">line</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Split the line by spaces and count non-empty entries</span> <span class="kt">var</span> <span class="n">words</span> <span class="p">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">' '</span><span class="p">,</span> <span class="n">StringSplitOptions</span><span class="p">.</span><span class="n">RemoveEmptyEntries</span><span class="p">);</span> <span class="k">return</span> <span class="n">words</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">PrintUsage</span><span class="p">()</span> <span class="p">{</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">usage_helper</span> <span class="p">=</span> <span class="s">@"Usage: ccwc [OPTION]... [FILE]... or: ccwc [OPTION]... --files0-from=F Print newline, word, and byte counts for each FILE, and a total line if more than one FILE is specified. A word is a non-zero-length sequence of characters delimited by white space. With no FILE, or when FILE is -, read standard input. The options below may be used to select which counts are printed, always in the following order: newline, word, character, byte, maximum line length. -c, --bytes print the byte counts -m, --chars print the character counts -l, --lines print the newline counts -w, --words print the word counts --help display this help and exit --version output version information and exit !!!DISCLAIMER: This is a clone of famous wc (Word Count) command line. "</span><span class="p">;</span> <span class="nf">WriteLine</span><span class="p">(</span><span class="n">usage_helper</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p>A very simple ArgumentsParser.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">record</span> <span class="nc">class</span> <span class="nf">CWArgument</span><span class="p">(</span> <span class="kt">string</span> <span class="n">FilePath</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">CountBytes</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">CountWords</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">CountLines</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">CountCharacters</span><span class="p">);</span> <span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">ArgumentsParser</span> <span class="p">{</span> <span class="cm">/* * -c : number of bytes * -l : number of lines * -w : number of words * -m : number of characters * Default options - equivalent to -c -l -w */</span> <span class="k">public</span> <span class="k">static</span> <span class="n">CWArgument</span> <span class="nf">Parse</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">arguments</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">countB</span> <span class="p">=</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"-c"</span><span class="p">)</span> <span class="p">||</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"--bytes"</span><span class="p">);</span> <span class="kt">var</span> <span class="n">countW</span> <span class="p">=</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"-w"</span><span class="p">)</span> <span class="p">||</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"--words"</span><span class="p">);</span> <span class="kt">var</span> <span class="n">countL</span> <span class="p">=</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"-l"</span><span class="p">)</span> <span class="p">||</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"--lines"</span><span class="p">);</span> <span class="kt">var</span> <span class="n">countM</span> <span class="p">=</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"-m"</span><span class="p">)</span> <span class="p">||</span> <span class="n">arguments</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"--chars"</span><span class="p">);</span> <span class="kt">var</span> <span class="n">path</span> <span class="p">=</span> <span class="n">arguments</span><span class="p">[^</span><span class="m">1</span><span class="p">];</span> <span class="kt">var</span> <span class="n">defaults</span> <span class="p">=</span> <span class="p">!</span><span class="n">countB</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">countW</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">countL</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">countM</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">defaults</span><span class="p">)</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">CWArgument</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="k">true</span><span class="p">,</span> <span class="k">true</span><span class="p">,</span> <span class="k">true</span><span class="p">,</span> <span class="k">false</span><span class="p">);</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">CWArgument</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">countB</span><span class="p">,</span> <span class="n">countW</span><span class="p">,</span> <span class="n">countL</span><span class="p">,</span> <span class="n">countM</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2> How It Works </h2> <ol> <li> <strong>File Handling</strong> : We use a <code>StreamReader</code> to read the file line by line. This is the simplest way to handle file reading in C#.</li> <li> <strong>Counting Options</strong> : We handle the different counting options using the <code>CWArgument</code> record and the <code>ArgumentsParser</code> class. The parser processes the command-line arguments to determine what should be counted (bytes, words, lines, or characters). By default, it counts bytes, words, and lines, unless specified otherwise.</li> <li> <strong>Counting Lines, Words, and Characters</strong> : Depending on the options provided, we count the number of lines, words, characters, and bytes.</li> <li> <strong>Command-Line Arguments</strong> : The file path and options (e.g., <code>-c</code> for bytes) are passed as command-line arguments. The program parses them to determine which statistics to display.</li> </ol> <h2> Usage </h2> <p>To run the program, compile it and run it from the command line:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>dotnet run <span class="nt">--</span> <span class="nt">-l</span> <span class="nt">-w</span> <span class="nt">-c</span> &lt;path_to_your_file&gt; </code></pre> </div> <p>For example:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>dotnet run <span class="nt">--</span> <span class="nt">-l</span> <span class="nt">-w</span> example.txt </code></pre> </div> <p>The output will show the selected statistics based on the arguments passed. For example:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>5 15 78 example.txt </code></pre> </div> <p>This means the file <code>example.txt</code> has 5 lines, 15 words, and 78 characters.</p> <h2> Create an runnable executable </h2> <p>To do so all you need is.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>dotnet publish <span class="nt">-c</span> Release <span class="nt">-o</span> ../output </code></pre> </div> <p>Check the output folder recently created.</p> <h2> Special Mention </h2> <p>This implementation is a minimalist solution to the <a href="proxy.php?url=https://codingchallenges.fyi/challenges/challenge-wc/" rel="noopener noreferrer">Build Your Own wc Tool challenge</a>.</p> <p>If you'd like to see a more refined version, including optimizations and additional features, check out my GitHub repository: <a href="proxy.php?url=https://github.com/ricardodemauro/Rmauro.CommandLines.WordCount" rel="noopener noreferrer">Rmauro.CommandLines.WordCount</a>.</p> <h2> Conclusion </h2> <p>We’ve built a minimalist version of the Unix <code>wc</code> tool in C# using just a few lines of code.</p> <p>By adding argument parsing, we made the tool customizable based on the user’s needs.</p> <p>This implementation is a great starting point for building command-line utilities and getting comfortable with file handling and argument parsing in C#.</p> <p>You can experiment further by adding features or improving performance, but this version should give you a solid foundation.</p> <p>Happy coding! 😎 </p> codechallenge csharp programming learning C# Enum Bitwise Operations Ricardo Tue, 27 Aug 2024 15:43:46 +0000 https://dev.to/rmaurodev/c-enum-bitwise-operations-339g https://dev.to/rmaurodev/c-enum-bitwise-operations-339g <p>Enums in C# offer more than just a simple way to define named constants—they can be a powerful tool for handling complex scenarios when combined with bitwise operations.</p> <p>In this post, we will dive into advanced C# enum usage, focusing on bitwise operations to create powerful and flexible code. By understanding how to effectively use C# enum flags with bitwise AND/OR operations, you can optimize your applications and write more efficient C# code.</p> <p>Using C# Enums, bitwise operations and C# flags you’ll enhance your ability to write more flexible, maintainable, and scalable code.</p> <h2> 🧾 Introduction to Enums </h2> <p>An enum is a special data type that enables a variable to be a set of predefined constants. In practice, enums help create a meaningful representation of data, making it easier to work with instead of using integer literals scattered throughout your code.</p> <p><strong>Example: Enum to represent different types of animals.</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">enum</span> <span class="n">Animal</span> <span class="p">{</span> <span class="n">Dog</span><span class="p">,</span> <span class="n">Cat</span><span class="p">,</span> <span class="n">Fish</span> <span class="p">}</span> </code></pre> </div> <h3> Why Use Enums? </h3> <ul> <li> <strong>Readability:</strong> Using named constants instead of numeric values makes your code easier to read.</li> <li> <strong>Maintainability:</strong> If the underlying value changes, you only need to update it in one place.</li> <li> <strong>Type Safety:</strong> Enums prevent invalid values from being assigned to a variable.</li> </ul> <h2> 💪 Advanced Enum Usage: Bitwise Operations </h2> <p>Enums can represent bit fields by combining values using bitwise operations, which is particularly useful for flags.</p> <h3> Defining Flags Enum </h3> <p>To do this, you should assign powers of two to each member of the enum.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="p">[</span><span class="n">Flags</span><span class="p">]</span> <span class="k">public</span> <span class="k">enum</span> <span class="n">DataVidsFlags</span> <span class="p">{</span> <span class="n">None</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span> <span class="n">FlagA</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span> <span class="c1">// 0001</span> <span class="n">FlagB</span> <span class="p">=</span> <span class="m">2</span><span class="p">,</span> <span class="c1">// 0010</span> <span class="n">FlagC</span> <span class="p">=</span> <span class="m">4</span><span class="p">,</span> <span class="c1">// 0100</span> <span class="n">FlagD</span> <span class="p">=</span> <span class="m">8</span> <span class="c1">// 1000</span> <span class="p">}</span> </code></pre> </div> <h3> Using Bitwise Operations in C# Enums </h3> <p>You can leverage bitwise operators to combine and check flags.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="c1">// Using combine flags</span> <span class="n">DataVidsFlags</span> <span class="n">myFlags</span> <span class="p">=</span> <span class="n">DataVidsFlags</span><span class="p">.</span><span class="n">FlagA</span> <span class="p">|</span> <span class="n">DataVidsFlags</span><span class="p">.</span><span class="n">FlagC</span><span class="p">;</span> <span class="c1">// Output: FlagA, FlagC</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">myFlags</span><span class="p">);</span> <span class="c1">// Checking if a specific flag is set</span> <span class="kt">bool</span> <span class="n">hasFlagB</span> <span class="p">=</span> <span class="p">(</span><span class="n">myFlags</span> <span class="p">&amp;</span> <span class="n">DataVidsFlags</span><span class="p">.</span><span class="n">FlagB</span><span class="p">)</span> <span class="p">==</span> <span class="n">DataVidsFlags</span><span class="p">.</span><span class="n">FlagB</span><span class="p">;</span> <span class="c1">// Output: Has Flag B: False</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Has Flag B: "</span> <span class="p">+</span> <span class="n">hasFlagB</span><span class="p">);</span> </code></pre> </div> <h3> Using HasFlag </h3> <p>The advantage of using the <code>HasFlag</code> method simplifies your code.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">if</span> <span class="p">(</span><span class="n">myFlags</span><span class="p">.</span><span class="nf">HasFlag</span><span class="p">(</span><span class="n">DataVidsFlags</span><span class="p">.</span><span class="n">FlagC</span><span class="p">))</span> <span class="p">{</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Flag C is set."</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h2> Conclusion </h2> <p>Enums in C# serve as a powerful tool for creating more readable, maintainable, and type-safe code. Whether you are using simple enums for clarity or leveraging bitwise operations for complex flag representations, how you implement them can greatly improve your application's code quality.</p> <h2> Tips for Using Enums </h2> <ol> <li> <strong>Use Descriptive Names:</strong> Choose names that accurately describe the values.</li> <li> <strong>Keep Them Organized:</strong> Consider placing enums in their own files for better organization.</li> <li> <strong>Avoid Value Confusion:</strong> Assign starting points when using bitwise operations for clarity on the intended manipulation.</li> <li> <strong>Utilize Flags:</strong> When multiple states can exist, use the <code>[Flags]</code> attribute to enable combined flags.</li> </ol> <h2> References </h2> <p><a href="proxy.php?url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators" rel="noopener noreferrer">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators</a></p> <p>Feel free to leave your thoughts, comments, or examples of how you’re using enums in your own projects! Happy coding! 😎</p> csharp dotnet beginners architecture Publish C# Project to Nuget.org Ricardo Mon, 26 Aug 2024 14:39:31 +0000 https://dev.to/rmaurodev/publish-c-project-to-nugetorg-2leb https://dev.to/rmaurodev/publish-c-project-to-nugetorg-2leb <p>Using NuGet packages and nuget.org are a great way to share our .NET projects with other developers and also distribute your code across multiple projects.</p> <p>In this guide, we'll walk through the process of publishing an existing .NET project as a NuGet package on NuGet.org. Let's use the <code>Rmauro.Extensions.Configuration.EnvFiles</code> project as an example, which is hosted on GitHub.</p> <h2> Prepare the Project for Packaging </h2> <p>Let's start by cloning the project or using DevContainers to edit in Github Cloud (as demonstrated here <a href="proxy.php?url=https://dev.to/rmaurodev/create-and-configure-a-github-codespace-4nap">Create and Configure a GitHub Codespace</a><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># clones the project </span> <span class="o">&gt;</span> git clone https://github.com/ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles.git <span class="o">&gt;</span> <span class="nb">cd </span>Rmauro.Extensions.Configuration.EnvFiles </code></pre> </div> <p>Open the <code>Rmauro.Extensions.Configuration.Env.csproj</code> file and include the necessary NuGet package metadata.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight xml"><code><span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk"</span><span class="nt">&gt;</span> <span class="nt">&lt;PropertyGroup&gt;</span> <span class="nt">&lt;TargetFramework&gt;</span>netstandard2.0<span class="nt">&lt;/TargetFramework&gt;</span> <span class="c">&lt;!--Starts here--&gt;</span> <span class="nt">&lt;PackageId&gt;</span>Rmauro.Extensions.Configuration.EnvFiles<span class="nt">&lt;/PackageId&gt;</span> <span class="nt">&lt;PackageVersion&gt;</span>1.0.0<span class="nt">&lt;/Version&gt;</span> <span class="nt">&lt;Authors&gt;</span>Ricardo Mauro<span class="nt">&lt;/Authors&gt;</span> <span class="nt">&lt;Description&gt;</span>Extension to read .env files in dotnet core projects<span class="nt">&lt;/Description&gt;</span> <span class="nt">&lt;RepositoryUrl&gt;</span>https://github.com/ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles.git<span class="nt">&lt;/RepositoryUrl&gt;</span> <span class="nt">&lt;PackageIconUrl&gt;</span>rmauro-favico-32.png<span class="nt">&lt;/PackageIconUrl&gt;</span> <span class="nt">&lt;PackageIcon&gt;</span>rmauro-favico-32.png<span class="nt">&lt;/PackageIcon&gt;</span> <span class="nt">&lt;PackageLicenseExpression&gt;</span>MIT<span class="nt">&lt;/PackageLicenseExpression&gt;</span> <span class="nt">&lt;RequireLicenseAcceptance&gt;</span>false<span class="nt">&lt;/RequireLicenseAcceptance&gt;</span> <span class="c">&lt;!--Ends here--&gt;</span> <span class="nt">&lt;/PropertyGroup&gt;</span> <span class="c">&lt;!--https://stackoverflow.com/a/76122058/1652594--&gt;</span> <span class="nt">&lt;ItemGroup&gt;</span> <span class="nt">&lt;None</span> <span class="na">Include=</span><span class="s">"../../assets/rmauro-favico-32.png"</span> <span class="na">Pack=</span><span class="s">"true"</span> <span class="na">PackagePath=</span><span class="s">""</span><span class="nt">/&gt;</span> <span class="nt">&lt;/ItemGroup&gt;</span> <span class="nt">&lt;/Project&gt;</span> </code></pre> </div> <p><strong>Properties Definition</strong></p> <ul> <li> <code>PackageId</code> Specifies the name for the resulting package</li> <li> <code>PackageVersion</code> This is semver compatible, for example 1.0.0, 1.0.0-beta, or 1.0.0-beta-00345. Defaults to Version if not set</li> <li> <code>Authors</code> A semicolon-separated list of packages authors, matching the profile names on nuget.org. These are displayed in the NuGet Gallery on nuget.org and are used to cross-reference packages by the same authors</li> <li> <code>Description</code> A long description for the assembly. If PackageDescription is not specified, then this property is also used as the description of the package</li> <li> <code>RepositoryUrl</code> Repository URL used to clone or retrieve source code.</li> <li> <code>PackageIconUrl</code> PackageIconUrl is deprecated in favor of PackageIcon. However, for the best downlevel experience, you should specify PackageIconUrl in addition to PackageIcon</li> <li> <code>PackageIcon</code> Specifies the package icon path, relative to the root of the package</li> <li> <code>PackageLicenseExpression</code> Corresponds to license expression. <a href="proxy.php?url=https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#packing-a-license-expression-or-a-license-file" rel="noopener noreferrer">Read more</a> </li> <li> <code>RequireLicenseAcceptance</code> A Boolean value that specifies whether the client must prompt the consumer to accept the package license before installing the package.</li> </ul> <p>For more properties read the Microsoft's Official Docs: <a href="proxy.php?url=https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target" rel="noopener noreferrer">https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target</a></p> <blockquote> <p>Tip: If you need to include Icon, you need to first import into the project by using <code>&lt;ItemGroup&gt;</code> and <code>&lt;None&gt;</code> tags and then use it.</p> </blockquote> <h2> Pack the NuGet Package </h2> <p>All set with our project, let's create the NuGet package.</p> <p>Navigate to the project directory containing the <code>.csproj</code> file and run the following command.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># build the package and sets the version to 0.0.1</span> <span class="o">&gt;</span> dotnet build <span class="nt">-c</span> Release /p:Version<span class="o">=</span>0.0.1 <span class="c"># packs the package</span> <span class="o">&gt;</span> dotnet pack <span class="nt">--configuration</span> Release </code></pre> </div> <p>This command will generate a <code>.nupkg</code> file in the <code>bin/Release</code> directory.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-4.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-19-54-26.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-4.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-19-54-26.png" alt="Publish C# Project to Nuget.org"></a></p> <h3> Auto Generate the Package on Build </h3> <p>Include the following property to auto generate the package.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight xml"><code><span class="nt">&lt;GeneratePackageOnBuild&gt;</span>true<span class="nt">&lt;/GeneratePackageOnBuild&gt;</span> </code></pre> </div> <h2> Create a NuGet.org Account and Get an API Key </h2> <p>Before you can publish your package, we need a NuGet.org account and an API key.</p> <p>Go to <a href="proxy.php?url=https://www.nuget.org/" rel="noopener noreferrer">NuGet.org</a> and click "Sign in" in the top right corner.</p> <ul> <li>Click "Create a new account" if you don't have one. You can sign up using a Microsoft account or create a new account with your email.</li> <li>Follow the prompts to complete your registration, including email verification.</li> </ul> <h3> Generate an API Key </h3> <p>Once logged in, click on your username in the top right corner and select "API Keys".</p> <p>Click "Create" to generate a new API key.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-19-58-26.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-19-58-26.png" alt="Publish C# Project to Nuget.org"></a></p> <p>Provide a name for your key (e.g., "MyAwesomeLibrary Publishing Key").</p> <p>Choose the scope for the key:</p> <ul> <li>"Push new packages and package versions" if you're only publishing.</li> <li>"Push and unlist packages" if you also want the ability to unlist packages.</li> </ul> <p>Select the glob pattern for the packages this key can push. Use <code>*</code> for all packages or specify package names. Then click "Create".</p> <p>Copy the generated API Key.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-20-14-27.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FScreenshot-from-2024-08-24-20-14-27.png" alt="Publish C# Project to Nuget.org"></a></p> <p><strong>Remember to keep your API key confidential. Never share it or commit it to source control.</strong></p> <h2> Publish to NuGet.org </h2> <p>Now that you have your API key, you can publish your package:</p> <ol> <li>Use the following command to publish your package, replacing <code>your_api_key</code> with your actual API key: </li> </ol> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="o">&gt;</span> dotnet nuget push <span class="sb">`</span> bin/Release/Rmauro.Extensions.Configuration.EnvFiles.1.0.0.nupkg <span class="sb">`</span> <span class="nt">--api-key</span> your_api_key <span class="sb">`</span> <span class="nt">--source</span> https://api.nuget.org/v3/index.json </code></pre> </div> <p><strong>NuGet.org</strong> will process our package. It usually takes a few minutes to be available.</p> <p>Here is our package: <a href="proxy.php?url=https://www.nuget.org/packages/Rmauro.Extensions.Configuration.Env/" rel="noopener noreferrer">https://www.nuget.org/packages/Rmauro.Extensions.Configuration.Env/</a></p> <h2> Updating Your Package </h2> <p>When you make changes to your project and want increment the published version.</p> <ol> <li>Build using the <code>/p:Version=0.0.0.2</code> parameter or simply increment the <code>&lt;Version&gt;</code> number in your <code>.csproj</code> file</li> <li>Repackage and publish again </li> </ol> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="o">&gt;</span> dotnet build <span class="nt">-c</span> Release /p:Version<span class="o">=</span>0.0.0.2 <span class="o">&gt;</span> dotnet pack <span class="nt">--configuration</span> Release <span class="o">&gt;</span> dotnet nuget push <span class="sb">`</span> bin/Release/Rmauro.Extensions.Configuration.EnvFiles.0.0.2.nupkg <span class="sb">`</span> <span class="nt">--api-key</span> <span class="o">{</span>NUGET_API_KEY<span class="o">}</span> <span class="sb">`</span> <span class="nt">--source</span> https://api.nuget.org/v3/index.json </code></pre> </div> <h2> Best Practices for Maintaining Your NuGet Package </h2> <ul> <li>Create/Update the README.md file in the nuget.org website</li> <li>Use semantic versioning (MAJOR.MINOR.PATCH) to communicate the nature of updates</li> <li>Regularly check for and update any dependencies in your project</li> <li>Keep your code updated and secure against know treats</li> <li>Rotate your API keys periodically for security</li> </ul> <h2> Conclusion </h2> <p>Publishing your existing .NET project as a NuGet package is a great way to share your work with the broader .NET community.</p> <p>By following these steps, you've made your <code>Rmauro.Extensions.Configuration.EnvFiles</code> library easily accessible to other developers.</p> <p>Happy packaging!!</p> nuget dotnet beginners csharp Create and Configure a GitHub Codespace Ricardo Tue, 20 Aug 2024 14:26:20 +0000 https://dev.to/rmaurodev/create-and-configure-a-github-codespace-4nap https://dev.to/rmaurodev/create-and-configure-a-github-codespace-4nap <p>GitHub Codespaces provides a powerful, cloud-based development environment that we can start and stop in seconds, tailored specifically to our project’s needs.</p> <p>In this tutorial, let's go through the process of set up and configure a <strong>GitHub Codespace</strong> using an existing repository.</p> <p>Let's dive in and see how you can get started with GitHub Codespaces today!</p> <h2> 🚀 Table of Contents </h2> <ul> <li>Before You Start - Mind the Costs</li> <li>Create a GitHub Codespace</li> <li>Select a Pre-Build Configuration</li> <li>Save and Push Changes</li> <li>Stop Current Codespace</li> <li>The Codespace Dashboard</li> </ul> <h2> ⌚ Before You Start - Mind the Costs </h2> <p>GitHub Codespaces offers a powerful cloud-based development environment with a <strong>free usage cota</strong> but it's <strong>NOT free.</strong> Understanding the pricing structure can help you manage costs effectively.</p> <blockquote> <p>GitHub Codespaces is paid for either by an organization, an enterprise, or a personal account. The Free and Pro plans for <strong>personal accounts include free use of GitHub Codespaces up to a fixed amount of usage every month.</strong></p> </blockquote> <p>Read more: <a href="proxy.php?url=https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces" rel="noopener noreferrer">https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces</a></p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-6.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-6.png" alt="Create and Configure a GitHub Codespace"></a><br> <em>Free of charge today - 08/17/2024</em></p> <h2> 🔨 Step by Step Guide </h2> <p>Let's dive into the step by step guide on Code Spaces.</p> <h3> Step 1: Create a GitHub Codespace </h3> <p>To create the <strong>Codespace</strong> click on the <strong>Code</strong> button, then <strong>Codespaces,</strong> and then select <code>Create codespace on main</code>.<br><br> If you have multiple branches, you can choose a specific branch for the Codespace.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-4.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-1.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-4.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-1.png" alt="Create and Configure a GitHub Codespace"></a></p> <blockquote> <p>A dialog may appear allowing you to choose the machine type (e.g., 2-core, 4-core). Select the appropriate machine based on your development needs.</p> </blockquote> <p>Once initialized, you’ll see a Visual Studio Code interface within your browser.</p> <h3> Step 2: Select a Pre-Build Configuration </h3> <p>On the <strong>Visual Code</strong> search bar start typing <code>&gt; Codespaces</code> to see all the available options.</p> <p>Select "<strong>Codespaces: Add Dev Container Configuration File...</strong>" to start from scratch. Next select <strong>"Create a new configuration"</strong> to start from scratch <strong>.</strong></p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-2.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-2.png" alt="Create and Configure a GitHub Codespace"></a></p> <p>Select "<strong>C# (.NET) devcontainers</strong>" or the best framework for your project.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-1.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-3.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-1.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-3.png" alt="Create and Configure a GitHub Codespace"></a></p> <p>Moving forward we can include additional features to install, such as ".NET Aspire".</p> <p>Once completed all the selections click on <strong>Rebuild Now</strong> to start building the Codespace.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-4.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-4.png" alt="Create and Configure a GitHub Codespace"></a></p> <p>The <strong>devcontainer.json</strong> file should be created. It contains all configuration for the recently created Codespace.</p> <blockquote> <p>You can commit and push the devcontainer.json file to the repository for later use.</p> </blockquote> <p>In the devcontainer.json file you can add more features, change ports, remote user, among other features. <strong>D</strong> on't forget to "Rebuild" if changes are made.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="err">//</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">format</span><span class="w"> </span><span class="err">details,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="err">https://aka.ms/devcontainer.json.</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">config</span><span class="w"> </span><span class="err">options,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">README</span><span class="w"> </span><span class="err">at:</span><span class="w"> </span><span class="err">https://github.com/devcontainers/templates/tree/main/src/dotnet</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C# (.NET)"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Or</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">a</span><span class="w"> </span><span class="err">Dockerfile</span><span class="w"> </span><span class="err">or</span><span class="w"> </span><span class="err">Docker</span><span class="w"> </span><span class="err">Compose</span><span class="w"> </span><span class="err">file.</span><span class="w"> </span><span class="err">More</span><span class="w"> </span><span class="err">info:</span><span class="w"> </span><span class="err">https://containers.dev/guide/dockerfile</span><span class="w"> </span><span class="nl">"image"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm"</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Features</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">add</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">dev</span><span class="w"> </span><span class="err">container.</span><span class="w"> </span><span class="err">More</span><span class="w"> </span><span class="err">info:</span><span class="w"> </span><span class="err">https://containers.dev/features.</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Use</span><span class="w"> </span><span class="err">'forwardPorts'</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">make</span><span class="w"> </span><span class="err">a</span><span class="w"> </span><span class="err">list</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">ports</span><span class="w"> </span><span class="err">inside</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">container</span><span class="w"> </span><span class="err">available</span><span class="w"> </span><span class="err">locally.</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"forwardPorts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">5000</span><span class="p">,</span><span class="w"> </span><span class="mi">5001</span><span class="p">],</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"portsAttributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"5001"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"protocol"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https"</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Use</span><span class="w"> </span><span class="err">'postCreateCommand'</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">run</span><span class="w"> </span><span class="err">commands</span><span class="w"> </span><span class="err">after</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">container</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">created.</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"postCreateCommand"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dotnet restore"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Configure</span><span class="w"> </span><span class="err">tool-specific</span><span class="w"> </span><span class="err">properties.</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"customizations"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Uncomment</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">connect</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">root</span><span class="w"> </span><span class="err">instead.</span><span class="w"> </span><span class="err">More</span><span class="w"> </span><span class="err">info:</span><span class="w"> </span><span class="err">https://aka.ms/dev-containers-non-root.</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="nl">"remoteUser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <h3> Step 3: Save and Push Changes </h3> <p>As we make changes in our source code, they will be contained only within the Codespace. Use the Git panel in Visual Studio Code to commit your changes.</p> <h3> Step 4: Stop Current Codespace </h3> <p>When finished let's <strong>STOP</strong> the Codespace to avoid incurring unnecessary charges.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-5.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-5.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-5.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcode-space-5.png" alt="Create and Configure a GitHub Codespace"></a></p> <p>When no longer need, we can delete it to free up resources.</p> <p>Simply navigate to the GitHub Codespaces dashboard, find your Codespace, and select <code>Delete</code>.</p> <h2> The Codespace Dashboard </h2> <p>Remember to check the <a href="proxy.php?url=https://github.com/codespaces" rel="noopener noreferrer">Codespace Dashboard</a> to make sure nothing is running without your knowledge.</p> <h2> Conclusion </h2> <p>The <strong>GitHub Codespaces</strong> can greatly enhance our development workflow, offering a consistent and accessible environment wherever we work.</p> <p>Happy coding! 😎</p> github beginners programming tutorial Load .Env file into IConfiguration Provider Ricardo Wed, 14 Aug 2024 18:25:27 +0000 https://dev.to/rmaurodev/load-env-file-into-iconfiguration-provider-4fk0 https://dev.to/rmaurodev/load-env-file-into-iconfiguration-provider-4fk0 <p>Managing environment variables efficiently is crucial for modern .NET applications, especially when configuring environments like development, staging, and production.</p> <p>.NET Core's <code>IConfiguration</code> interface is a powerful tool for managing application settings, and it can be extended to include environment variables stored in a <code>.env</code> file using a custom configuration provider.</p> <h2> In This Series </h2> <ul> <li><a href="proxy.php?url=https://dev.to/rmaurodev/reading-env-files-in-c-124g">Reading .env Files in C#</a></li> <li>Loading DotEnv file into IConfiguration (You're here)</li> <li>Creating NuGet Package for EnvReader (coming soon)</li> </ul> <p>In this post, we’ll demonstrate how to integrate the <code>EnvReader</code> class into <code>IConfiguration</code>, enabling seamless configuration management for your C# applications with support for DotEnv files.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcover-iconfiguration-source-envfile.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Fcover-iconfiguration-source-envfile.png" alt="Load .Env file into IConfiguration Provider"></a></p> <blockquote> <p>This article is a follow-up to our previous post, <a href="proxy.php?url=https://rmauro.dev/read-env-file-in-csharp/" rel="noopener noreferrer">Reading a .env File in C#</a>, where we introduced the <code>EnvReader</code> class to load environment variables from a <code>.env</code> file.</p> </blockquote> <h2> <strong>Setting Up the Project</strong> </h2> <p>Create a new .NET Standard Class Library and add the dependency packages.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># creates class library</span> dotnet new classlib <span class="nt">-o</span> Extensions.Configuration.EnvFile <span class="c"># add to solution</span> dotnet sln add Extensions.Configuration.EnvFile/Extensions.Configuration.EnvFile.csproj <span class="c">#add dependency packages</span> dotnet add package Microsoft.Extensions.Configuration.Abstractions dotnet add package Microsoft.Extensions.Configuration.FileExtensions </code></pre> </div> <p>The <code>.csproj</code> file should look like this.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight xml"><code><span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk"</span><span class="nt">&gt;</span> <span class="nt">&lt;PropertyGroup&gt;</span> <span class="nt">&lt;TargetFramework&gt;</span>netstandard2.1<span class="nt">&lt;/TargetFramework&gt;</span> <span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span> <span class="nt">&lt;/PropertyGroup&gt;</span> <span class="nt">&lt;ItemGroup&gt;</span> <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.Extensions.Configuration.Abstractions"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.Extensions.Configuration.FileExtensions"</span> <span class="na">Version=</span><span class="s">"8.0.1"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/ItemGroup&gt;</span> <span class="nt">&lt;/Project&gt;</span> </code></pre> </div> <h2> <strong>Implementing the Configuration reader</strong> </h2> <p>To have a fully functional <code>EnvReader</code> integrated with <code>Microsoft.Extensions.Configuration.IConfiguration</code> interface lets us set up 4 different classes.</p> <ul> <li>EnvConfigurationExtensions - Here we will have the <strong>AddEnvFile</strong> method and overloads</li> <li>EnvConfigurationProvider - Provider to read the .env file</li> <li>EnvConfigurationSource - Source of Configuration that integrates with the Provider</li> <li>EnvReader - The .env file reader/parser</li> </ul> <h3> Step 1: Set Up the <code>EnvReader</code> Class </h3> <p>For this version of <strong>EnvReader</strong> let's use Stream instead of file path.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System.IO</span><span class="p">;</span> <span class="k">internal</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">EnvReader</span> <span class="p">{</span> <span class="k">public</span> <span class="k">static</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">Load</span><span class="p">(</span><span class="n">Stream</span> <span class="n">stream</span><span class="p">)</span> <span class="p">{</span> <span class="n">StreamReader</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StreamReader</span><span class="p">(</span><span class="n">stream</span><span class="p">);</span> <span class="k">while</span> <span class="p">(</span><span class="n">reader</span><span class="p">.</span><span class="nf">Peek</span><span class="p">()</span> <span class="p">&gt;</span> <span class="p">-</span><span class="m">1</span><span class="p">)</span> <span class="p">{</span> <span class="kt">string</span> <span class="n">line</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nf">ReadLine</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="p">||</span> <span class="n">line</span><span class="p">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s">"#"</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span> <span class="c1">// Skip empty lines and comments</span> <span class="kt">var</span> <span class="n">parts</span> <span class="p">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">'='</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parts</span><span class="p">.</span><span class="n">Length</span> <span class="p">!=</span> <span class="m">2</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> <span class="c1">// Skip lines that are not key-value pairs</span> <span class="kt">var</span> <span class="n">key</span> <span class="p">=</span> <span class="n">parts</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="nf">Trim</span><span class="p">();</span> <span class="kt">var</span> <span class="k">value</span> <span class="p">=</span> <span class="n">parts</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="nf">Trim</span><span class="p">();</span> <span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;(</span><span class="n">key</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> Step 2: Create a Custom Configuration Provider </h3> <p>To integrate <code>EnvReader</code> into <code>IConfiguration</code>, let's create a custom <em>configuration provider</em>.</p> <p>This provider will load environment variables from a <code>.env</code> file and make them available through <code>IConfiguration</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System.IO</span><span class="p">;</span> <span class="k">internal</span> <span class="k">class</span> <span class="nc">EnvConfigurationProvider</span> <span class="p">:</span> <span class="n">FileConfigurationProvider</span> <span class="p">{</span> <span class="k">public</span> <span class="nf">EnvConfigurationProvider</span><span class="p">(</span><span class="n">FileConfigurationSource</span> <span class="n">source</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">source</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span> <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Load</span><span class="p">(</span><span class="n">Stream</span> <span class="n">stream</span><span class="p">)</span> <span class="p">{</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">EnvReader</span><span class="p">.</span><span class="nf">Load</span><span class="p">(</span><span class="n">stream</span><span class="p">))</span> <span class="p">{</span> <span class="n">Data</span><span class="p">[</span><span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">]</span> <span class="p">=</span> <span class="n">item</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> Step 3: Create a Custom Configuration Source </h3> <p>Next, let's create a custom configuration source that will be used to add our <code>EnvConfigurationProvider</code> to the <code>IConfigurationBuilder</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">EnvConfigurationSource</span> <span class="p">:</span> <span class="n">FileConfigurationSource</span> <span class="p">{</span> <span class="k">public</span> <span class="k">override</span> <span class="n">IConfigurationProvider</span> <span class="nf">Build</span><span class="p">(</span><span class="n">IConfigurationBuilder</span> <span class="n">builder</span><span class="p">)</span> <span class="p">{</span> <span class="nf">EnsureDefaults</span><span class="p">(</span><span class="n">builder</span><span class="p">);</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">EnvConfigurationProvider</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> Step 4: Add Extension Method to <code>IConfigurationBuilder</code> </h3> <p>To make it easy to use our custom configuration provider, create a set of extension methods to register the EnvReader Provider.</p> <blockquote> <p>To make registering easier, create extension methods in the namespace Microsoft.Extensions.Configuration<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">Extensions.Configuration.EnvFile</span><span class="p">;</span> <span class="k">using</span> <span class="nn">Microsoft.Extensions.FileProviders</span><span class="p">;</span> <span class="k">using</span> <span class="nn">Microsoft.Extensions.FileProviders.Physical</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System</span><span class="p">;</span> <span class="k">namespace</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span> <span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">EnvConfigurationExtensions</span> <span class="p">{</span> <span class="k">public</span> <span class="k">static</span> <span class="n">IConfigurationBuilder</span> <span class="nf">AddEnvFile</span><span class="p">(</span> <span class="k">this</span> <span class="n">IConfigurationBuilder</span> <span class="n">builder</span><span class="p">,</span> <span class="kt">string</span> <span class="n">path</span> <span class="p">=</span> <span class="s">".env"</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">optional</span> <span class="p">=</span> <span class="k">false</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">reloadOnChange</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">fileProvider</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PhysicalFileProvider</span><span class="p">(</span><span class="n">AppContext</span><span class="p">.</span><span class="n">BaseDirectory</span><span class="p">,</span> <span class="n">ExclusionFilters</span><span class="p">.</span><span class="n">Hidden</span> <span class="p">|</span> <span class="n">ExclusionFilters</span><span class="p">.</span><span class="n">System</span><span class="p">);</span> <span class="k">return</span> <span class="nf">AddEnvFile</span><span class="p">(</span><span class="n">builder</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="n">path</span><span class="p">,</span> <span class="n">optional</span><span class="p">:</span> <span class="n">optional</span><span class="p">,</span> <span class="n">reloadOnChange</span><span class="p">:</span> <span class="n">reloadOnChange</span><span class="p">,</span> <span class="n">provider</span><span class="p">:</span> <span class="n">fileProvider</span><span class="p">);</span> <span class="p">}</span> <span class="k">public</span> <span class="k">static</span> <span class="n">IConfigurationBuilder</span> <span class="nf">AddEnvFile</span><span class="p">(</span> <span class="k">this</span> <span class="n">IConfigurationBuilder</span> <span class="n">builder</span><span class="p">,</span> <span class="n">IFileProvider</span> <span class="n">provider</span><span class="p">,</span> <span class="kt">string</span> <span class="n">path</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">optional</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">reloadOnChange</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">builder</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentNullException</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">builder</span><span class="p">));</span> <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">path</span><span class="p">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"invalid path"</span><span class="p">,</span> <span class="k">nameof</span><span class="p">(</span><span class="n">path</span><span class="p">));</span> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddEnvFile</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">s</span><span class="p">.</span><span class="n">FileProvider</span> <span class="p">=</span> <span class="n">provider</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">Path</span> <span class="p">=</span> <span class="n">path</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">Optional</span> <span class="p">=</span> <span class="n">optional</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="n">ReloadOnChange</span> <span class="p">=</span> <span class="n">reloadOnChange</span><span class="p">;</span> <span class="n">s</span><span class="p">.</span><span class="nf">ResolveFileProvider</span><span class="p">();</span> <span class="p">});</span> <span class="p">}</span> <span class="k">public</span> <span class="k">static</span> <span class="n">IConfigurationBuilder</span> <span class="nf">AddEnvFile</span><span class="p">(</span> <span class="k">this</span> <span class="n">IConfigurationBuilder</span> <span class="n">builder</span><span class="p">,</span> <span class="n">Action</span><span class="p">&lt;</span><span class="n">EnvConfigurationSource</span><span class="p">&gt;</span> <span class="n">configureSource</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">configureSource</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h2> Demonstration </h2> <p>Now that everything is set up, let's integrate it into an ASP.NET Core application.</p> <p>In the <code>Program.cs</code> or <code>Startup.cs</code>, add the following code to include your <code>.env</code> file in the configuration.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span> <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">AddEnvFile</span><span class="p">();</span> <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span> <span class="n">app</span><span class="p">.</span><span class="nf">MapGet</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="p">(</span><span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="c1">// Access the environment variables</span> <span class="kt">string</span> <span class="n">apiKey</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"API_KEY"</span><span class="p">]</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Missing API_KEY env variable"</span><span class="p">);</span> <span class="kt">string</span> <span class="n">databaseUrl</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"DATABASE_URL"</span><span class="p">]</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Missing DATABASE_URL env variable"</span><span class="p">);</span> <span class="kt">string</span> <span class="n">debug</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"DEBUG"</span><span class="p">]</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Missing DEBUG env variable"</span><span class="p">);</span> <span class="c1">// Output the values</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"API Key: </span><span class="p">{</span><span class="n">apiKey</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Database URL: </span><span class="p">{</span><span class="n">databaseUrl</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Debug Mode: </span><span class="p">{</span><span class="n">debug</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="k">return</span> <span class="k">new</span> <span class="p">{</span> <span class="n">apiKey</span><span class="p">,</span> <span class="n">databaseUrl</span><span class="p">,</span> <span class="n">debug</span> <span class="p">};</span> <span class="p">});</span> <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span> </code></pre> </div> <p>Run the application and we should see the result below.</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Ficonfiguration-env.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-3.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2Ficonfiguration-env.png" alt="Load .Env file into IConfiguration Provider"></a></p> <h2> Source Code </h2> <p>Source code available on Github repo.</p> <p>GitHub - ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles: <a href="proxy.php?url=https://github.com/ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles" rel="noopener noreferrer">https://github.com/ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles</a></p> <h2> Conclusion </h2> <p>By extending <code>IConfiguration</code> with a custom configuration provider, you can easily manage environment variables from a <code>.env</code> file in your ASP.NET Core applications.</p> <p>This approach keeps your configuration clean and organized, making it easier to handle different environments without hardcoding sensitive values.</p> <p>Again, feel free to enhance this solution by adding features like default values, nested configuration, or more sophisticated parsing logic. Happy coding! 😎</p> csharp dotnet beginners designpatterns The Strategy Design Pattern in C# Applications Ricardo Mon, 12 Aug 2024 17:09:24 +0000 https://dev.to/rmaurodev/the-strategy-design-pattern-in-c-applications-2pmp https://dev.to/rmaurodev/the-strategy-design-pattern-in-c-applications-2pmp <p>The Strategy design pattern offers a powerful solution for making algorithms interchangeable within your applications. Let's cover C# application and implementation in this article.</p> <p>By encapsulating related algorithms within a family and allowing them to be swapped seamlessly, this pattern enhances flexibility and maintainability.</p> <p>In this post, we'll dive into the Strategy pattern, explore its key concepts, and implement a simple example in C#.</p> <h2> Understanding the Strategy Pattern </h2> <p>The Strategy pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the clients that use it.</p> <h3> Key Components </h3> <ul> <li> <strong>Strategy Interface</strong> : A common interface for all supported algorithms.</li> <li> <strong>Concrete Strategies</strong> : Implementations of the strategy interface with specific algorithms.</li> <li> <strong>Context</strong> : Maintains a reference to a strategy object and allows clients to set or change the strategy at runtime.</li> </ul> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FStrategyDesignPatternExample.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres-2.cloudinary.com%2Fdgoa5llvh%2Fimage%2Fupload%2Fq_auto%2FStrategyDesignPatternExample.png" alt="The Strategy Design Pattern in C# Applications"></a></p> <h2> Implementing the Strategy Pattern in C </h2> <p>Let's implement the Strategy pattern with a simple example of sorting algorithms.</p> <h3> 1. Define the Strategy Interface </h3> <p>First, we define an interface that all sorting strategies will implement:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">ISortStrategy</span> <span class="p">{</span> <span class="k">void</span> <span class="nf">Sort</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h3> 2. Implement Concrete Strategies </h3> <p>Next, we create concrete classes that implement the sorting algorithms:</p> <h4> BubbleSortStrategy </h4> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">BubbleSortStrategy</span> <span class="p">:</span> <span class="n">ISortStrategy</span> <span class="p">{</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">Sort</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">list</span><span class="p">.</span><span class="n">Count</span> <span class="p">-</span> <span class="m">1</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">j</span> <span class="p">&lt;</span> <span class="n">list</span><span class="p">.</span><span class="n">Count</span> <span class="p">-</span> <span class="n">i</span> <span class="p">-</span> <span class="m">1</span><span class="p">;</span> <span class="n">j</span><span class="p">++)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">&gt;</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span> <span class="p">+</span> <span class="m">1</span><span class="p">])</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">temp</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span> <span class="p">+</span> <span class="m">1</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span> <span class="p">+</span> <span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="n">temp</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h4> QuickSortStrategy </h4> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">QuickSortStrategy</span> <span class="p">:</span> <span class="n">ISortStrategy</span> <span class="p">{</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">Sort</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">)</span> <span class="p">{</span> <span class="nf">QuickSort</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">list</span><span class="p">.</span><span class="n">Count</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span> <span class="p">}</span> <span class="k">private</span> <span class="k">void</span> <span class="nf">QuickSort</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">,</span> <span class="kt">int</span> <span class="n">low</span><span class="p">,</span> <span class="kt">int</span> <span class="n">high</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">low</span> <span class="p">&lt;</span> <span class="n">high</span><span class="p">)</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">pi</span> <span class="p">=</span> <span class="nf">Partition</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">low</span><span class="p">,</span> <span class="n">high</span><span class="p">);</span> <span class="nf">QuickSort</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">low</span><span class="p">,</span> <span class="n">pi</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span> <span class="nf">QuickSort</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">pi</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">high</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">private</span> <span class="kt">int</span> <span class="nf">Partition</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">,</span> <span class="kt">int</span> <span class="n">low</span><span class="p">,</span> <span class="kt">int</span> <span class="n">high</span><span class="p">)</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">pivot</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">high</span><span class="p">];</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="p">(</span><span class="n">low</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="p">=</span> <span class="n">low</span><span class="p">;</span> <span class="n">j</span> <span class="p">&lt;</span> <span class="n">high</span><span class="p">;</span> <span class="n">j</span><span class="p">++)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">&lt;=</span> <span class="n">pivot</span><span class="p">)</span> <span class="p">{</span> <span class="n">i</span><span class="p">++;</span> <span class="kt">int</span> <span class="n">temp</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">=</span> <span class="n">temp</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">temp1</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="m">1</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="n">list</span><span class="p">[</span><span class="n">high</span><span class="p">];</span> <span class="n">list</span><span class="p">[</span><span class="n">high</span><span class="p">]</span> <span class="p">=</span> <span class="n">temp1</span><span class="p">;</span> <span class="k">return</span> <span class="n">i</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> 3. Create the Context Class </h3> <p>The context class maintains a reference to a strategy object and allows clients to change the strategy:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">SortContext</span> <span class="p">{</span> <span class="k">private</span> <span class="n">ISortStrategy</span> <span class="n">_sortStrategy</span><span class="p">;</span> <span class="k">public</span> <span class="nf">SortContext</span><span class="p">(</span><span class="n">ISortStrategy</span> <span class="n">sortStrategy</span><span class="p">)</span> <span class="p">{</span> <span class="n">_sortStrategy</span> <span class="p">=</span> <span class="n">sortStrategy</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">SetStrategy</span><span class="p">(</span><span class="n">ISortStrategy</span> <span class="n">sortStrategy</span><span class="p">)</span> <span class="p">{</span> <span class="n">_sortStrategy</span> <span class="p">=</span> <span class="n">sortStrategy</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">Sort</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span><span class="p">)</span> <span class="p">{</span> <span class="n">_sortStrategy</span><span class="p">.</span><span class="nf">Sort</span><span class="p">(</span><span class="n">list</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> 4. Using the Strategy Pattern </h3> <p>Finally, we use the context class to apply different sorting strategies:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">class</span> <span class="nc">Program</span> <span class="p">{</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">numbers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">34</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="m">23</span><span class="p">,</span> <span class="m">32</span><span class="p">,</span> <span class="m">5</span><span class="p">,</span> <span class="m">62</span> <span class="p">};</span> <span class="c1">// Use BubbleSort strategy</span> <span class="n">SortContext</span> <span class="n">context</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SortContext</span><span class="p">(</span><span class="k">new</span> <span class="nf">BubbleSortStrategy</span><span class="p">());</span> <span class="n">context</span><span class="p">.</span><span class="nf">Sort</span><span class="p">(</span><span class="n">numbers</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Bubble Sorted List: "</span> <span class="p">+</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">numbers</span><span class="p">));</span> <span class="c1">// Change to QuickSort strategy</span> <span class="n">context</span><span class="p">.</span><span class="nf">SetStrategy</span><span class="p">(</span><span class="k">new</span> <span class="nf">QuickSortStrategy</span><span class="p">());</span> <span class="n">numbers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">34</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="m">23</span><span class="p">,</span> <span class="m">32</span><span class="p">,</span> <span class="m">5</span><span class="p">,</span> <span class="m">62</span> <span class="p">};</span> <span class="c1">// Reset the list</span> <span class="n">context</span><span class="p">.</span><span class="nf">Sort</span><span class="p">(</span><span class="n">numbers</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Quick Sorted List: "</span> <span class="p">+</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">numbers</span><span class="p">));</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h2> Conclusion </h2> <p>The Strategy design pattern in C# provides a robust framework for creating interchangeable algorithms, promoting flexibility and maintainability in your applications.</p> <p>By encapsulating each algorithm within its own class and using a common interface, the Strategy pattern adheres to the open/closed principle, making it easier to introduce new algorithms without modifying existing code. Embrace the Strategy pattern to enhance your software development practices and build more adaptable systems.</p> <p>For more detailed articles and hands-on tutorials on .NET and C#, check out my blog at <a href="proxy.php?url=https://rmauro.dev/" rel="noopener noreferrer">rmauro.dev</a>.</p> designpatterns csharp dotnet beginners Reading .env Files in C# Ricardo Fri, 09 Aug 2024 14:49:06 +0000 https://dev.to/rmaurodev/reading-env-files-in-c-124g https://dev.to/rmaurodev/reading-env-files-in-c-124g <p>In C#, managing application configuration using environment variables stored in a <strong>.env</strong> file is a best practice, especially for handling different environments like development, staging, and production.</p> <p>A <code>.env</code> file is a convenient way to store these variables locally during development.</p> <p>In this post, let's check how to read a <em>.env</em> file in a C# application without relying on any third-party libraries. In Fact it's pretty simple to do.</p> <h2> Why Use a .env File? </h2> <p>A <em>.env</em> file contains key-value pairs representing environment variables.<br><br> It helps in keeping configuration separate from the codebase and is especially useful for.</p> <ul> <li> <strong>Security:</strong> Sensitive information like API keys can be excluded from the codebase.</li> <li> <strong>Consistency:</strong> Ensures that configuration is consistent across different environments.</li> <li> <strong>Convenience:</strong> Easy to switch between different configurations during development.</li> </ul> <h2> Setting Up the Project </h2> <p>Let's start with a simple .NET console application.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>dotnet new console <span class="nt">-n</span> EnvReaderApp <span class="nb">cd </span>EnvReaderApp </code></pre> </div> <p>Create a <em>.env</em> file in the root of your project directory.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># .env</span> <span class="nv">API_KEY</span><span class="o">=</span>12345 <span class="nv">DATABASE_URL</span><span class="o">=</span>localhost:5432 <span class="nv">DEBUG</span><span class="o">=</span><span class="nb">true</span> </code></pre> </div> <h2> Implementing the .env File Reader </h2> <p>Let's implement a simple parser that reads the <em>.env</em> file and sets the variables as environment variables in the application.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span> <span class="k">using</span> <span class="nn">System.IO</span><span class="p">;</span> <span class="k">class</span> <span class="nc">EnvReader</span> <span class="p">{</span> <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Load</span><span class="p">(</span><span class="kt">string</span> <span class="n">filePath</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(!</span><span class="n">File</span><span class="p">.</span><span class="nf">Exists</span><span class="p">(</span><span class="n">filePath</span><span class="p">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">FileNotFoundException</span><span class="p">(</span><span class="s">$"The file '</span><span class="p">{</span><span class="n">filePath</span><span class="p">}</span><span class="s">' does not exist."</span><span class="p">);</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">line</span> <span class="k">in</span> <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllLines</span><span class="p">(</span><span class="n">filePath</span><span class="p">))</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="p">||</span> <span class="n">line</span><span class="p">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s">"#"</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span> <span class="c1">// Skip empty lines and comments</span> <span class="kt">var</span> <span class="n">parts</span> <span class="p">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">'='</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">parts</span><span class="p">.</span><span class="n">Length</span> <span class="p">!=</span> <span class="m">2</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> <span class="c1">// Skip lines that are not key-value pairs</span> <span class="kt">var</span> <span class="n">key</span> <span class="p">=</span> <span class="n">parts</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="nf">Trim</span><span class="p">();</span> <span class="kt">var</span> <span class="k">value</span> <span class="p">=</span> <span class="n">parts</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="nf">Trim</span><span class="p">();</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">SetEnvironmentVariable</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>Let's use the EnvReader in our project.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight csharp"><code><span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Load environment variables from .env file</span> <span class="n">EnvReader</span><span class="p">.</span><span class="nf">Load</span><span class="p">(</span><span class="s">".env"</span><span class="p">);</span> <span class="c1">// Access the environment variables</span> <span class="kt">string</span> <span class="n">apiKey</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"API_KEY"</span><span class="p">);</span> <span class="kt">string</span> <span class="n">databaseUrl</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"DATABASE_URL"</span><span class="p">);</span> <span class="kt">string</span> <span class="n">debug</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"DEBUG"</span><span class="p">);</span> <span class="c1">// Output the values</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"API Key: </span><span class="p">{</span><span class="n">apiKey</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Database URL: </span><span class="p">{</span><span class="n">databaseUrl</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Debug Mode: </span><span class="p">{</span><span class="n">debug</span><span class="p">}</span><span class="s">"</span><span class="p">);</span> <span class="c1">// Check if debug mode is enabled</span> <span class="k">if</span> <span class="p">(</span><span class="kt">bool</span><span class="p">.</span><span class="nf">TryParse</span><span class="p">(</span><span class="n">debug</span><span class="p">,</span> <span class="k">out</span> <span class="kt">bool</span> <span class="n">isDebug</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="n">isDebug</span><span class="p">)</span> <span class="p">{</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Debug mode is enabled."</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Debug mode is not enabled."</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> How It Works </h3> <ol> <li> <strong>Reading the .env File:</strong> The <code>Load</code> method reads all lines from the <code>.env</code> file.</li> <li> <strong>Processing Each Line:</strong> It skips empty lines and comments, splits the line into key and value, and sets them as environment variables using <code>Environment.SetEnvironmentVariable</code>.</li> <li> <strong>Using Environment Variables:</strong> The <code>Main</code> method demonstrates accessing these variables and using them in the application logic.</li> </ol> <h3> Best Practices </h3> <ol> <li> <strong>Security:</strong> Add the <code>.env</code> file to your <code>.gitignore</code> file to avoid committing sensitive information.</li> <li> <strong>Template Files:</strong> Use a <code>.env.example</code> file with placeholder values to share configuration requirements without exposing sensitive data.</li> <li> <strong>Error Handling:</strong> Implement robust error handling for scenarios like missing files or incorrect formats.</li> </ol> <h3> Conclusion </h3> <p>Reading a <code>.env</code> file in C# without third-party libraries is straightforward and allows you to manage configuration securely and efficiently.</p> <p>This approach gives you complete control over how you handle and parse environment variables, which can be tailored to fit specific needs or constraints.</p> <p>Feel free to enhance this solution by adding features like default values, nested configuration, or more sophisticated parsing logic. Happy coding! 😎</p> csharp dotnet programming beginners