<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Luke Lowrey</title>
    <description>The latest articles on Forem by Luke Lowrey (@lukencode).</description>
    <link>https://forem.com/lukencode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F78084%2F90c4f974-aecb-4511-8def-734f9e6b3d79.png</url>
      <title>Forem: Luke Lowrey</title>
      <link>https://forem.com/lukencode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lukencode"/>
    <language>en</language>
    <item>
      <title>14 .NET packages I always recommend</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sat, 20 Mar 2021 02:26:20 +0000</pubDate>
      <link>https://forem.com/lukencode/14-net-packages-i-always-recommend-454p</link>
      <guid>https://forem.com/lukencode/14-net-packages-i-always-recommend-454p</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F03%2Fnuget-cover-min.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F03%2Fnuget-cover-min.png" alt="14 .NET packages I always recommend"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post lists open source libraries available on &lt;a href="http://nuget.org/" rel="noopener noreferrer"&gt;Nuget.org&lt;/a&gt; that I regularly use and recommend. These packages will save you time and make your applications better. They have good documentation and a great developer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  MediatR
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jbogard/MediatR" rel="noopener noreferrer"&gt;MediatR&lt;/a&gt; is a simple in-process implementation of the &lt;a href="https://en.wikipedia.org/wiki/Mediator_pattern" rel="noopener noreferrer"&gt;mediator pattern&lt;/a&gt;. It helps developer write clean, decoupled and extendable code. Mediator is configured using dependency injection with out of the box support for .NET Core, Autofac and others. The &lt;a href="https://github.com/jbogard/MediatR/wiki" rel="noopener noreferrer"&gt;documentation on Github&lt;/a&gt; is the best place to get started.&lt;/p&gt;

&lt;p&gt;I like to use MediatR to write simple command query type requests using the &lt;em&gt;IRequest&lt;/em&gt; interface. &lt;em&gt;IRequest&lt;/em&gt; handle messages sent to a single handler. Examples of types of &lt;em&gt;IRequests&lt;/em&gt; in a standard web application might be "&lt;em&gt;GetUserQuery&lt;/em&gt;" or "&lt;em&gt;UpdateCartCommand&lt;/em&gt;". The example below uses an internal nested class to keep the query model and query handler together in one class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//a simple "query" type request&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUserQuery&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUserQueryHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserGetQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="n"&gt;_userService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UserGetQueryHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_userService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetUserQuery&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//normlly something more extensive here...&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//calling code&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;mediator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GetUserQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
A simple "query" type mediatr request





&lt;p&gt;Mediatr also supports notification messages which can have multiple handlers through INotification. Notifications work in a similar way to requests but don't return values. They are great for exposing a "extension point" in your application that you may need to build on later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a side note: everything &lt;a href="https://jimmybogard.com/" rel="noopener noreferrer"&gt;Jimmy Bogard&lt;/a&gt; writes code or otherwise is great.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Serilog
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://serilog.net/" rel="noopener noreferrer"&gt;Serilog&lt;/a&gt; provides &lt;em&gt;structured&lt;/em&gt; logging which can target output to just about anywhere. Structured logs capture your log statements and objects as json. They give much more context into what is happening in your application and with the right tools they can be queried and analysed in more detail than standard text logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Latitude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Longitude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;134&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;elapsedMs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processed {@Position} in {Elapsed:000} ms."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elapsedMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//log output&lt;/span&gt;
&lt;span class="c1"&gt;//{"Position": {"Latitude": 25, "Longitude": 134}, "Elapsed": 34}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example from Serilog website





&lt;p&gt;One of the best things about Serilog is the community has written sinks - which write logs to an provider for just about every service you can think of. The &lt;a href="https://github.com/serilog/serilog/wiki/Provided-Sinks" rel="noopener noreferrer"&gt;provided sinks&lt;/a&gt; Github page has a full list. A couple I recommend checking out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://datalust.co/seq" rel="noopener noreferrer"&gt;Seq&lt;/a&gt; - self hosted structured log viewer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;- exception reporting service that plugs in automatically to &lt;em&gt;error&lt;/em&gt; log events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hangfire
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.hangfire.io/" rel="noopener noreferrer"&gt;Hangfire&lt;/a&gt; is an easy way to perform scheduled or fire-and forget background jobs for .NET Core and Framework applications. I love how easy it is to get up an running as the jobs can run in your app's main process without requiring a dedicated service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Fire and forget&lt;/span&gt;
&lt;span class="n"&gt;BackgroundJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This will run once, in the background"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//Recurring job&lt;/span&gt;
&lt;span class="n"&gt;RecurringJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOrUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This will run daily"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Daily&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Calling a dependency injection job&lt;/span&gt;
&lt;span class="n"&gt;BackgroundJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;myService&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example Hangfire jobs





&lt;p&gt;Hangfire has a great built in dashboard, dependency injection support and storage options for SQL server, PostgreSQL, Redis and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F03%2Fimage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F03%2Fimage.png" alt="14 .NET packages I always recommend"&gt;&lt;/a&gt;Hangfire dashboard&lt;/p&gt;

&lt;h3&gt;
  
  
  FluentEmail
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lukencode/FluentEmail" rel="noopener noreferrer"&gt;FluentEmail&lt;/a&gt; (written by me) helps you implement complete email sending functionality in your app in less than 10 minutes. It features built in providers for the most popular email senders including SendGrid and Mailgun along with Razor templates out of the box. I recently wrote a &lt;a href="https://lukelowrey.com/dotnet-email-guide-2021/" rel="noopener noreferrer"&gt;full guide to .NET email using FluentEmail&lt;/a&gt; that will get you started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bill.gates@microsoft.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"luke.lowrey@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Luke"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi Luke!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fluent email looks great!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Send an email with FluentEmail





&lt;h3&gt;
  
  
  LazyCache
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/alastairtree/LazyCache" rel="noopener noreferrer"&gt;LazyCache&lt;/a&gt; is an easy to use, thread safe and developer friendly wrapper for in-memory caching in .NET Core. I've &lt;a href="https://lukelowrey.com/caching-in-aspnet-core-with-lazycache/" rel="noopener noreferrer"&gt;written a bit about LazyCache&lt;/a&gt; in the past and I still reach for it every time I need a cache provider.&lt;/p&gt;

&lt;p&gt;LazyCache uses a "GetOrAdd" pattern for caching where you request an item from the cache and at the same time provide the function to add that item to the cache if it is missing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrAddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HomePageData"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;homePageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//function to get cachable data&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//12 hour cache expiry&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example cache GetOrAdd call





&lt;p&gt;LazyCache is easy to extend so if you need to move beyond a simple in memory cache to something distributed the step up should be pretty seamless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dapper
&lt;/h3&gt;

&lt;p&gt;Sometimes SQL data access with Entity Framework is overkill or just &lt;em&gt;too slow&lt;/em&gt;. When you want to run raw SQL but still get some nice object mapping &lt;a href="https://github.com/StackExchange/Dapper" rel="noopener noreferrer"&gt;Dapper&lt;/a&gt; is the way to go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dapper is a &lt;a href="https://www.nuget.org/packages/Dapper" rel="noopener noreferrer"&gt;NuGet library&lt;/a&gt; that you can add in to your project that will extend your &lt;code&gt;IDbConnection&lt;/code&gt; interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dapper takes your existing database connection and adds extension methods to run raw SQL and map it back to C# classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dogs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"select * from Dogs where Age &amp;gt; @Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//sql query, @ parameters&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;//anonymous object to map parameters&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example dapper query





&lt;h3&gt;
  
  
  MiniProfiler
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://miniprofiler.com/" rel="noopener noreferrer"&gt;MiniProfiler&lt;/a&gt; is a simple performance profiler for .NET Core and framework (also Ruby, Go and Node) web applications. It attaches automatically to the key parts of your application - mainly database access. MiniProfiler will then render an awesome little timing result directly on your app. It is great for running during development to pick up rogue LINQ queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F02%2Fimage-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F02%2Fimage-6.png" alt="14 .NET packages I always recommend"&gt;&lt;/a&gt;MiniProfiler results&lt;/p&gt;

&lt;p&gt;MiniProfiler has &lt;a href="https://miniprofiler.com/dotnet/AspDotNet" rel="noopener noreferrer"&gt;great setup documentation&lt;/a&gt; with a heap of options. For security reasons, I normally only have it enabled during development.&lt;/p&gt;

&lt;h3&gt;
  
  
  LinqKit
&lt;/h3&gt;

&lt;p&gt;LINQKit is a set of extensions for querying Entity Framework with Linq. LINQKit allows you to build linq queries and conditions as predicates.&lt;/p&gt;

&lt;p&gt;My most common use for LINQKit is simplifying queries with lots of criteria (think grids with filters) using the PredicateBuilder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//base linq query&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//add conditions as required&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PredicateBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;//apply conditions to query&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;filteredUsers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsExpandable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example LINQKit predicate usage to add optional query conditions





&lt;h3&gt;
  
  
  FluentMigrator or DbUp
&lt;/h3&gt;

&lt;p&gt;If you are looking for an alternative to running EntityFramework code first migrations both FluentMigrator and DbUp will do the job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dbup.github.io/" rel="noopener noreferrer"&gt;DbUp&lt;/a&gt; works best if you like raw SQL and simple scripts. You simply create a console application and add SQL files as embedded resources. Some simple code in Program.cs will run your migration scripts in order - easy to integrate into a devops pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F02%2Fimage-7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flukelowrey.com%2Fcontent%2Fimages%2F2021%2F02%2Fimage-7.png" alt="14 .NET packages I always recommend"&gt;&lt;/a&gt;Embedded scripts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//or some configuration&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;upgrader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;DeployChanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SqlDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithScriptsEmbeddedInAssembly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogToConsole&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upgrader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PerformUpgrade&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;//omitted error handling...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Program.cs





&lt;p&gt;&lt;a href="https://fluentmigrator.github.io/" rel="noopener noreferrer"&gt;FluentMigrator&lt;/a&gt; takes a similar concept with a more code-based approach. Instead of adding raw scripts you create small classes for each migration. This approach gives you more flexibility in building migrations (and saves you remembering the create foreign key sql syntax) and allows for more complete up/down migration scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FluentMigrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Migrations&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20180430121800&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddLogTable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Migration&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Log"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AsInt64&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;PrimaryKey&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Text"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AsString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Log"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Example migration creating a Log table





&lt;h3&gt;
  
  
  CsvHelper
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://joshclose.github.io/CsvHelper/" rel="noopener noreferrer"&gt;CsvHelper&lt;/a&gt; is my go to package for reading and writing csv files.  The library is flexible enough that you can creating custom mapping classes and rules for your C# classes or use the low level row and column methods directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//import csv to a class&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StreamReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"importfile.csv"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CsvReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterClassMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;YourClassMap&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;//optionally use a mapping class&lt;/span&gt;
   &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRecords&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;YourClass&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//use a mapping class&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YourClass&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YourClassMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;YourClass&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FooMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Identifier"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//overrider column names and more&lt;/span&gt;
        &lt;span class="nf"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TheName"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//or access columns directly&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CsvReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadHeader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetField&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Import csv file examples





&lt;p&gt;There are similar options for exporting csv files either with direct column writes or using classes and mapping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StreamWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path\\to\\file.csv"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CsvWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Simple export csv example





&lt;h3&gt;
  
  
  Hashids
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hashids.org/net/" rel="noopener noreferrer"&gt;Hashids for .NET&lt;/a&gt; generates short, unique string identifiers from integers - similar to YouTube video ids. It is great for turning one or more integers into a single short, non sequencial hashes to use in urls and sharing. Just don't use it in place of security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hashids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Hashids&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example salt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//creates "R9tGSE" hash&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//decodes "R9tGSE" back into int[] {1, 2, 3}&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Humanizer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/Humanizr/Humanizer" rel="noopener noreferrer"&gt;Humanizer&lt;/a&gt; is for turning dates, numbers, enums and more in human friendly readable strings. It supports lots of data types and can be used with multiple languages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//These are some methods I use all the time, check out the documentation for the full list&lt;/span&gt;

&lt;span class="c1"&gt;//dates&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHours&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Humanize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"yesterday"&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Humanize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"4 hours from now"&lt;/span&gt;

&lt;span class="c1"&gt;//timespan&lt;/span&gt;
&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Humanize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"2 weeks"&lt;/span&gt;

&lt;span class="c1"&gt;//Pluralize and singularize&lt;/span&gt;
&lt;span class="s"&gt;"Dog"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Pluralize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Dogs"&lt;/span&gt;
&lt;span class="s"&gt;"Dogs"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Singularize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Dog"&lt;/span&gt;

&lt;span class="c1"&gt;//truncate&lt;/span&gt;
&lt;span class="s"&gt;"Long text to truncate"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Long text…"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Common Humanizer methods





&lt;h3&gt;
  
  
  Bogus
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/bchavez/Bogus" rel="noopener noreferrer"&gt;Bogus&lt;/a&gt; is a "fake" data generator for .NET. I shudder at the thought of generating "demo" data and "test" data but Bogus takes a bit part of that pain away.&lt;/p&gt;

&lt;p&gt;Bogus allow you to map you C# classes to fake data to quickly generate lots of data. It is also possible to access the underlying methods directly and generate a phone number, email address etc on demand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//map a class&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userIds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testUsers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Avatar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Avatar&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;//generate users&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testUsers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;//use methods directly&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DomainName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//eg "sylvester.com"&lt;/span&gt;
&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// eg "enable leading-edge architectures"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Simple Bogus data generation examples





&lt;h3&gt;
  
  
  Honourable mentions
&lt;/h3&gt;

&lt;p&gt;I also love these libraries but this post was taking me forever to finish.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fluentvalidation.net/" rel="noopener noreferrer"&gt;FluentValidation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.automapper.org/en/stable/" rel="noopener noreferrer"&gt;AutoMapper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://autofac.org/" rel="noopener noreferrer"&gt;Autofac&lt;/a&gt;- if you are stuck on .NET framework this if your best dependency injection option&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you enjoyed the post, please share the thread on Twitter and let me know if I missed out your favourite libraries.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1374102332225490947-882" src="https://platform.twitter.com/embed/Tweet.html?id=1374102332225490947"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1374102332225490947-882');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1374102332225490947&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>opensource</category>
      <category>csharp</category>
      <category>nuget</category>
    </item>
    <item>
      <title>A complete guide to send email in .NET (2021)</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Tue, 26 Jan 2021 02:00:00 +0000</pubDate>
      <link>https://forem.com/lukencode/a-complete-guide-to-send-email-in-net-2021-4ce8</link>
      <guid>https://forem.com/lukencode/a-complete-guide-to-send-email-in-net-2021-4ce8</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sagaYBH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2021/01/email3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sagaYBH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2021/01/email3.jpg" alt="A complete guide to send email in .NET (2021)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lukencode/FluentEmail"&gt;FluentEmail&lt;/a&gt; is an open-source .NET library (created by me but with lots of help) that helps you implement complete email sending functionality in you dotnet app in less than 10 minutes (probably). It features built in providers for the most popular email senders including SendGrid and Mailgun along with Razor or Liquid templates out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use FluentEmail?
&lt;/h2&gt;

&lt;p&gt;FluentEmail pieces together the components your application needs for sending email.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API is so simple that autocomplete (almost) writes your code for you! &lt;/li&gt;
&lt;li&gt;Well tested providers for the popular email senders and template engines are available out of the box (keep reading to see examples)&lt;/li&gt;
&lt;li&gt;Solid dependency injection keeps your code clean and allows you to easily write tests or switch out email options for different tenants/environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;p&gt;FluentEmail and its providers can be installed via Nuget. The FluentEmail.Core library is all you need to get started.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.Core&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To construct and send an email you just keep building up the fluent methods on the IEmail class. When you are ready to send call SendAsync().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bill.gates@microsoft.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"luke.lowrey@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Luke"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi Luke!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fluent email looks great!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are the most common methods available on the email object (for the full list see &lt;a href="https://github.com/lukencode/FluentEmail/blob/master/src/FluentEmail.Core/IFluentEmail.cs"&gt;IFluentEmail.cs&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;. &lt;strong&gt;To&lt;/strong&gt; (string emailAddress) - &lt;em&gt;add recipients&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;. &lt;strong&gt;SetFrom&lt;/strong&gt; (string emailAddress) - &lt;em&gt;change the sender address&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;. &lt;strong&gt;CC/BCC&lt;/strong&gt; (string emailAddress) - &lt;em&gt;add CC or BCC&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;. &lt;strong&gt;Subject&lt;/strong&gt; (string subject) - &lt;em&gt;set the subject&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;. &lt;strong&gt;Body&lt;/strong&gt; (string body) - &lt;em&gt;set the message body (without templating)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;. &lt;strong&gt;Attach&lt;/strong&gt; (Attachment attachment) - &lt;em&gt;add attachments&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UsingTemplate&lt;/strong&gt; (string template, T model, bool isHtml = true) - &lt;em&gt;set a template, keep reading for more templating information&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SendAsync&lt;/strong&gt; () - &lt;em&gt;send the email using the configured sender&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dependency Injection
&lt;/h2&gt;

&lt;p&gt;The best way to use FluentEmail is to configure your sender and template choices with dependency injection. There is good out of the box support for the built in .NET DependencyInjection classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRazorRenderer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSmtpSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example sets up FluentEmail with a default SendFrom address and uses the injection helpers to add the SmtpSender and RazorRenderer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ISender&lt;/strong&gt; - this interface is the provider that will be used to send the email. In this example the AddSmtpSender() method configures an SmtpSender provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ITemplateRenderer&lt;/strong&gt; - this is an optional interface to provide email templating support. In the example the AddRazorRenderer() method is setting up the RazorRenderer provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using dependency injection will make a couple of interfaces available to your code. See the example below for sending email with the configured services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailExampleController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendSingleEmail&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromServices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;IFluentEmail&lt;/span&gt; &lt;span class="n"&gt;singleEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;    
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;singleEmail&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a single email"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendMultipleEmail&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromServices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;IFluentEmailFactory&lt;/span&gt; &lt;span class="n"&gt;emailFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;    
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailFactory&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test email 1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is the first email"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;email1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailFactory&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test email 2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is the second email"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;email2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IEmail&lt;/strong&gt; - this is a single instance of an email to be sent as demonstrated in the basic example above. Use this to send one email (but no more!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IEmailFactory&lt;/strong&gt; - this interface has a method .Create() which will give you an IEmail instance. Use this interface to send multiple emails in the same method or request - see the following example.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Template Rendering
&lt;/h2&gt;

&lt;p&gt;FluentEmail has core support for multiple template renderer providers. To use template rendering, include one of the providers and configure it as part of your dependency injection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using Razor templating package (or set using AddRazorRenderer in services)&lt;/span&gt;
&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRenderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RazorRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Dear @Model.Name, You are totally @Model.Compliment."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Luke Lowrey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Developer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hi Luke, this is an email message"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bob@hotmail.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"somedude@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"woo nuget"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsingTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UsingTemplate method accepts a string for the template and an object for the model to use. The template itself will depend on the provider you are using, this example uses the standard RazorRenderer&lt;/p&gt;

&lt;p&gt;FluentEmail also providers helper methods to source template files from disk or as embedded resources as well as serving different templates based on culture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UsingTemplateFromEmbedded&lt;/strong&gt; (string path, T model, Assembly assembly) - pass the assembly with the embedded resource and the path eg "YourApp.EmailTemplates.Welcome.txt"".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UsingTemplateFromFile&lt;/strong&gt; (string filename, T model) - pass the file path for the template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Razor Email Templates
&lt;/h3&gt;

&lt;p&gt;The examples above use the FluentEmail.Razor template renderer. This is the most popular option for templating. The Razor render uses the &lt;a href="https://github.com/toddams/RazorLight"&gt;RazorLight&lt;/a&gt;library to render Razor strings.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.Razor&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The RazorRenderer supports any valid Razor code. This example (taken from the test project) shows how you can use layout pages in templates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure the Razor Renderer&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;//pass in the root directory for files&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRazorRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCurrentDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 

        &lt;span class="c1"&gt;//OR pass in the root assembly if you use embedded resources&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRazorRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//In your template code include a layout file&lt;/span&gt;
&lt;span class="c1"&gt;//the template could be sourced from file/embedded if that is configured&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
    @{ Layout = ""./Shared/_Layout.cshtml""; }

    Hi @Model.Name here is a list @foreach(var i in Model.Numbers) { @i }
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"LUKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Numbers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Razor template example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsingTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Liquid Templates
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://shopify.github.io/liquid/"&gt;Liquid templates&lt;/a&gt; are a more restricted templating language created by Shopify. They are more lightweight than Razor templates as well as safer and simpler for end users to create. Properties on the model are made available as Liquid properties in the template. FluentEmail uses &lt;a href="https://github.com/sebastienros/fluid"&gt;Fluid&lt;/a&gt; (see &lt;a href="https://github.com/sebastienros/fluid"&gt;samples&lt;/a&gt;) to render the templates in C#.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure the Razor Renderer&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLiquidRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// file provider is used to resolve layout files if they are in use&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PhysicalFileProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;someRootPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"EmailTemplates"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Liquid template which utilizes layout&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
    {% layout '_layout.liquid' %}    
    Dear {{ Name }}, You are totally {{ Compliment }}.
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ViewModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Luke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Compliment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Awesome"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"somedude@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Liquid templates"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsingTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Email Senders
&lt;/h2&gt;

&lt;p&gt;FluentEmail allows you to plug in popular email sending providers (or build your own by implementing ISender). To use a sender include the provider and configure it as part of dependency injection.&lt;/p&gt;

&lt;p&gt;The following senders are available as core libraries. The same core FluentEmail methods are used to send emails, the only difference is what sender you configure.&lt;/p&gt;

&lt;h3&gt;
  
  
  SMTP Sender
&lt;/h3&gt;

&lt;p&gt;The SMTP sender use &lt;em&gt;System.Net.Mail.SmtpClient&lt;/em&gt; to send email. It is set up using dependency injection. You can provide basic host and port details or a full SmtpClient instance.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.Smtp&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure smtp sender&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSmtpSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yoursmtphost.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//configure host and port&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSmtpSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SmtpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;//OR provide your own SmtpClient instance&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Send email as per examples above&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email body"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//this will use the SmtpSender&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mailgun Sender
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt; is an API based email sender. The FluentEmail Mailgun sender supports most functionality including attachments, tags and headers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.Mailgun&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure Mailgun sender&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMailGunSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"domainname.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"yourapikey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FluentEmail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mailgun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MailGunRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;USA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Send email as per examples above&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mailgun example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email body"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tagname"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//the Mailgun sender supports tags&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//this will use the MailgunSender&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SendGrid Sender
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://sendgrid.com/"&gt;SendGrid&lt;/a&gt;is another popular API based email sender. The FluentEmail SendGrid provider can be used in "live" or "sandbox" mode.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.Mailgun&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure SendGrid sender&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSendGridSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Send emails as per examples above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MimeKit Sender
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jstedfast/MimeKit"&gt;MimeKit&lt;/a&gt;is an open source .NET email library with support for IMAP, POP3, and SMTP. If you need to do anything advanced with email protocols you need to use MimeKit.&lt;/p&gt;

&lt;p&gt;The MimeKit sender for FluentEmail lets you to set up whatever flavour of email sending you need and include it in the FluentEmail framework.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package FluentEmail.MailKit&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//configure MailKit sender&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFluentEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fromemail@test.test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMailKitSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SmtpClientOptions&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;UseSsl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Send emails as per examples above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Senders
&lt;/h3&gt;

&lt;p&gt;Other people in the open source community have built and maintain senders for different email providers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft Graph API Sender - Microsoft Graph API Sender - &lt;a href="https://www.nuget.org/packages/FluentEmail.Graph/"&gt;https://www.nuget.org/packages/FluentEmail.Graph/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Postmark Sender - &lt;a href="https://www.nuget.org/packages/FluentEmail.Postmark/"&gt;https://www.nuget.org/packages/FluentEmail.Postmark/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MailJet Sender - &lt;a href="https://www.nuget.org/packages/FluentEmail.Mailjet/"&gt;https://www.nuget.org/packages/FluentEmail.Mailjet/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MailTrap Sender - &lt;a href="https://www.nuget.org/packages/FluentEmail.Mailtrap/"&gt;https://www.nuget.org/packages/FluentEmail.Mailtrap/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extending FluentEmail
&lt;/h2&gt;

&lt;p&gt;FluentEmail was built to make it easy to plug in your own providers for templating and sending. If you are working on something you think belongs in the core repository head on over to the &lt;a href="https://github.com/lukencode/FluentEmail"&gt;Github repo&lt;/a&gt;and let me know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this post useful and want to help me (and others out) please share the thread on twitter.&lt;/em&gt;&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NU-GNL14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1281104920465207296/bfXOxLFb_normal.png" alt="Luke Lowrey 🛴 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Luke Lowrey 🛴
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/lukencode"&gt;@lukencode&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Just posted a .NET guide for sending email from your app in under 10 minutes using FluentEmail &lt;a href="https://twitter.com/hashtag/dotnet"&gt;#dotnet&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;a href="https://t.co/ywm371tprV"&gt;lukelowrey.com/dotnet-email-g…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      10:47 AM - 26 Jan 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1354018040673755137" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1354018040673755137" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1354018040673755137" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>GitHub Action - Build and Run .NET Tests on new pull requests</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Mon, 31 Aug 2020 11:21:26 +0000</pubDate>
      <link>https://forem.com/lukencode/github-action-build-and-run-net-tests-on-new-pull-requests-3m61</link>
      <guid>https://forem.com/lukencode/github-action-build-and-run-net-tests-on-new-pull-requests-3m61</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--14EDJAXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/08/Annotation-2020-08-31-205922.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--14EDJAXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/08/Annotation-2020-08-31-205922.png" alt="GitHub Action - Build and Run .NET Tests on new pull requests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pull request validation saves time and encourages better coding practices. You can easily set up a &lt;a href="https://github.com/features/actions"&gt;GitHub action&lt;/a&gt; to validate new code by building and running tests when a new PR is created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a GitHub Action to run on new pull requests
&lt;/h3&gt;

&lt;p&gt;Add an action to your repository using the "&lt;em&gt;Actions" tab -&amp;gt; New workflow&lt;/em&gt; on github.com or by creating a build-and-test.yml file in the &lt;em&gt;.github/workflows/&lt;/em&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example &lt;a href="https://github.com/lukencode/FluentEmail/blob/master/.github/workflows/dotnet-core.yml"&gt;dotnet-core.yml&lt;/a&gt; from FluentEmail&lt;/em&gt;&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Build &amp;amp; Test

on:
  pull_request:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.101
    - name: Install dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release --no-restore
    - name: Test
      run: dotnet test --no-restore --verbosity normal

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The action uses the &lt;em&gt;on: pull_request&lt;/em&gt; trigger which accepts the branches you want to run the workflow&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;actions/checkout@v2&lt;/em&gt; and &lt;em&gt;actions/setup-dotnet@v1&lt;/em&gt; are community actions that will get your code and setup dotnet&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;run&lt;/em&gt; steps make the calls the the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/"&gt;dotnet cli&lt;/a&gt; to restore, build and test&lt;/li&gt;
&lt;li&gt;Be sure to run on ubuntu rather than Windows for better stability and more minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  View status of validation on pull requests
&lt;/h3&gt;

&lt;p&gt;When the action is in place you will see the validation status of all pull requests on the pull request tab. You will also see the checks inside the PR details and can prevent contributors merging pull requests with failing checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jfBgwhHB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/08/image-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jfBgwhHB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/08/image-5.png" alt="GitHub Action - Build and Run .NET Tests on new pull requests"&gt;&lt;/a&gt;Pull request tab status&lt;/p&gt;

&lt;p&gt;Check out these other &lt;a href="https://lukelowrey.com/tag/github-action/"&gt;GitHub Actions&lt;/a&gt; 👇&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lukelowrey.com/github-action-to-add-blog-posts-to-your-profile/"&gt;Add blog posts to your GitHub profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lukelowrey.com/use-github-actions-to-publish-nuget-packages/"&gt;Publish nuget packages to nuget.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>GitHub action to automatically add blog posts to your profile</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Tue, 25 Aug 2020 11:21:56 +0000</pubDate>
      <link>https://forem.com/lukencode/github-action-to-automatically-add-blog-posts-to-your-profile-4b1c</link>
      <guid>https://forem.com/lukencode/github-action-to-automatically-add-blog-posts-to-your-profile-4b1c</guid>
      <description>&lt;h2&gt;
  
  
  1. Create a GitHub Profile
&lt;/h2&gt;

&lt;p&gt;GitHub now allows you to &lt;a href="https://docs.github.com/en/github/setting-up-and-managing-your-github-profile/managing-your-profile-readme#about-your-profile-readme"&gt;add custom content to your profile page&lt;/a&gt; via a special repository with a readme.md file. All you need to do is create a repository named the same as your username and add a README.md file. For example, my profile repo is lukencode/lukencode.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Update README.md
&lt;/h2&gt;

&lt;p&gt;The README.md can have any valid markdown content you want. In addition to that you need a specially marked up readme-section that the action workflow below will use to inject content.&lt;/p&gt;

&lt;p&gt;This example uses &lt;code&gt;&amp;lt;!--START_SECTION:feed--&amp;gt;&lt;/code&gt; as the target. You can use multiple named sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 👋 Hi friends! I'm Luke.

I lead tech teams and build things on the web. Currently CTO at [Endpoint IQ](https://endpointiq.com.au/'). 


### 📙 Blog Posts
&amp;lt;!--START_SECTION:feed--&amp;gt;
&amp;lt;!--END_SECTION:feed--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Example &lt;a href="https://github.com/lukencode/lukencode/blob/master/README.md"&gt;readme.md&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create a GitHub Action workflow
&lt;/h2&gt;

&lt;p&gt;Add a &lt;a href="https://github.com/features/actions"&gt;GitHub Action&lt;/a&gt; to your repository using the "Actions" tab -&amp;gt; New workflow on github.com or br creating a workflow.yml file in the .github/workflows/ directory. The action will be setup to run on a schedule, look for new content in an RSS feed and update readme.md in the repository.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://github.com/JasonEtco/rss-to-readme"&gt;JasonEtco/rss-to-readme&lt;/a&gt; action to read the rss. All you need to provide is an RSS endpoint for it to look for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update readme with blog posts&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Once a day at 8 AM&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0 8 * * *&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JasonEtco/rss-to-readme@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;feed-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://lukelowrey.com/rss/&lt;/span&gt;
          &lt;span class="na"&gt;readme-section&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;feed&lt;/span&gt;
          &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="c1"&gt;# max number of items (default 5)&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;####&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;[{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}]({{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;*{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;contentSnippet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}*&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Example &lt;a href="https://github.com/lukencode/lukencode/blob/master/.github/workflows/update-readme-rss.yml"&gt;update-readme-rss.yml&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The YAML is pretty start forward. Rather than triggering on an action like pushing to branch it uses a simple schedule and cron expression to run daily. The rss-to-readme step reads RSS from the feed-url and injects it into the marked up section with the feed key in readme.md.&lt;/p&gt;

&lt;p&gt;The template property is optional, if you leave it out the posts will display in a simple list. You can customise the output with a &lt;code&gt;{{handlebars}}&lt;/code&gt; style template. Under the covers the action uses rss-parser to get the content. The &lt;a href="https://github.com/rbren/rss-parser#output"&gt;properties available to you when using a custom template can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;link&lt;/li&gt;
&lt;li&gt;pubDate&lt;/li&gt;
&lt;li&gt;content&lt;/li&gt;
&lt;li&gt;contentSnippet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 TIP: change the on trigger to "push to master" for easy testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🎆Success! Like clockwork the action runs, finds the latest items in RSS and updates readme.md. Check out my profile on GitHub &lt;a href="https://github.com/lukencode"&gt;github.com/lukencode&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://twitter.com/lukencode"&gt;Follow me on twitter @lukencode&lt;/a&gt;.
&lt;/h3&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>The simplest CSS variable dark mode theme</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sun, 26 Jul 2020 10:50:26 +0000</pubDate>
      <link>https://forem.com/lukencode/the-simplest-css-variable-dark-mode-theme-11bm</link>
      <guid>https://forem.com/lukencode/the-simplest-css-variable-dark-mode-theme-11bm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6epW3_hb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/07/dark-mode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6epW3_hb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/07/dark-mode.png" alt="The simplest CSS variable dark mode theme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have been working on a &lt;a href="https://lukelowrey.com/ghostsolo-a-free-and-open-source-ghost-theme/"&gt;custom Ghost theme&lt;/a&gt; to power my blog (you are looking at an early version right now!). One thing I wanted to have a crack at was a dark/light theme switcher. It turns out with modern CSS this is pretty straight forward.&lt;/p&gt;

&lt;p&gt;The approaches I considered were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS classes set on the &lt;/li&gt;
&lt;li&gt;Switch out style sheet entirely&lt;/li&gt;
&lt;li&gt;CSS variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went with CSS variables because my blog audience tends to be on the latest browser versions so I don't need to worry much about browser support (&lt;a href="https://caniuse.com/#feat=css-variables"&gt;not that it is too bad&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If a blog post is too much for you, check out dark mode in 4 tweets thread.&lt;br&gt;
&lt;/p&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V20e7WHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EeT0w-hVoAAD4jE.png" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NU-GNL14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1281104920465207296/bfXOxLFb_normal.png" alt="Luke Lowrey 🛴 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Luke Lowrey 🛴
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/lukencode"&gt;@lukencode&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      The easiest &lt;a href="https://twitter.com/hashtag/html"&gt;#html&lt;/a&gt; / &lt;a href="https://twitter.com/hashtag/css"&gt;#css&lt;/a&gt; / &lt;a href="https://twitter.com/hashtag/javascript"&gt;#javascript&lt;/a&gt; dark mode in 4 tweets.&lt;br&gt;&lt;br&gt;&lt;a href="https://twitter.com/hashtag/100DaysOfCode"&gt;#100DaysOfCode&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/coding"&gt;#coding&lt;/a&gt;&lt;br&gt;&lt;br&gt;1. 🌞Light (also default via :root selector) and 🌙Dark theme css variables 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      05:08 AM - 01 Aug 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1289427747396177921" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1289427747396177921" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      19
      &lt;a href="https://twitter.com/intent/like?tweet_id=1289427747396177921" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      13
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  Using CSS variables for themes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS variables&lt;/a&gt; are properties you can set in your stylesheets that can be used in later styles. They are similar to tools such as SASS but built directly into CSS. This makes them very well suited for theming.&lt;/p&gt;

&lt;p&gt;This is a simplified snippet from the default variables used in my theme &lt;a href="https://lukelowrey.com/ghostsolo"&gt;GhostSolo&lt;/a&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --background-color: #fff;
    --text-color: #121416d8;
    --link-color: #543fd7;
}

html[data-theme='light'] {
    --background-color: #fff;
    --text-color: #121416d8;
    --link-color: #543fd7;
}

html[data-theme='dark'] {
    --background-color: #212a2e;
    --text-color: #F7F8F8;
    --link-color: #828fff;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The :root selector is the default set of values. When &lt;em&gt;&lt;/em&gt; is set those values are overridden by the &lt;em&gt;html[data-theme='dark']&lt;/em&gt; values. It is really just a matter of applying the variables in your CSS to get the effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
    background: var(--background-color);
    color: var(--text-color);
}

a {
    color: var(--link-color);
}

a:hover {
    text-decoration: underline;
    filter: brightness(80%);
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In my theme I have variables for a couple of key style choices to allow distinct colour themes beyond dark/light.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --background-color: #fff;
    --alternate-background-color: #f7f7f9;
    --text-color: #121416d8;
    --text-color-light: #777676bb;
    --link-color: #543fd7;
    --masthead-color: #543fd7;
    --masthead-text: #fff;
    --button-color: #263238;
    --button-text: #fff;
    --bs-font-sans-serif: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --bs-font-serif: Georgia, serif;
    --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: the --bs prefixed variables are &lt;a href="https://v5.getbootstrap.com/docs/5.0/customize/css-variables/"&gt;CSS variables bootstrap 5 supports&lt;/a&gt;. The css variable support in Bootstrap is a bit limited and doesn't apply to things like &lt;em&gt;.btn-primary&lt;/em&gt; or &lt;em&gt;.bg-light&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JavaScript dark mode toggle
&lt;/h3&gt;

&lt;p&gt;With the CSS theming in place I needed a simple way to switch between "dark" and "light" modes.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var toggle = document.getElementById("theme-toggle");

var storedTheme = localStorage.getItem('theme') || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
if (storedTheme)
    document.documentElement.setAttribute('data-theme', storedTheme)

toggle.onclick = function() {
    var currentTheme = document.documentElement.getAttribute("data-theme");
    var targetTheme = "light";

    if (currentTheme === "light") {
        targetTheme = "dark";
    }

    document.documentElement.setAttribute('data-theme', targetTheme)
    localStorage.setItem('theme', targetTheme);
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This simple JavaScript snippet will set the theme based off the user's system preferred setting using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;media query for&lt;/a&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;"prefers-color-scheme: dark"&lt;/a&gt;.&lt;/em&gt; It will also store the value in local storage so it can be persisted across each page. The selected theme is set as a data-theme attribute on the html node.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other CSS theming tricks
&lt;/h3&gt;

&lt;p&gt;I couldn't quite get form inputs looking nice with a pure CSS variable approach. When using data attributes, you can always target them in your CSS the traditional way. This CSS gives the inputs a nice dark overlay against any background colour but didn't look right on the light theme.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[data-theme='dark'] .form-control {
    background-color: rgba(0, 0, 0, 0.6);
    border-color: rgba(0, 0, 0, 0.6);
    color: var(--text-color) !important;
}

[data-theme='dark'] .form-control:focus {
    color: var(--text-color) !important;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I needed to show/hide my sun moon icons in the dark mode toggle button. I went with a Bootstrap inspired display utility approach to change an element display based on the theme.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[data-theme='light'] .d-block-light,
[data-theme='dark'] .d-block-dark {
    display: block !important;
}


&amp;lt;button id="theme-toggle" class="btn btn-link btn-sm ml-2 small" type="button"&amp;gt;
 &amp;lt;span class="d-block-light d-none"&amp;gt;{{&amp;gt; "icons/moon"}}&amp;lt;/span&amp;gt;
 &amp;lt;span class="d-block-dark d-none"&amp;gt;{{&amp;gt; "icons/sun"}}&amp;lt;/span&amp;gt;
&amp;lt;/button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  See it in action
&lt;/h3&gt;

&lt;p&gt;Use the sun/moon icon in the nav bar of this page to see it in action. Or just watch the gif:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0e4Cqd7l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://lukelowrey.com/content/images/2020/07/theme-toggle.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0e4Cqd7l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://lukelowrey.com/content/images/2020/07/theme-toggle.gif" alt="The simplest CSS variable dark mode theme"&gt;&lt;/a&gt;Dark mode toggle&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@mwichary/dark-theme-in-a-day-3518dde2955a"&gt;Dark theme in a day - Marcin Wichary&lt;/a&gt; - some great advanced techniques with HSL colours and transitions&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/"&gt;A complete guide to dark mode - CSS tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Use GitHub actions to publish NuGet packages</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sat, 06 Jun 2020 12:47:14 +0000</pubDate>
      <link>https://forem.com/lukencode/use-github-actions-to-publish-nuget-packages-46c4</link>
      <guid>https://forem.com/lukencode/use-github-actions-to-publish-nuget-packages-46c4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WbsSrwwr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1590595906931-81f04f0ccebb%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WbsSrwwr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1590595906931-81f04f0ccebb%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Use GitHub actions to publish NuGet packages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://lukelowrey.com/use-github-actions-to-publish-nuget-packages/"&gt;lukelowrey.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the most frustrating aspects of maintaining an open source .NET Core library has been publishing the nuget packages on &lt;a href="https://www.nuget.org/"&gt;nuget.org&lt;/a&gt;. In the world of devops automation, manually creating and uploading packages felt so old-fashioned (don't get me started on Azure Devops).&lt;/p&gt;

&lt;p&gt;Here is how I automated pushing packages to nuget.org for &lt;a href="https://github.com/lukencode/FluentEmail/"&gt;FluentEmail&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Github Action that runs on push to the master branch&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://github.com/marketplace/actions/publish-nuget"&gt;Publish Nuget&lt;/a&gt; action to package and publish nuget packages&lt;/li&gt;
&lt;li&gt;Create a Nuget.org API key and set it as a GitHub secret&lt;/li&gt;
&lt;li&gt;Nuget packages are published on push to master!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create a GitHub Action
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/features/actions"&gt;GitHub actions&lt;/a&gt; makes it easy to automatically build test and deploy code hosted on GitHub. There are heaps of community built actions that cover the whole #devops spectrum.&lt;/p&gt;

&lt;p&gt;Creating a new workflow creates a yaml file in .github/workflows for the repository. Infrastructure as code! The best way to start is create a new workflow using the .NET Core starter action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OkhfsBfp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OkhfsBfp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-2.png" alt="Use GitHub actions to publish NuGet packages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This action sets up a simple dotnet environment. It is a great starting point for running validation builds and tests on new pull requests. For publishing packages running the action on push to the master branch works best.&lt;/p&gt;

&lt;p&gt;Here is the full yaml for the workflow I am using. It uses actions to setup .Net Core, restore packages, build and publish nuget.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Publish Packages

on:
  push:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.101
    - name: Install dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release --no-restore
    - name: Publish FluentEmail.Core
      uses: brandedoutcast/publish-nuget@v2.5.2
      with:
          PROJECT_FILE_PATH: src/FluentEmail.Core/FluentEmail.Core.csproj
          NUGET_KEY: ${{secrets.NUGET_API_KEY}}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Publish nuget package action
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/brandedoutcast"&gt;brandedoutcast&lt;/a&gt;has created a reusable GitHub Action to &lt;a href="https://github.com/marketplace/actions/publish-nuget"&gt;Publish Nuget&lt;/a&gt; packages. You can see it referenced in the &lt;em&gt;"uses:"&lt;/em&gt; section in the workflow above. This action does the heavy lifting for packaging and publishing nuget packages. whenever the project version is updated. The action (by default) looks for changes to the &lt;em&gt;1.0&lt;/em&gt; tag in your csproj file.&lt;/p&gt;

&lt;p&gt;There are a couple of variables you can set to get the process working just right. For a basic setup you only really need to set &lt;em&gt;PROJECT_FILE_PATH&lt;/em&gt; and &lt;em&gt;NUGET_KEY&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The full publish-nuget variable list allows you to tweak the versioning method and format as well as target package sources other than nuget.org.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Filepath of the project to be packaged, relative to root of repository
PROJECT_FILE_PATH: YourProject/YourProject.csproj

# NuGet package id, used for version detection &amp;amp; defaults to project name
# PACKAGE_NAME: YourProject

# API key to authenticate with NuGet server
NUGET_KEY: ${{secrets.NUGET_API_KEY}}

# NuGet server uri hosting the packages, defaults to https://api.nuget.org
# NUGET_SOURCE: https://api.nuget.org

# Filepath with version info, relative to root of repository &amp;amp; defaults to PROJECT_FILE_PATH
# VERSION_FILE_PATH: Directory.Build.props

# Regex pattern to extract version info in a capturing group
# VERSION_REGEX: &amp;lt;Version&amp;gt;(.*)&amp;lt;\/Version&amp;gt;

# Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH &amp;amp; VERSION_REGEX
# VERSION_STATIC: 1.0.0

# Flag to toggle git tagging, enabled by default
# TAG_COMMIT: true

# Format of the git tag, [*] gets replaced with actual version
# TAG_FORMAT: v*

# Flag to toggle pushing symbols along with nuget package to the server, disabled by default
# INCLUDE_SYMBOLS: false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Nuget.org API key as GitHub Action Secret
&lt;/h3&gt;

&lt;p&gt;The workflow above uses the nuget API to automatically push the package. You need to create an API key for your profile on nuget.org.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IruVRAHD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IruVRAHD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-3.png" alt="Use GitHub actions to publish NuGet packages"&gt;&lt;/a&gt;Create API Keys from your nuget.org profile&lt;/p&gt;

&lt;p&gt;You can then enter the key as a&lt;a href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets"&gt;secret in your GitHub repository&lt;/a&gt;. The key for the secret must be &lt;em&gt;NUGET_API_KEY&lt;/em&gt;. The API key will then be available to the publish package action workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9V7u-GmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9V7u-GmK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-4.png" alt="Use GitHub actions to publish NuGet packages"&gt;&lt;/a&gt;Setting a GitHub secret&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;Now when code is push to the master branch (and the project version changes) the publish packages action run and publishes the nuget package!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ymJI7Ttx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ymJI7Ttx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/06/image-5.png" alt="Use GitHub actions to publish NuGet packages"&gt;&lt;/a&gt;Success!&lt;/p&gt;

&lt;p&gt;A more advanced branching and action setup would allow you to publish pre-release versions from a beta branch and full release versions from master.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful, retweet or like the thread on twitter.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How I use GitHub actions to automatically publish &lt;a href="https://twitter.com/nuget?ref_src=twsrc%5Etfw"&gt;@nuget&lt;/a&gt; packages for a .NET core library&lt;a href="https://t.co/xOD5IqklwC"&gt;https://t.co/xOD5IqklwC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Luke Lowrey (&lt;a class="comment-mentioned-user" href="https://dev.to/lukencode"&gt;@lukencode&lt;/a&gt;
) &lt;a href="https://twitter.com/lukencode/status/1270101881885388801?ref_src=twsrc%5Etfw"&gt;June 8, 2020&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dotnet</category>
      <category>devops</category>
      <category>opensource</category>
      <category>github</category>
    </item>
    <item>
      <title>Software demo tips for developers</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Wed, 03 Jun 2020 10:42:34 +0000</pubDate>
      <link>https://forem.com/lukencode/software-demo-tips-for-developers-53pb</link>
      <guid>https://forem.com/lukencode/software-demo-tips-for-developers-53pb</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bJclY04K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1528238646472-f2366160b6c1%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bJclY04K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1528238646472-f2366160b6c1%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Presentation tips for a better product demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Giving product demos can be stressful and overwhelming. You need to focus on the software, your presentation and keep your potential client's needs at the front of your mind. These tips help me stay focused and deliver a confident and professional demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be prepared
&lt;/h3&gt;

&lt;p&gt;The most valuable step you can take to improve your product demos is preparation. I have written a &lt;a href="https://lukelowrey.com/product-demo-checklist/"&gt;demo preparation guide&lt;/a&gt; that outlines the research, planning and practice techniques that have helped me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't go alone
&lt;/h3&gt;

&lt;p&gt;It is very difficult to maintain your focus and enthusiasm for any presentation longer than half an hour. In my experience it is nearly always best to bring some backup from your team. An extra person can concentrate closely on questions, get a better read on how the demo goes, and give you a chance to catch your breath every now and then.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only bring people that will be able to contribute. I have found that using two presenters works well. One person takes control of the software and the other handles the introductions and company overview, and is the first call for questions.&lt;/li&gt;
&lt;li&gt;Each person included in the presentation should be given some space to contribute. It could be as simple as splitting the introduction and demo between you or handing off a standard technical explanation. There is nothing more awkward then sitting through an hour meeting and never having a chance to contribute.&lt;/li&gt;
&lt;li&gt;Try to include people to cover both business and technical expertise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Be friendly but stick to business
&lt;/h3&gt;

&lt;p&gt;It is very useful to break the ice with your audience before you kick on into the demo. My advice is to keep it simple and brief; you don't want to appear unfriendly but you also need to ensure you have time to show your software in the best light.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mostly stick to your plan
&lt;/h3&gt;

&lt;p&gt;Hopefully you have come prepared with a demo of some high-level user stories and a demo script. A demo where no one says anything is not a good sign but being peppered with questions and never getting into a groove is also an issue. You need to balance answering questions and adapting to the audience's preference with telling the story of your product the way you have planned.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let the audience know up front what you are going to show them. It gives them a chance to point out what is important to them and will also give them an idea of what will be shown so they don't need to jump ahead with questions.&lt;/li&gt;
&lt;li&gt;Always describe the software in business terms rather than technical functions. An easy way to remind yourself to do this is starting explanations with &lt;em&gt;"As a [end user type] I can..."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Stop and ask for questions at convenient spots in your presentation. For example, once you have completed some workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Control your tempo
&lt;/h3&gt;

&lt;p&gt;A problem that many developers have (including me) when presenting software is moving too fast. When you know the product well and have a time limit it is easy to blow through every function of the software at a million miles an hour.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't try to show everything. You should spend the most time on your product's strongest features; no need to open every page.&lt;/li&gt;
&lt;li&gt;Deliberately pause at key discussion points. I normally mark these in my demo script. You want to leave on the screen interesting, strong features of the product as you pause and make your point. &lt;/li&gt;
&lt;li&gt;When you pause, take your hands completely off the keyboard and mouse. This is your cue to look at the camera or audience and speak using hand gestures.&lt;/li&gt;
&lt;li&gt;Go easy on the mouse. Try not to dart your mouse all over the screen. An annoying habit I need to be aware of is flicking my mouse all around the button I am talking about. Slow your mouse movement down and take your hand off completely when you talk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tips for presenting remotely over video
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--74a6uepJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/demo-screen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--74a6uepJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/demo-screen.png" alt="Presentation tips for a better product demo"&gt;&lt;/a&gt;Example remote demo screen setup&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prepare your software and desktop. Get your product setup in its own window or tabs. Close down your chat app, email and anything else that could pop something embarrassing onto the screen.&lt;/li&gt;
&lt;li&gt;Share as little as possible. If you are demoing a web application, stick to the web browser. As above you want to limit the chance of oversharing.&lt;/li&gt;
&lt;li&gt;Keep your script on screen. If you have a big monitor and can limit your screen sharing to a single window it is very useful to keep your notes visible to the side of the main demo.&lt;/li&gt;
&lt;li&gt;Get the right window size and zoom level. With a big widescreen monitor sharing Fullscreen is not always the best option. Play around with window size and zoom level before the demo to find a good fit.&lt;/li&gt;
&lt;li&gt;Open by stating &lt;em&gt;"We are looking at the homepage..."&lt;/em&gt; this should prompt someone to interrupt your screenshare is not working and is more natural than &lt;em&gt;"can everyone see this?"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Make sure you look at the camera. I have a habit of watching my own video feed when presenting. Because of this I tend to move my video to just below my webcam to ensure I am always facing the camera.&lt;/li&gt;
&lt;li&gt;Try a custom virtual background. For a simple, branded background try &lt;a href="https://www.actionablebackground.com/"&gt;actionablebackground.com&lt;/a&gt;. If you know the audience well or have a nice office, I don't think this is necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tips for presenting in person
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Try to position yourself at the front of a meeting room (in front of the big screen) facing toward the audience.&lt;/li&gt;
&lt;li&gt;Pausing is your cue to look around and make eye contact. It is also a good way to encourage questions from your audience.&lt;/li&gt;
&lt;li&gt;Always assume the presentation equipment will be missing the cables and adaptors you need. Bring your own.&lt;/li&gt;
&lt;li&gt;As above, have a backup internet source ready to go.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>product</category>
      <category>business</category>
      <category>career</category>
    </item>
    <item>
      <title>How to prepare the perfect product demo</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Wed, 27 May 2020 21:36:08 +0000</pubDate>
      <link>https://forem.com/lukencode/how-to-prepare-the-perfect-product-demo-l0i</link>
      <guid>https://forem.com/lukencode/how-to-prepare-the-perfect-product-demo-l0i</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://lukelowrey.com/product-demo-checklist/"&gt;lukelowrey.com/product-demo-checklist&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hY1O9r9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517245386807-bb43f82c33c4%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hY1O9r9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1517245386807-bb43f82c33c4%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="How to prepare the perfect product demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don't have to be a sales person to deliver a great software demonstration. I have found the key to improving the quality and consistency of my demos is research, planning and practice.  This demo preparation checklist helped me transform from rambling techie to educated and coherent domain expert.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know your audience
&lt;/h3&gt;

&lt;p&gt;I view software demonstrations similarly to job interviews; there is an expectation that you have researched the company you are presenting to and the people who will be attending. You need solid grasp of their core business and where your product fits it. It is also useful to know in advance the roles of the attendees and a little about their background. Hopefully you can save yourself the embarrassment of over explaining a point to an expert in the field. Public company websites and LinkedIn profiles are a good source of information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask Questions in Advance
&lt;/h3&gt;

&lt;p&gt;Send an email before the meeting to ask questions in advance. Use this information to tailor your demo. These types of questions are good options to discover the key issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are the roles and background of those attending?&lt;/li&gt;
&lt;li&gt;What are your top 3 pain points with your current process?&lt;/li&gt;
&lt;li&gt;Are you currently using any other tools in the process?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you learn here researching your audience should then be used to fine tune the demo story and script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Craft user stories
&lt;/h3&gt;

&lt;p&gt;Before you start rattling off the product features to show in your run through, take a step back and imagine the software through a user's eyes. Those in software would be familiar with a &lt;a href="https://www.atlassian.com/agile/project-management/user-stories"&gt;user story&lt;/a&gt;. User stories help frame software requirements from the point of view of the end user. They are useful when preparing your product demo for the same reason.&lt;/p&gt;

&lt;p&gt;A typically agile user story is formatted like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a [ROLE], I want to &lt;a href="https://dev.toso%20that%20%5BBENEFIT%5D"&gt;ACTION&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The format works for planning software demos as well. The key difference is while in an agile framework the user story is the smallest unit of work in a software demo it tends to have a larger scope. Depending on how your product works it may be useful to design one story for each type of user interacting with the system.&lt;/p&gt;

&lt;p&gt;Here are some examples I have used recently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a &lt;strong&gt;potential student&lt;/strong&gt; , I want to &lt;strong&gt;apply for a research project&lt;/strong&gt; so that &lt;strong&gt;I can start research at the university&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;As a &lt;strong&gt;supervisor&lt;/strong&gt; , I want to &lt;strong&gt;review and progress my student projects&lt;/strong&gt; so that &lt;strong&gt;my students don't miss any milestones&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;As a &lt;strong&gt;research officer&lt;/strong&gt; , I want to &lt;strong&gt;ensure student projects are meeting milestones&lt;/strong&gt; so that ....&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should have a couple of key user stories for your product that you can mix and match depending on what you know about your audience. The more familiar your demo feels the easier it is for the potential client to imagine themselves using your software in production.&lt;/p&gt;

&lt;p&gt;I tend to pick two or three user stories to base my demos around. These stories are separate but should fit together to form the overall picture of your software. Often "data collection, "data workflow" and "reporting and analytics" flow well together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write a demo script
&lt;/h3&gt;

&lt;p&gt;After you have a clear idea of the stories you want to tell draft a script or run sheet around them. The script needs to act as a high-level guide, not detail step by step actions to complete. When presenting as you need to be flexible with audience questions and preferences but try to act within your broad script.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't know the product well enough to work off high level notes it should be someone else running the show.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Write a section for each user story you are going to cover. You should try to simulate the process that the user would take in the product.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't hold back on the good stuff.&lt;/strong&gt; Including some stronger features/screens/reports early on in your run through. You want people to be engaged early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't try to include everything.&lt;/strong&gt; You need to spend time showing your best and most relevant features so don't feel like you need to "feature dump" every button and every screen in your plan. This is a trap that developers often fall into.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include "actions" in your script&lt;/strong&gt;. You need to show the process of your software, not just what every screen looks like. Seeing data change state and move around your system is key to making it "feel real" for the audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid complex data entry.&lt;/strong&gt; You should "do things" in the demo as the point above suggests but no one will enjoy watching you fumble typing for 20 minutes only to fail form validation. If it is going to take too long, have an example ready to go.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My preference is for each user story to outline each function you intend to show. Within each function I add details on how it should be delivered. I tend to colour code the details along these lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yellow is a key talking point. This is normally a specific business benefit that I want to highlight. Yellow is my cue to slow down while presenting and make sure I get my point across.&lt;/li&gt;
&lt;li&gt;Green is an action I need to take in the system. I normally highlight in green things like submitting forms, changing status or anything that I need to actually do (as opposed to just show) in the system to proceed.&lt;/li&gt;
&lt;li&gt;Purple is a detour or action outside of the main system. I often end up cross selling related products during our demos. Purple is a reminder to me to open up that other product/tab/example outside of the normal demo flow.&lt;/li&gt;
&lt;li&gt;Red is something I need to sort out before the demo. Things I highlight in red include missing data, questions to clarify about the client or (hopefully not) errors in the product. Red needs to be sorted well before demo time.&lt;/li&gt;
&lt;li&gt;Uncoloured items are general reminders on the story flow or minor talking points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t5rjtZBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/image-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t5rjtZBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/image-2.png" alt="How to prepare the perfect product demo"&gt;&lt;/a&gt;Example run sheet&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need demo slides?
&lt;/h3&gt;

&lt;p&gt;Slide decks are not my strong point and I am a firm believer in the "show don't tell" method. However, having some simple high-level slides ready can help structure your introductions and drive home the key benefits you are pitching.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introductions. Include the names and roles of the presenters from your company. This is a good reminder to actually do intros as it is something that can be missed and is awkward to pick up later on.&lt;/li&gt;
&lt;li&gt;Company overview. Your company's key information and mission.&lt;/li&gt;
&lt;li&gt;Product overview and key benefits. Include the name and logo of the product you are showing. Try to distil the top 1-3 benefits into this slide and refer to them in your script.&lt;/li&gt;
&lt;li&gt;[Optional] Description of the user stories you want to show. Sometimes a simple process diagram can be useful here.&lt;/li&gt;
&lt;li&gt;Conclusion. A slide to show at the end of the demo. Summarise the next steps and key contact details. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Demo Data
&lt;/h3&gt;

&lt;p&gt;In the ideal world you configure your demo data to match the audience as closely as possible. For important demonstrations I have used publicly available client polices and documents to configure our demonstration site as closely as possible to how I envision the client would operate in production.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The bottom line is your demo data has to be good. "Test project 1" and "This is a comment" is not good enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your demo data should be good enough that you not find yourself saying "this is just demo data".&lt;/p&gt;

&lt;p&gt;You should avoid "smoke and mirrors" in your data and functionality. If you don't have good data for a report showing a screenshot is fine as long as you aren't trying to pass it off as the real deal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know your weaknesses
&lt;/h3&gt;

&lt;p&gt;Put yourself into the shoes of your future client and ask "what is missing from this product?". You will know the software better than anyone so it shouldn't be too difficult to come up with some potential gaps. They key is to have a decent (and honest) answer to what may be lacking in your product.&lt;/p&gt;

&lt;p&gt;Here are some ways to frame missing functionality when asked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you have accounted for it on your product road map. This should be something you can provide to a client if requested.&lt;/li&gt;
&lt;li&gt;Have an explanation for an alternative method or workaround in the system.&lt;/li&gt;
&lt;li&gt;Accept the question (if it comes) as important feedback and make an effort to acknowledge it in any follow up communications.&lt;/li&gt;
&lt;li&gt;Be confident about what is out of scope for your product&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PRACTICE
&lt;/h3&gt;

&lt;p&gt;Like all things in life, the more you practice the better you get. It can be tricky to find an audience for a "real" dry run of a software demo. You can fall into the trap of not selling to them because they are not a real client.&lt;/p&gt;

&lt;p&gt;I normally go with two types of practice. One is a "simulated" run through of the demo with a team member - ideally someone who is involved in the meeting. I have my script up on screen and we step through making tweaks where necessary.&lt;/p&gt;

&lt;p&gt;The second practice is a "live" run of the demo. This can still work well with team members, especially those not familiar with the product. Often the best candidates (and the hardest to get hold of) are in leadership and executive positions as they best emulate the decision makers that you need to convince.&lt;/p&gt;

&lt;p&gt;Another option is to do regular internal team "functionality presentations". These work best if kept short - one user story from your plan. Internal demonstrations have the added benefit of knowledge sharing - both of the product and how to show it off effectively.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am writing a follow up post on demo presentation tips. Follow me &lt;a href="https://twitter.com/lukencode"&gt;@lukencode&lt;/a&gt; and I will let you know when it is ready. I want to hear any other tips that have helped you nail your demos.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>product</category>
      <category>business</category>
      <category>demo</category>
    </item>
    <item>
      <title>Text based stand-ups</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sun, 17 May 2020 00:35:25 +0000</pubDate>
      <link>https://forem.com/lukencode/text-based-stand-ups-4ggl</link>
      <guid>https://forem.com/lukencode/text-based-stand-ups-4ggl</guid>
      <description>&lt;p&gt;Like a lot of software companies Endpoint IQ runs a daily team stand-up. It keeps the team promotes accountability and transparency. But you don't have to go far to find a developer who loathes the morning stand-up ritual. For some it can cause anxiety while most fail to see the benefit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lw40jAJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/standup-cancelled.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lw40jAJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/standup-cancelled.jpg" alt=""&gt;&lt;/a&gt;Stand-up cancelled&lt;/p&gt;

&lt;p&gt;We used to do the traditional stand up in a circle and talk method but I have found the most effective way to keep everyone informed is switching to simple, text-based updates.&lt;/p&gt;

&lt;p&gt;Every morning the team posts an update in our #standup chat channel with something along the lines of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What I did yesterday?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What am I doing today?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Are there any blockers?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The format itself isn't important as long as we capture where we are up to, what we plan to do and if anything is stopping us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o8TRvp-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/image-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o8TRvp-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lukelowrey.com/content/images/2020/05/image-1.png" alt=""&gt;&lt;/a&gt;Example text stand up update&lt;/p&gt;

&lt;p&gt;Writing the update as opposed to just saying it during a meeting is quick and I find it has some real advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It forces me to think before we I start work and make sure my plans are aligned with what the team is trying to do.&lt;/li&gt;
&lt;li&gt;Keeps me focused during the day. Having a record of what I planned to achieve and knowing it will be revisited the next day keeps met accountable.&lt;/li&gt;
&lt;li&gt;Stand-ups are not dominated by loudest voices.&lt;/li&gt;
&lt;li&gt;Allows us to be flexible times. We have people start from 7am - 9:30am as long as you provide your update you never really "miss" a stand-up
&amp;lt;!--kg-card-end: markdown--&amp;gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This type of short, recurring daily update lends itself well to collaboration tools like Slack and Microsoft Teams. Endpoint IQ uses a dedicated Teams channel #standups to post the updates. Any questions or updates from the team takes place in the replies to the original post. This keeps the discussions easy to follow over the course of the week. I also encourage people to posts screenshots, videos or gifs of their work.&lt;/p&gt;

&lt;p&gt;We tried out a couple of "stand-up bots" which attempt to automate the process by sending reminders and consolidating responses. The bots generally got in the way more than they helped.&lt;/p&gt;

</description>
      <category>management</category>
      <category>code</category>
    </item>
    <item>
      <title>Automatically remove unused css from Bootstrap or other frameworks</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sun, 29 Jul 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/lukencode/automatically-remove-unused-css-from-bootstrap-or-other-frameworks-331e</link>
      <guid>https://forem.com/lukencode/automatically-remove-unused-css-from-bootstrap-or-other-frameworks-331e</guid>
      <description>&lt;p&gt;I love bootstrap (and other css frameworks). For a developer like me who often works on web projects without any design input it is a real lifesaver.The problem is since bootstrap is a kitchen sink type framework (although you can pick and choose via sass) the css file size can get out of hand quickly. Bootstrap.min.css weighs in at 138 KB. If you add a theme and custom css it can easily bump you over 200 kb.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.purgecss.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kvzbo8e0n04lkgdzngq.png" alt="PurgeCSS" width="192" height="75"&gt;&lt;/a&gt;&lt;a href="https://www.purgecss.com/" rel="noopener noreferrer"&gt;PurgeCSS&lt;/a&gt; is a node.js package that can detect and remove unused css selectors. It will analyze your view pages - be they HTML or a templating engine and build a list of the css selectors in use. PurgeCSS will then take your stylesheets and remove any selectors that are not present in your views.&lt;/p&gt;

&lt;p&gt;I setup PurgeCSS using the &lt;a href="https://www.purgecss.com/with-webpack" rel="noopener noreferrer"&gt;webpack plugin&lt;/a&gt; which works in conjunction with the extract-text-webpack-plugin. My webpack config is based off the &lt;a href="https://lukencode.com/2018/04/14/simple-webpack-config-to-build-javascript-sass-and-css-using-npm-and-aspnet-core/" rel="noopener noreferrer"&gt;Simple webpack config&lt;/a&gt; I have written about previously.&lt;/p&gt;

&lt;p&gt;The webpack config was already setup to process sass and combine css into a single file. I added the PurgecssPlugin which will run at after the initial css processing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i -D purgecss-webpack-plugin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i -D extract-text-webpack-plugin&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const path = require('path')
const glob = require('glob')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const PurgecssPlugin = require('purgecss-webpack-plugin')

const outputDir = './wwwroot/dist'
const entry = './wwwroot/js/app.js'
const cssOutput = 'site.css'

module.exports = (env) =&amp;gt; {    
    return [{
        entry: entry,
        output: {
            path: path.join(__dirname, outputDir),
            filename: '[name].js',
            publicPath: '/dist/'
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: 'babel-loader'
                },
                {
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        use: ['css-loader'],
                        fallback: 'style-loader'
                    })
                },
                {
                    test: /\.scss$/,
                    use: ExtractTextPlugin.extract({
                        use: ['css-loader', 'sass-loader'],
                        fallback: 'style-loader'
                    })
                }
            ]
        },
        plugins: [
            new ExtractTextPlugin(cssOutput),
            new PurgecssPlugin({
                paths: glob.sync('./Views/**/*.cshtml', { nodir: true }),
                whitelistPatterns: [/selectize-.*/]
            })
        ]
    }]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin accepts an array of paths to view files to search for css selectors in use. The glob package accepts a search pattern and generates a list of files. I am looking for .cshtml view files in my .net web app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paths: glob.sync('./Views/**/*.cshtml', { nodir: true })

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whitelistPatterns parameter allows you to exclude selectors from the purge that may not have been present in the paths. I found that the &lt;a href="https://selectize.github.io/selectize.js/" rel="noopener noreferrer"&gt;selectize plugin&lt;/a&gt; I am using has css classes that are added dynamically and were being removed so I added a pattern to match the prefix of its css classes. I could alternatively include the .js file for this plugin with the paths parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whitelistPatterns: [/selectize-.*/]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running PurgeCSS on one of my (admittedly heavy) bootstrap based web sites reduced the css file size from 159KB to 60KB with essentially no effort on my end!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp20xfrefb07fu6dr8sjm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp20xfrefb07fu6dr8sjm.png" alt="PurgeCSS" width="795" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webpack</category>
      <category>purgecss</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Sending email in .NET Core with FluentEmail</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Sun, 01 Jul 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/lukencode/sending-email-in-net-core-with-fluentemail-4k4m</link>
      <guid>https://forem.com/lukencode/sending-email-in-net-core-with-fluentemail-4k4m</guid>
      <description>&lt;p&gt;It has been a long time since I posted about or contributed to &lt;a href="https://github.com/lukencode/FluentEmail" rel="noopener noreferrer"&gt;FluentEmail&lt;/a&gt;, the open source .net email package I created way back in 2010. During that time (largely thanks to the awesome work by &lt;a href="https://benjii.me" rel="noopener noreferrer"&gt;Ben Cull&lt;/a&gt;) fluent email has been updated to support .net core.&lt;/p&gt;

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

&lt;p&gt;I recently got my hands dirty coding again to improve the .net core support - specifically around dependency injection. Here is a walkthrough for using FluentEmail in an ASP.NET core web application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required Packages
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package FluentEmail.Core&lt;/code&gt;&lt;br&gt;&lt;br&gt;
The core FluentEmail classes and interfaces&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package FluentEmail.Razor&lt;/code&gt;&lt;br&gt;&lt;br&gt;
A Razor implementation of &lt;code&gt;ITemplateRenderer&lt;/code&gt; this uses RazorLight to render Razor templates as the email body.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PM&amp;gt; Install-Package FluentEmail.Smtp&lt;/code&gt;&lt;br&gt;&lt;br&gt;
An Smtp implementation of &lt;code&gt;ISender&lt;/code&gt; which uses System.Net.Mail under the hood.&lt;/p&gt;

&lt;p&gt;There are a couple of other packages available to use in place of smtp or razor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lukencode/FluentEmail/blob/master/src/Senders/FluentEmail.Mailgun" rel="noopener noreferrer"&gt;FluentEmail.Mailgun&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lukencode/FluentEmail/blob/master/src/Senders/FluentEmail.Sendgrid" rel="noopener noreferrer"&gt;FluentEmail.SendGrid&lt;/a&gt;&amp;lt;!-- - &lt;a href="https://github.com/lukencode/FluentEmail/blob/master/src/Renderers/FluentEmail.DotLiquid" rel="noopener noreferrer"&gt;FluentEmail.DotLiquid&lt;/a&gt; --&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Dependency Injection
&lt;/h3&gt;

&lt;p&gt;In previous versions of FluentEmail emails were composed using a static helper method. This is not now recommended due to the difficulty in testing and maintainability. The FluentEmail libraries now come with extensions methods for Microsoft Dependency Injection. The code below should be added to the ConfigureServices method in Startup.cs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void ConfigureServices(IServiceCollection services)
{
    services
        .AddFluentEmail("defaultsender@test.test")
        .AddRazorRenderer()
        .AddSmtpSender("localhost", 25);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AddFluentEmail("defaultsender@test.test")&lt;/code&gt; extension method accepts a default sender address and has extension methods to set the IRenderer, in this case the RazorLight renderer using &lt;code&gt;.AddRazorRenderer()&lt;/code&gt; and the SMTP sender using &lt;code&gt;.AddSmtpSender("localhost", 25)&lt;/code&gt;. The MailGun and SendGrid packages also have extension method helpers.&lt;/p&gt;

&lt;p&gt;These methods will make two interfaces available in your application for sending email &lt;code&gt;IFluentEmail&lt;/code&gt; for a single email and &lt;code&gt;IFluentEmailFactory&lt;/code&gt; for multiple emails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending an email
&lt;/h3&gt;

&lt;p&gt;Below is the most basic example of sending an email. This code will take the IFluentEmail configured above and send using the SmtpSender. The email will be sent from “&lt;a href="mailto:defaultsender@test.test"&gt;defaultsender@test.test&lt;/a&gt;” as configured on startup (this can be overridden with the &lt;code&gt;email.SetFrom()&lt;/code&gt; method)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IActionResult&amp;gt; SendEmail([FromServices]IFluentEmail email)
{
    await email
        .To("test@test.test")
        .Subject("test email subject")
        .Body("This is the email body")
        .SendAsync();

    return View();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a bunch of other useful methods on IFluentEmail to do things like add CC or BCC recipients, add attachments, set priority or set the reply to address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render email using a Razor template
&lt;/h3&gt;

&lt;p&gt;Sending basic plain text emails is fine - but most of the time you are going to want to mix in some rich data. FluentEmail allows you to set the body of the email as a template rather than a plain string by using &lt;code&gt;.UsingTemplate&amp;lt;T&amp;gt;(string template, T model)&lt;/code&gt; instead of &lt;code&gt;.Body()&lt;/code&gt;. This will be taken combined with an object as a model and be passed to the IRenderer that has been set - in our case FluentEmail.Razor to process. Here is a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IActionResult&amp;gt; SendEmail([FromServices]IFluentEmail email)
{
    var model = new
    {
        Name = "test name"
    };

    var template = "hi @Model.Name this is a razor template @(5 + 5)!";

    await email
        .To("test@test.test")
        .Subject("test email subject")
        .UsingTemplate(template, model)
        .SendAsync();

    return View();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The body of this email will render as “Hi test name this is a razor template 10!”. The Razor renderer will allow you to write just about any valid Razor code. The model passed to the renderer can be any object type - I often find it convenient to use an anonymous object as in the example above.&lt;/p&gt;

&lt;p&gt;FluentEmail has a couple of helper methods to make using templates a bit cleaner. I personally like the embedded resource option because it makes it easy to share templates across projects.&lt;/p&gt;

&lt;p&gt;Render template from file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.UsingTemplateFromFile($"{Directory.GetCurrentDirectory()}/Mytemplate.cshtml", model)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Render template from embedded resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.UsingTemplateFromEmbedded("Example.Project.Namespace.template-name.cshtml", 
    model, 
    TypeFromYourEmbeddedAssembly.GetType().GetTypeInfo().Assembly)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note for .NET Core 2 users:&lt;/strong&gt; You will need to add the following line to the project containing any embedded razor views.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;MvcRazorExcludeRefAssembliesFromPublish&amp;gt;false&amp;lt;/MvcRazorExcludeRefAssembliesFromPublish&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Send multiple emails in the same scope
&lt;/h3&gt;

&lt;p&gt;If you need to send multiple emails from the same class or action you will want to use IFluentEmailFactory rather than reusing an instance of IEmail. IFluentEmailFactory has a single Create() method which will give you a new instance of IFluentEmail to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IActionResult&amp;gt; SendMultiple([FromServices] IFluentEmailFactory emailFactory)
{
    await emailFactory.Create()
        .To("test1@test.test")
        .Subject("test email subject")
        .UsingTemplate("hi @Model.Name this is the first email @(5 + 5)!", new { Name = "test name" })
        .SendAsync();

    await emailFactory.Create()
        .To("test1@test.test")
        .Subject("test email subject")
        .UsingTemplate("hi @Model.Name this is the second email @(5 + 5)!", new { Name = "test name 2" })
        .SendAsync();

    return View();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extending
&lt;/h3&gt;

&lt;p&gt;As I have mentioned a couple of times FluentEmail can be easily extended by implementing ISender or ITemplateRenderer. ISender dictates how the email will actually sent. ITemplate renderer accepts a template string and a model and returns the rendered result.&lt;/p&gt;

&lt;p&gt;For more information check out the &lt;strong&gt;&lt;a href="https://github.com/lukencode/FluentEmail" rel="noopener noreferrer"&gt;FluentEmail Github repository&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Caching in ASP.NET Core with LazyCache</title>
      <dc:creator>Luke Lowrey</dc:creator>
      <pubDate>Wed, 18 Apr 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/lukencode/caching-in-aspnet-core-with-lazycache-1cp8</link>
      <guid>https://forem.com/lukencode/caching-in-aspnet-core-with-lazycache-1cp8</guid>
      <description>&lt;p&gt;Caching is a great way to get improve the performance of your application - either enabling high load scenarios or papering over some bad code to make life bearable. I have always favoured a simple “GetOrCreate” style caching API so when I found the open source &lt;a href="https://github.com/alastairtree/LazyCache"&gt;LazyCache&lt;/a&gt; by &lt;a href="https://alastaircrabtree.com/"&gt;Alistair Crabtree&lt;/a&gt; I was keen to check it out.&lt;/p&gt;

&lt;p&gt;LazyCache describes itself as “An easy to use thread safe generics based in memory caching service with a simple developer friendly API for C#” which I would pretty much have to agree with.&lt;/p&gt;

&lt;p&gt;The ASP.NET Core version is currently in beta (though seems to be pretty solid) and can be installed via nuget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package LazyCache.AspNetCore -Version 2.0.0-beta03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed it can be easily made available when configuring services in startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.AddLazyCache();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will inject IAppCache throughout your application.&lt;/p&gt;

&lt;p&gt;Here is a simplified example of using LazyCache from the homepage my &lt;a href="https://austechjobs.com.au"&gt;Australian tech job board - Austechjobs&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;IAppCache is the Lazy Cache service being injected into my class. It provides a &lt;em&gt;GetOrAddAsync&lt;/em&gt; method that accepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A cache key which in my example is the string “HomeModel”.&lt;/li&gt;
&lt;li&gt;A factory to retrieve the data to be cached in the form of a function Func&amp;lt;ICacheEntry, Task&amp;gt; addItemFactory. The code in my example returns a model after making a database call (the bit I wanted to cache).&lt;/li&gt;
&lt;li&gt;Options for cache length. I am using a simple sliding timespan of 12 hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I find the simplicity of this caching setup very pleasing. It’s implementation uses Lazy&amp;lt;T&amp;gt; to ensure the factory method is only executed once. Under the hood by default LazyCache will use IMemoryCache in Microsoft.Extensions.Caching.Memory. This is implementation will only cache data on the machine it is running on. If I was running a more intensive service I would want to extend &lt;a href="https://github.com/alastairtree/LazyCache/wiki/2.0-Extending-LazyCache-with-Redis,-Cassandra-etc"&gt;LazyCache&lt;/a&gt; and implement something like redis for distributed caching. The good news is because I am using IAppCache throughout my application I can change the underlying caching provider with relative ease.&lt;/p&gt;

</description>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
