<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://optimalcoder.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://optimalcoder.net/" rel="alternate" type="text/html" /><updated>2026-06-30T16:25:13+00:00</updated><id>https://optimalcoder.net/feed.xml</id><title type="html">Optimal Coder</title><subtitle>Building software systems with clean architecture and modern engineering practices.</subtitle><author><name>Mirnes Mrkaljevic</name></author><entry><title type="html">Vibe Coding Traps and Delusions</title><link href="https://optimalcoder.net/vibe-coding-traps-and-delusions/" rel="alternate" type="text/html" title="Vibe Coding Traps and Delusions" /><published>2026-06-19T00:00:00+00:00</published><updated>2026-06-19T00:00:00+00:00</updated><id>https://optimalcoder.net/vibe-coding-traps-and-delusions</id><content type="html" xml:base="https://optimalcoder.net/vibe-coding-traps-and-delusions/"><![CDATA[<p>For a long time, I am thinking about writing something on this topic, but since I was not sure about my thoughts and how to formulate them, I kept silent. The last few months things are getting clearer in my mind, so here I am to express my opinion on current AI development and its influence on software engineering industry and everyday tasks of one developer.
I am writing this post in hope that my experience of more than one year actively using LLMs and agents in coding could help someone who is struggling with the entire idea behind.</p>

<h2 id="productivity-fallacy">Productivity Fallacy</h2>

<p>We all heard big announcements on how AI is boosting productivity of developers by 20% - 30% and once when something is in public space as the mainstream idea, it is not so convenient to speak against it. Main industry drivers are making fortune based on that assumptions and it is not popular to be against the idea, or, like in my case, to be less for it as the main narration suggests. I would say that, even, it is not so hard as it is meaningless having in mind that your opinion won’t make any difference.</p>

<p>LLMs are indeed helping to write code, or better said, to generate text. This is in the end the idea of large language models -&gt; meaningful text generation. The main trap here is that, as a developer, you get a sense of large productivity because suddenly you are generating large amount of code in seconds. Having this metric in the head, productivity percentage from the beginning of the last paragraph is definitely true. What is important here is that the same code needs to be read and proven if it is working as it should.</p>

<p>Before, when you got the task, you would think first on how to implement it and then start with small portions, writing everything on your own, or reading docs of other libraries to be sure how to call their interfaces, etc. In that process, your cognitive focus was entirely on the code writing and in the same time understanding what it is doing.</p>

<p>On the other hand, with the AI generating the code, you don’t have anymore same cognitive process, but the cognitive load is shifting towards code review. OK, we can say, we are still good if we do the review faster than we used to write code before. Indeed, that is true, but here lie two things to consider:</p>

<ol>
  <li>
    <p>AI, by default, is generating  more code than we wrote before for the same tasks. Having this in mind, code review is longer and it is harder to get entire context at once of what is written, because you have so much code to take a look at.</p>
  </li>
  <li>
    <p>Code review is not the same as writing the code. Here is the big difference; we can compare it with writing the book and reading the book. Does the same understanding of the book content has its author and the reader? Same is happening here with the code. We will produce anomalies where people who are standing behind pull requests do not remember what that code is doing in a longer run.</p>
  </li>
</ol>

<h2 id="fears-on-productivity">Fears On Productivity</h2>

<p>If the main measurement of the productivity is the number of generated lines of code, this is already steered in wrong direction, because it will result in two things:</p>

<ol>
  <li>
    <p>Developers will spend less time reviewing because they compete to write the new code. Reviewing it is not something what brings an award.</p>
  </li>
  <li>
    <p>With less code review comes the even lesser understanding of what code does.</p>
  </li>
</ol>

<p>Both things are extremely dangerous in a long run. What will happen when something breaks in production; who can resolve it and take responsibility of the change on the code that he/she does not understand?</p>

<h2 id="ai-assisted-coding-vs-ai-driven-coding">AI Assisted Coding vs AI Driven Coding</h2>

<p>Another aspect what I tend to see is that vibe coding is steering the idea of AI assisted coding towards AI driven coding. Responses from AI agents are being more and more persuasive and many developers tend to put all trust in it, having less critical approach. This makes the game more complicated because it twists around the roles of main driver and his assistant. If we combine this with the statements from the last two paragraphs, we can imagine in which direction it can lead.</p>

<p>With the last development on skills and AI bots doing the review on every pull requests, insisting on the human code review has to be bigger than ever. If we rely on bot doing the code review and then pasting the bot comments into prompt asking for the changes, or even when bot automatically creates a changes for us, that is already concerning and should raise the question about who is steering the development and who shall be the main driver. These things could have a big cost in the future when something goes wrong, and it always will. Some things will go wrong, it is just matter of time and cost in the end.</p>

<h2 id="asset-vs-liability">Asset vs Liability</h2>

<p>Code is not an asset, it is a liability. The asset is its functionality which brings money when it is put in production. But every new line of code is liability by default because it is a technical debt, it requires maintenance and therefore spends money. And more you have of it, you are more down in that debt spiral. Less code which is doing its job shall be the main focus.</p>

<p>Therefore, productivity measurement which relies on how many lines of code one developer is generating does not make any sense. In fact, it only has a negative impact because, as an output, it has less code reviews and more generated code.</p>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>AI agents are very helpful when used carefully. Asking it to explain you and summarize what some portion of legacy code does is extremely helpful. Asking it to write an unit of code which you steer from the beginning to the end and having full focus on it is extremely helpful. AI bots which are doing the review are helpful, can find edge cases which you didn’t think of during the development. All of these things can really be used as a benefit, but not as the <em>only</em> source of truth.</p>

<p>Some of the lessons I have learned so far is to aim to write less code as possible, because that is one of the ways how you can still keep yourself focused and not lose the track.
Also, when bug-bot does the review, you should ask yourself if that comment makes sense in the business logic you are expecting to implement.
Be critical, be more critical on the review, same as you would be for someone who is touching the code base for the first time. This will help to break the illusion which agent is giving persuading you that it did the correct thing by default.</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="coding" /><category term="software" /><category term="ai" /><category term="llm" /><summary type="html"><![CDATA[For a long time, I am thinking about writing something on this topic, but since I was not sure about my thoughts and how to formulate them, I kept silent. The last few months things are getting clearer in my mind, so here I am to express my opinion on current AI development and its influence on software engineering industry and everyday tasks of one developer. I am writing this post in hope that my experience of more than one year actively using LLMs and agents in coding could help someone who is struggling with the entire idea behind.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://optimalcoder.net/assets/images/posts/ai-assisted-coding.jpg" /><media:content medium="image" url="https://optimalcoder.net/assets/images/posts/ai-assisted-coding.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why Soft Skills and Body Language Matter for Software Developers</title><link href="https://optimalcoder.net/why-body-language-is-important-for-software-developer/" rel="alternate" type="text/html" title="Why Soft Skills and Body Language Matter for Software Developers" /><published>2025-06-16T00:00:00+00:00</published><updated>2025-06-16T00:00:00+00:00</updated><id>https://optimalcoder.net/why-body-language-is-important-for-software-developer</id><content type="html" xml:base="https://optimalcoder.net/why-body-language-is-important-for-software-developer/"><![CDATA[<p>When we think of software developers, we often imagine them focused only on the (messy) code. But in an usual collaborative work environment, technical skills alone are not enough. Body language does significantly influence how you are perceived, how you connect with your team, and how effectively you convey your ideas.
We will try here in a few points to summarize why body language is important for developers and what we should pay attention to:</p>

<h2 class="space" id="1-improves-team-communication">1. Improves Team Communication</h2>

<p>Developers regularly work in teams; they participate in daily stand-ups, code reviews, brainstorming sessions, internal workshops… Positive body language like an eye contact, open posture, and nodding shows attentiveness and fosters trust, helping ideas flow more easily.</p>

<h2 class="space" id="2-boosts-leadership-presence">2. Boosts Leadership Presence</h2>

<p>As developers grow into senior roles or team leads, their ability to inspire and influence others becomes crucial. Confident body language supports verbal communication and reinforces authority and clarity.</p>

<h2 class="space" id="3-enhances-presentation-skills">3. Enhances Presentation Skills</h2>

<p>Whether you’re demoing a project or explaining a complex architecture to non-technical stakeholders, your gestures, facial expressions, and posture can make the difference between confusion and clarity.</p>

<h2 class="space" id="4-reduces-miscommunication">4. Reduces Miscommunication</h2>

<p>In diverse teams, especially in cross-cultural ones, body language can be beneficial where words may fail. Being aware of your own non-verbal signals and interpreting others’, can prevent potential misunderstandings.</p>

<h2 class="space" id="5-improves-interviews-and-career-growth">5. Improves Interviews and Career Growth</h2>

<p>Whether you’re applying for a job or seeking internal promotion, your body language during interviews or meetings plays a crucial role in how competent, confident, and trustworthy you may seem.</p>

<h2 class="space" id="books-to-consider">Books To Consider</h2>

<p>Above, we mentioned few points why body language is important. Bellow are some good books to read on this topic:</p>
<ol>
  <li>
    <p><strong>“What Every BODY is Saying” by Joe Navarro</strong>
   A former FBI agent shares how to decode others’ body language and how to project confidence. <a href="https://amzn.eu/d/fSxYUwK">https://amzn.eu/d/fSxYUwK</a></p>
  </li>
  <li>
    <p><strong>“The Definitive Book of Body Language” by Barbara Pease &amp; Allan Pease</strong>
A comprehensive guide to understanding non-verbal cues in various settings. <a href="https://amzn.eu/d/7ENzQQU">https://amzn.eu/d/7ENzQQU</a></p>
  </li>
  <li>
    <p><strong>“Body Language: How to Read Others’ Thoughts by Their Gestures” by Allan Pease</strong>
A classic that breaks down practical body language for daily interactions. <a href="https://amzn.eu/d/2VLgUJT">https://amzn.eu/d/2VLgUJT</a></p>
  </li>
  <li>
    <p><strong>“Presence” by Amy Cuddy</strong>
Focuses on how body posture affects confidence and presence, including for presentations and meetings. <a href="https://amzn.eu/d/3LlQj4c">https://amzn.eu/d/3LlQj4c</a></p>
  </li>
  <li>
    <p><strong>“Louder Than Words” by Joe Navarro</strong>
Offers deeper insight into how professionals can use non-verbal intelligence to communicate more effectively. <a href="https://amzn.eu/d/gpdSaro">https://amzn.eu/d/gpdSaro</a></p>
  </li>
  <li>
    <p><strong>“The Like Switch” by Jack Schafer</strong>
A former FBI agent explains how to use body language and psychology to build rapport quickly. <a href="https://amzn.eu/d/hbnyqEh">https://amzn.eu/d/hbnyqEh</a></p>
  </li>
</ol>

<p>Have you already read some of them? Share your thoughts in the comments!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="bodylanguage" /><category term="softskills" /><category term="developer" /><category term="books" /><summary type="html"><![CDATA[When we think of software developers, we often imagine them focused only on the (messy) code. But in an usual collaborative work environment, technical skills alone are not enough. Body language does significantly influence how you are perceived, how you connect with your team, and how effectively you convey your ideas. We will try here in a few points to summarize why body language is important for developers and what we should pay attention to:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://optimalcoder.net/assets/images/posts/body-language.jpg" /><media:content medium="image" url="https://optimalcoder.net/assets/images/posts/body-language.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">ASP.NET Core Request Lifecycle Explained: From HTTP Request to Response</title><link href="https://optimalcoder.net/understanding-the-lifecycle-of-dotnet-core-web-api-request/" rel="alternate" type="text/html" title="ASP.NET Core Request Lifecycle Explained: From HTTP Request to Response" /><published>2025-06-09T00:00:00+00:00</published><updated>2025-06-09T00:00:00+00:00</updated><id>https://optimalcoder.net/understanding-the-lifecycle-of-dotnet-core-web-api-request</id><content type="html" xml:base="https://optimalcoder.net/understanding-the-lifecycle-of-dotnet-core-web-api-request/"><![CDATA[<p>When you send a request to a .NET Core Web API, it goes thru series of steps before a response is returned. Understanding this lifecycle is important in order to built what we need and how to use some of the prebuild functionalities. In this post, we’ll break down the main steps of the .NET Core Web API request lifecycle.</p>

<h2 class="space" id="1-application-startup">1. Application Startup</h2>

<p>Before any request is handled, the application goes through the startup process:</p>

<ul>
  <li><strong>Program.cs / Main Method</strong>: This is the entry point. It builds and runs the web host.</li>
  <li><strong>Startup.cs (if used)</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">ConfigureServices</code>: This method is used to register services in the <strong>Dependency Injection (DI)</strong> container.</li>
      <li><code class="language-plaintext highlighter-rouge">Configure</code>: This sets up the <strong>HTTP request pipeline</strong> using middleware.</li>
    </ul>
  </li>
</ul>

<p>With .NET 6 and later, the startup logic is often consolidated into the <code class="language-plaintext highlighter-rouge">Program.cs</code> file using the minimal hosting model.</p>

<h2 class="space" id="2-http-request-pipeline-middleware">2. HTTP Request Pipeline (Middleware)</h2>

<p>Once the application is running, every incoming HTTP request goes through the middleware pipeline. Middleware components can perform operations before and after the next component runs:</p>

<ul>
  <li><strong>Built-in Middleware Examples</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">UseRouting</code> – Matches the request to an endpoint.</li>
      <li><code class="language-plaintext highlighter-rouge">UseAuthentication</code> – Handles authentication.</li>
      <li><code class="language-plaintext highlighter-rouge">UseAuthorization</code> – Checks user permissions.</li>
      <li><code class="language-plaintext highlighter-rouge">UseEndpoints</code> – Executes the matched endpoint (controller action, etc.).</li>
    </ul>
  </li>
</ul>

<p>Custom middleware can also be added for logging, request modification, etc.</p>

<h2 class="space" id="3-routing-and-endpoint-matching">3. Routing and Endpoint Matching</h2>

<p>Routing determines which controller and action should handle the request:</p>

<ul>
  <li>Based on the request URL and HTTP method (GET, POST, etc.).</li>
  <li>Routes can be attribute-based (<code class="language-plaintext highlighter-rouge">[HttpGet("api/products")]</code>) or convention-based (defined in <code class="language-plaintext highlighter-rouge">MapControllers()</code>).</li>
</ul>

<h2 class="space" id="4-model-binding-and-validation">4. Model Binding and Validation</h2>

<p>Once the endpoint is matched:</p>

<ul>
  <li><strong>Model Binding</strong> kicks in to map HTTP request data (query strings, headers, body) to method parameters and models.</li>
  <li><strong>Validation</strong> is performed using data annotations (<code class="language-plaintext highlighter-rouge">[Required]</code>, <code class="language-plaintext highlighter-rouge">[StringLength]</code>, etc.) or custom validators.</li>
</ul>

<p>If validation fails, the framework can automatically return a <code class="language-plaintext highlighter-rouge">400 Bad Request</code>.</p>

<h2 class="space" id="5-controller-action-execution">5. Controller Action Execution</h2>

<p>Now the actual controller method (action) is invoked. Here’s what happens:</p>

<ul>
  <li>Dependencies required by the controller or action method are injected.</li>
  <li>The logic in the controller is executed.</li>
  <li>It returns a result (e.g., <code class="language-plaintext highlighter-rouge">Ok()</code>, <code class="language-plaintext highlighter-rouge">NotFound()</code>, or a custom object).</li>
</ul>

<h2 class="space" id="6-result-execution">6. Result Execution</h2>

<p>The action result is processed and converted into an HTTP response:</p>

<ul>
  <li>This may involve serializing objects into JSON (typically using <code class="language-plaintext highlighter-rouge">System.Text.Json</code> or <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code>).</li>
  <li>The framework sets the response headers and status code.</li>
</ul>

<h2 class="space" id="7-middleware-response-processing">7. Middleware Response Processing</h2>

<p>After the action result is generated, control returns back <em>up</em> through the middleware pipeline in reverse order. This gives middleware components the chance to:</p>

<ul>
  <li>Modify or inspect the response (e.g., add custom headers).</li>
  <li>Log the outcome (status code, response time, etc.).</li>
  <li>Handle exceptions globally (via <a href="https://optimalcoder.net/net-core-web-api-exceptions-and-logger">custom exception-handling middleware</a>).</li>
  <li>Apply cross-cutting features like CORS, caching, and compression.</li>
</ul>

<p>This is the final point where behavior can be influenced before the response goes back to the client.</p>

<h2 class="space" id="8-response-sent-to-client">8. Response Sent to Client</h2>

<p>Finally, the HTTP response is sent back to the client. At this point, the request lifecycle is complete.</p>

<h2 class="space" id="conclusion">Conclusion</h2>

<p>The .NET Core Web API request lifecycle is powerful and highly extensible. By understanding each step, you can design better APIs, implement robust logging and error handling, and create a maintainable architecture that scales well.</p>

<p>Happy coding!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="dotnet core" /><category term="web api" /><category term="softwaredesign" /><category term="optimization" /><summary type="html"><![CDATA[When you send a request to a .NET Core Web API, it goes thru series of steps before a response is returned. Understanding this lifecycle is important in order to built what we need and how to use some of the prebuild functionalities. In this post, we’ll break down the main steps of the .NET Core Web API request lifecycle.]]></summary></entry><entry><title type="html">Premature Optimization in Software Development: Examples, Risks, and Best Practices</title><link href="https://optimalcoder.net/premature-optimization/" rel="alternate" type="text/html" title="Premature Optimization in Software Development: Examples, Risks, and Best Practices" /><published>2025-06-02T00:00:00+00:00</published><updated>2025-06-02T00:00:00+00:00</updated><id>https://optimalcoder.net/premature-optimization</id><content type="html" xml:base="https://optimalcoder.net/premature-optimization/"><![CDATA[<p>In software development, performance matters, but chasing it too early can lead you into dangerous territory. This is the pitfall of premature optimization, a term that has become a cautionary mantra among developers. As said by Donald Knuth, the phrase “premature optimization is the root of all evil” is not just a quip, it’s a hard-earned lesson from decades of building complex systems.</p>

<h2 class="space" id="what-is-premature-optimization">What Is Premature Optimization?</h2>

<p>Premature optimization is the act of trying to make parts of a system faster or more efficient before there is a clear need or before you fully understand the system’s requirements and behavior. It often involves guessing at bottlenecks rather than measuring them, optimizing code paths that don’t impact the overall performance, or complicating designs in anticipation of hypothetical future needs.</p>

<h2 class="space" id="why-its-a-trap">Why It’s a Trap</h2>

<h3 class="space" id="1-wasted-time-and-effort">1. Wasted Time and Effort</h3>

<p>When you optimize too early, you risk spending hours or even days improving something that turns out to be irrelevant. You might optimize a loop that runs once per hour instead of a query that runs a thousand times per second. Without data, you’re solving the wrong problems.</p>

<h3 class="space" id="2-complexity-without-justification">2. Complexity Without Justification</h3>

<p>Optimized code is often more complex and harder to read or maintain. If the performance gain isn’t needed, all you’re left with is unnecessary complexity, in other words, a debt you or your teammates will have to pay later.</p>

<h3 class="space" id="3-inhibits-flexibility">3. Inhibits Flexibility</h3>

<p>Over-optimized code is often not flexible. It’s tuned for a specific case and harder to refactor or extend. As project requirements evolve, which they always do, this premature rigidity can slow progress and reduce adaptability.</p>

<h3 class="space" id="4-violates-yagni-you-arent-gonna-need-it">4. Violates YAGNI (You Aren’t Gonna Need It)</h3>

<p>Premature optimization often assumes future needs, like handling millions of users, processing terabytes of data, etc. without current evidence. You end up designing for scale you may never reach, violating a key agile principle: build what you need when you need it.</p>

<h2 class="space" id="when-optimization-does-matter">When Optimization Does Matter</h2>

<p>Of course, optimization has its place, but after you’ve measured performance, identified real bottlenecks, and validated that improvements are necessary. This approach, known as profiling-first optimization, ensures that your efforts are focused where they will have real impact.</p>

<p>A few places where early attention to performance can be warranted include:</p>

<ul>
  <li>
    <p>Low-level systems (like OS kernels or game engines)</p>
  </li>
  <li>
    <p>High-frequency trading systems</p>
  </li>
  <li>
    <p>Mobile or embedded systems with strict resource constraints</p>
  </li>
</ul>

<p>C- ode running in tight loops that process huge datasets</p>

<p>In these contexts, a deep understanding of performance may be built into the design from the beginning, but even then, measurements guide the process.</p>

<h2 class="space" id="best-practices-to-avoid-the-trap">Best Practices to Avoid the Trap</h2>

<ol>
  <li>
    <p>Profile Before Optimizing: Use tools to measure real performance and identify hotspots.</p>
  </li>
  <li>
    <p>Start Simple, Then Improve: Write clean, maintainable code first. Optimize only when needed.</p>
  </li>
  <li>
    <p>Use Clear Benchmarks: Don’t optimize based on gut feelings. Set clear, measurable performance goals.</p>
  </li>
  <li>
    <p>Isolate Critical Paths: Keep performance-critical code separate from general logic when needed.</p>
  </li>
  <li>
    <p>Revisit with Data: As usage grows, revisit your code with new performance data and optimize accordingly.</p>
  </li>
</ol>

<h2 class="space" id="conclusion">Conclusion</h2>

<p>Premature optimization is seductive. It feels like you’re being smart and proactive. But in reality, it often leads to wasted time, reduced code quality, and misallocated resources. In modern development, especially with powerful hardware and compilers, correctness, clarity, and maintainability should come first. Optimize when, and only when, the data tells you it’s necessary.</p>

<p>Write code that works. Make it clear. Then, if needed, make it fast.</p>

<p>Happy coding!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="cleancode" /><category term="designpatterns" /><category term="softwaredesign" /><category term="optimization" /><summary type="html"><![CDATA[In software development, performance matters, but chasing it too early can lead you into dangerous territory. This is the pitfall of premature optimization, a term that has become a cautionary mantra among developers. As said by Donald Knuth, the phrase “premature optimization is the root of all evil” is not just a quip, it’s a hard-earned lesson from decades of building complex systems.]]></summary></entry><entry><title type="html">Design Patterns: Adapter</title><link href="https://optimalcoder.net/design-patterns-adapter/" rel="alternate" type="text/html" title="Design Patterns: Adapter" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://optimalcoder.net/design-patterns-adapter</id><content type="html" xml:base="https://optimalcoder.net/design-patterns-adapter/"><![CDATA[<p>The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. Rather than modifying existing code to fit a new system, the Adapter Pattern lets us wrap it with a new interface, acting as a bridge between mismatched parts of your application.</p>

<p>This approach is especially valuable when integrating legacy systems or third-party APIs that don’t follow your architecture or naming conventions.</p>

<h2 class="space" id="when-to-use-the-adapter-pattern">When to Use the Adapter Pattern</h2>

<p>You should consider using the Adapter Pattern when:</p>

<ul>
  <li>
    <p>You need to integrate an existing class, but its interface doesn’t match what your code expects.</p>
  </li>
  <li>
    <p>You’re modernizing or refactoring a system and want to avoid breaking changes.</p>
  </li>
  <li>
    <p>You’re working with third-party libraries that can’t be modified.</p>
  </li>
</ul>

<h2 class="space" id="implementing-the-prototype-pattern-in-c">Implementing the Prototype Pattern in C#</h2>

<p>Lets imagine we are building an application that relies on a clean, simple interface for logging:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">ILogger</span>
<span class="p">{</span>
    <span class="k">void</span> <span class="nf">LogInfo</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">);</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Now imagine you have a legacy logging class written years ago:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">LegacyLogger</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">WriteLogToFile</span><span class="p">(</span><span class="kt">string</span> <span class="n">severity</span><span class="p">,</span> <span class="kt">string</span> <span class="n">message</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"[</span><span class="p">{</span><span class="n">severity</span><span class="p">.</span><span class="nf">ToUpper</span><span class="p">()}</span><span class="s">] </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s"> (written to file)"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>These two interfaces are incompatible. The client expects <code class="language-plaintext highlighter-rouge">LogInfo(string)</code>, but the legacy class only supports <code class="language-plaintext highlighter-rouge">WriteLogToFile(string, string)</code>.</p>

<p>Instead of modifying either interface, you can use the Adapter Pattern to resolve this mismatch.</p>

<h3 class="space" id="create-the-adapter">Create the Adapter</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">LegacyLoggerAdapter</span> <span class="p">:</span> <span class="n">ILogger</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">LegacyLogger</span> <span class="n">_legacyLogger</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">LegacyLoggerAdapter</span><span class="p">(</span><span class="n">LegacyLogger</span> <span class="n">legacyLogger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_legacyLogger</span> <span class="p">=</span> <span class="n">legacyLogger</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">LogInfo</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// Adapt method name and parameter structure</span>
        <span class="n">_legacyLogger</span><span class="p">.</span><span class="nf">WriteLogToFile</span><span class="p">(</span><span class="s">"info"</span><span class="p">,</span> <span class="n">message</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here, <code class="language-plaintext highlighter-rouge">LegacyLoggerAdapter</code> translates the expected <code class="language-plaintext highlighter-rouge">LogInfo</code> method into the legacy class’s <code class="language-plaintext highlighter-rouge">WriteLogToFile</code> method.</p>

<h3 class="space" id="client">Client</h3>

<p>Now, our client code can use the <code class="language-plaintext highlighter-rouge">ILogger</code> interface without knowing anything about the legacy system:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">ILogger</span> <span class="n">logger</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">LegacyLoggerAdapter</span><span class="p">(</span><span class="k">new</span> <span class="nf">LegacyLogger</span><span class="p">());</span>
        <span class="n">logger</span><span class="p">.</span><span class="nf">LogInfo</span><span class="p">(</span><span class="s">"Adapter pattern works!"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The client gets the expected behavior, and the legacy system remains untouched.</p>

<h2 class="space" id="benefits">Benefits</h2>

<ul>
  <li>
    <p>Decouples client code from implementation details</p>
  </li>
  <li>
    <p>Avoids rewriting or breaking legacy systems</p>
  </li>
  <li>
    <p>Supports Open/Closed Principle, open to extension, closed to modification</p>
  </li>
  <li>
    <p>Makes third-party or legacy classes reusable in new contexts</p>
  </li>
</ul>

<h2 class="space" id="when-not-to-use-it">When Not to Use It</h2>

<ul>
  <li>
    <p>When you can directly modify the original interface or class</p>
  </li>
  <li>
    <p>When adding an adapter just introduces unnecessary complexity</p>
  </li>
  <li>
    <p>If it’s used to hide bad design permanently instead of improving it over time</p>
  </li>
</ul>

<h2 class="space" id="conclusion">Conclusion</h2>

<p>The Adapter Pattern is really useful when we need to make incompatible interfaces work together without modifying existing code. This pattern provides clean and maintanable solution in the cases when we need to deal with legacy systems, some external libraries or when we just trying to reuse old components in new ways.</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="cleancode" /><category term="csharp" /><category term="designpatterns" /><category term="adapter" /><summary type="html"><![CDATA[The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. Rather than modifying existing code to fit a new system, the Adapter Pattern lets us wrap it with a new interface, acting as a bridge between mismatched parts of your application.]]></summary></entry><entry><title type="html">ASP.NET Core Web API Versioning and Testing</title><link href="https://optimalcoder.net/net-core-web-api-versioning-and-testing/" rel="alternate" type="text/html" title="ASP.NET Core Web API Versioning and Testing" /><published>2025-05-19T00:00:00+00:00</published><updated>2025-05-19T00:00:00+00:00</updated><id>https://optimalcoder.net/net-core-web-api-versioning-and-testing</id><content type="html" xml:base="https://optimalcoder.net/net-core-web-api-versioning-and-testing/"><![CDATA[<p>In the <a href="https://optimalcoder.net/net-core-web-api-services-and-repositories">previous post</a>, we have paid attention on our project structure and organizing the code in multiple layers to achieve better readability and easier maintanence in the future. Now we can focus on long-term sustainability through versioning and testing.</p>

<p>In this final part of the series, we will:</p>
<ul>
  <li>Introduce API versioning so your app can evolve without breaking existing clients</li>
  <li>Add unit tests to validate service and controller behavior</li>
</ul>

<h2 class="space" id="api-versioning">API Versioning?</h2>

<p>Versioning helps your API evolve over time without breaking existing clients. Let’s enable it.</p>

<h3 class="space" id="step-1-install-required-package">Step 1: Install Required Package</h3>

<p>In the <code class="language-plaintext highlighter-rouge">Sample.Api</code> project, install:</p>
<div class="language-bash space highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package Microsoft.AspNetCore.Mvc.Versioning
</code></pre></div></div>
<h3 id="step-2-configure-versioning-in-programcs">Step 2: Configure Versioning in Program.cs</h3>

<p>In <code class="language-plaintext highlighter-rouge">Program.cs</code>, add the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    builder.Services.AddApiVersioning<span class="o">(</span>options <span class="o">=&gt;</span>
    <span class="o">{</span>
        options.AssumeDefaultVersionWhenUnspecified <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
        options.DefaultApiVersion <span class="o">=</span> new ApiVersion<span class="o">(</span>1, 0<span class="o">)</span><span class="p">;</span>
        options.ReportApiVersions <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="o">})</span><span class="p">;</span>

</code></pre></div></div>

<p>This:</p>

<ul>
  <li>
    <p>Defaults to version 1.0 when none is specified</p>
  </li>
  <li>
    <p>Adds version info in response headers</p>
  </li>
  <li>
    <p>Prepares your API to support multiple versions side-by-side</p>
  </li>
</ul>

<p>You can version your API using:</p>

<ul>
  <li>
    <p>URL segments (/v1/controller)</p>
  </li>
  <li>
    <p>Query string (?api-version=1.0)</p>
  </li>
  <li>
    <p>Headers (e.g., api-version: 1.0)</p>
  </li>
</ul>

<p>We’ll use URL-based versioning in this example.</p>

<h3 class="space" id="step-3-create-a-versioned-controller">Step 3: Create a Versioned Controller</h3>

<p>In <code class="language-plaintext highlighter-rouge">Sample.Api/Controllers</code>, decorate your controller like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/v{version:apiVersion}/[controller]"</span><span class="p">)]</span>
<span class="p">[</span><span class="nf">ApiVersion</span><span class="p">(</span><span class="s">"1.0"</span><span class="p">)]</span>
<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">UserController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
<span class="p">....</span>
</code></pre></div></div>

<p>That’s it—you’ve now added support for versioning via the URL.</p>

<h2 class="space" id="setting-up-a-test-project">Setting Up a Test Project</h2>

<p class="space">Now let’s set up unit testing using NUnit.</p>
<h3 id="step-1-create-a-new-test-project">Step 1: Create a New Test Project</h3>

<p>In the solution folder:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new nunit <span class="nt">-n</span> Sample.Api.Tests
</code></pre></div></div>
<p>Then add project references:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add Sample.Api.Tests reference Sample.Services
dotnet add Sample.Api.Tests reference Sample.Repositories
dotnet add Sample.Api.Tests reference Sample.DTO
dotnet add Sample.Api.Tests reference Sample.Entities

</code></pre></div></div>

<p>And install these packages:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add Sample.Api.Tests package Moq
dotnet add Sample.Api.Tests package Microsoft.AspNetCore.Mvc
</code></pre></div></div>
<p>Next, we can organize our tests in different folders, like <code class="language-plaintext highlighter-rouge">/Services</code>, <code class="language-plaintext highlighter-rouge">/Repositories</code>, <code class="language-plaintext highlighter-rouge">/Controllers</code></p>

<h3 class="space" id="step-2-writing-unit-tests-for-services">Step 2: Writing Unit Tests for Services</h3>

<p>Now, for the showing purpose, we can test UserService by mocking UserRepository:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">TestFixture</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">UserTests</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Test</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">GetUserByUsername_ReturnsDataFromRepo</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">mockRepo</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IUserRepository</span><span class="p">&gt;();</span>
        <span class="n">mockRepo</span><span class="p">.</span><span class="nf">Setup</span><span class="p">(</span><span class="n">r</span> <span class="p">=&gt;</span> <span class="n">r</span><span class="p">.</span><span class="nf">GetUser</span><span class="p">(</span><span class="s">"Test"</span><span class="p">)).</span><span class="nf">Returns</span><span class="p">(</span><span class="k">new</span> <span class="nf">User</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Id</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span>
            <span class="n">Email</span> <span class="p">=</span> <span class="s">"test mail"</span><span class="p">,</span>
            <span class="n">Username</span> <span class="p">=</span> <span class="s">"Test"</span><span class="p">,</span>
        <span class="p">});</span>

        <span class="kt">var</span> <span class="n">service</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">UserService</span><span class="p">(</span><span class="n">mockRepo</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">user</span> <span class="p">=</span> <span class="n">service</span><span class="p">.</span><span class="nf">GetUser</span><span class="p">(</span><span class="s">"Test"</span><span class="p">);</span>

        <span class="n">Assert</span><span class="p">.</span><span class="nf">That</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">Is</span><span class="p">.</span><span class="nf">EqualTo</span><span class="p">(</span><span class="s">"test mail"</span><span class="p">));</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, we have shown one mocking example and how to isolate our service testing from depencency on repository. Similar can be done with controllers as well. For more details on testing and mocking, you can take a look at some of the previous posts for <a href="https://optimalcoder.net/unit-testing-and-test-driven-development-tdd-in-csharp">unit testing</a> and <a href="https://optimalcoder.net/tdd-and-mocking">mocking</a>.</p>

<h2 class="space" id="summary">Summary</h2>

<p>With versioning and testing in place, your API is now ready for real-world use:</p>

<ul>
  <li>
    <p>Versioning allows the API to grow without breaking existing clients</p>
  </li>
  <li>
    <p>Unit tests help you catch regressions early and keep logic robust</p>
  </li>
</ul>

<p>This wraps up our step-by-step guide to building a clean, testable, and production-ready .NET 9 Web API using best practices.</p>

<p>Thanks for following the series!</p>

<p>Happy coding!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="dotnet" /><category term="dotnet9" /><category term="cleancode" /><category term="csharp" /><category term="softwaredesign" /><category term="versioning" /><category term="mocking" /><category term="unittesting" /><summary type="html"><![CDATA[In the previous post, we have paid attention on our project structure and organizing the code in multiple layers to achieve better readability and easier maintanence in the future. Now we can focus on long-term sustainability through versioning and testing.]]></summary></entry><entry><title type="html">ASP.NET Core Web API Architecture: Service and Repository Pattern Best Practices</title><link href="https://optimalcoder.net/net-core-web-api-services-and-repositories/" rel="alternate" type="text/html" title="ASP.NET Core Web API Architecture: Service and Repository Pattern Best Practices" /><published>2025-05-12T00:00:00+00:00</published><updated>2025-05-12T00:00:00+00:00</updated><id>https://optimalcoder.net/net-core-web-api-services-and-repositories</id><content type="html" xml:base="https://optimalcoder.net/net-core-web-api-services-and-repositories/"><![CDATA[<p>In the <a href="https://optimalcoder.net/net-core-web-api-exceptions-and-logger">previous post</a>, we added global error handling and logging using NLog. Now that we’ve made our API more resilient, it’s time to go forwards with cleaning up the architecture and separate concerns.</p>

<p>In this post, we’ll cover:</p>
<ul>
  <li>Introduce a clean project  structure</li>
  <li>Create Service and Repository layers</li>
  <li>Register dependencies with Dependency Injection</li>
  <li>Hook everything up in a simple API endpoint</li>
</ul>

<p>This sets the foundation for building a maintainable and testable API going forward.</p>

<h2 class="space" id="why-clean-architecture">Why Clean Architecture?</h2>

<p>A clean arhitecture helps us keeping business logic separate from infrastructure layer and make our 
project easier to test and extend and, in the end, it improves readability and maintanability over time.</p>

<h2 class="space" id="proposed-project-structure">Proposed Project Structure</h2>

<p>We’ll split the solution into multiple class libraries to keep things modular and clean:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Api</code> contains API-specific setup like controllers and middleware.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Services</code> includes business logic, independent of controllers or data sources.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Repositories</code> handles communication with the database.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Entities</code> contains models that map directly to your database schema.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.DTO</code> defines what’s exposed via API—keeping entities encapsulated.</p>
  </li>
</ul>

<p>Each layer references only what it needs. For example:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Services</code> references <code class="language-plaintext highlighter-rouge">Sample.Repositories</code>, <code class="language-plaintext highlighter-rouge">Sample.DTO</code>, <code class="language-plaintext highlighter-rouge">Sample.Entities</code></p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Repositories</code> references <code class="language-plaintext highlighter-rouge">Sample.Entities</code></p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Sample.Api</code> references <code class="language-plaintext highlighter-rouge">Sample.Services</code>, <code class="language-plaintext highlighter-rouge">Sample.Repositories</code> and <code class="language-plaintext highlighter-rouge">Sample.DTO</code></p>
  </li>
</ul>

<h3 id="lets-create-migration-with-some-test-data">Lets Create Migration With Some Test Data</h3>

<p>Update your Migrations with:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="p">[</span><span class="nf">Migration</span><span class="p">(</span><span class="m">20250503001</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Mig20250503001_UsersTestData</span> <span class="p">:</span> <span class="n">Migration</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Down</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">NotImplementedException</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Up</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Insert</span><span class="p">.</span><span class="nf">IntoTable</span><span class="p">(</span><span class="s">"Users"</span><span class="p">).</span><span class="nf">Row</span><span class="p">(</span><span class="k">new</span>
            <span class="p">{</span>
                <span class="n">Username</span> <span class="p">=</span> <span class="s">"john.doe"</span><span class="p">,</span>
                <span class="n">Email</span> <span class="p">=</span> <span class="s">"john.doe@email.com"</span>
            <span class="p">});</span>
            
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="repository">Repository</h3>

<p>In the repository project we need to add SqlKata library. More info about SqlKata can be found in one of my <a href="https://optimalcoder.net/dapper-on-steroids-sqlkata">earlier post</a></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Sample.Entities</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">SqlKata.Execution</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Sample.Repositories</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IUserRepository</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="nf">InsertNewUser</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">);</span>
        <span class="n">User</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">int</span> <span class="n">userId</span><span class="p">);</span>
        <span class="n">User</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">UserRepository</span> <span class="p">:</span> <span class="n">IUserRepository</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">QueryFactory</span> <span class="n">_db</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">TableName</span> <span class="p">=</span> <span class="s">"Users"</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">UserRepository</span><span class="p">(</span><span class="n">QueryFactory</span> <span class="n">db</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_db</span> <span class="p">=</span> <span class="n">db</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="kt">int</span> <span class="nf">InsertNewUser</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="n">TableName</span><span class="p">).</span><span class="n">InsertGetId</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;(</span><span class="n">user</span><span class="p">);</span>
        <span class="p">}</span>


        <span class="k">public</span> <span class="n">User</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">int</span> <span class="n">userId</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="n">TableName</span><span class="p">).</span><span class="nf">Where</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="n">Id</span><span class="p">),</span> <span class="n">userId</span><span class="p">).</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">User</span><span class="p">&gt;().</span><span class="nf">Single</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">User</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="n">TableName</span><span class="p">).</span><span class="nf">Where</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="n">Username</span><span class="p">),</span> <span class="n">username</span><span class="p">).</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">User</span><span class="p">&gt;().</span><span class="nf">Single</span><span class="p">();</span>

        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="service">Service</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Sample.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IUserService</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="nf">InsertNewUser</span><span class="p">(</span><span class="n">UserDTO</span> <span class="n">user</span><span class="p">);</span>
        <span class="n">UserDTO</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">UserService</span> <span class="p">:</span> <span class="n">IUserService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IUserRepository</span> <span class="n">_repository</span><span class="p">;</span>
        <span class="k">public</span> <span class="nf">UserService</span><span class="p">(</span><span class="n">IUserRepository</span> <span class="n">repository</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_repository</span> <span class="p">=</span> <span class="n">repository</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">UserDTO</span> <span class="nf">GetUser</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">entity</span> <span class="p">=</span> <span class="n">_repository</span><span class="p">.</span><span class="nf">GetUser</span><span class="p">(</span><span class="n">username</span><span class="p">);</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nf">UserDTO</span><span class="p">()</span>
            <span class="p">{</span>
                <span class="n">Username</span> <span class="p">=</span> <span class="n">entity</span><span class="p">.</span><span class="n">Username</span><span class="p">,</span>
                <span class="n">Email</span> <span class="p">=</span> <span class="n">entity</span><span class="p">.</span><span class="n">Email</span>
            <span class="p">};</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="kt">int</span> <span class="nf">InsertNewUser</span><span class="p">(</span><span class="n">UserDTO</span> <span class="n">user</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">entity</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">User</span><span class="p">()</span>
            <span class="p">{</span>
                <span class="n">Username</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Username</span><span class="p">,</span>
                <span class="n">Email</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span>
            <span class="p">};</span>
            <span class="k">return</span> <span class="n">_repository</span><span class="p">.</span><span class="nf">InsertNewUser</span><span class="p">(</span><span class="n">entity</span><span class="p">);</span>        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>
<p>Here is just a simple implementation given, but the base idea is for service classes to manage interaction between <code class="language-plaintext highlighter-rouge">Repositories</code> and <code class="language-plaintext highlighter-rouge">Api Controllers</code> and do all the things in between about mapping data and additional business logic.</p>

<h3 id="register-all-in--programcs">Register all in  <code class="language-plaintext highlighter-rouge">Program.cs</code></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">....</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddScoped</span><span class="p">(</span><span class="n">provider</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">connection</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SqlConnection</span><span class="p">(</span><span class="n">connectionString</span><span class="p">);</span>

    <span class="kt">var</span> <span class="n">compiler</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SqlServerCompiler</span><span class="p">();</span>

    <span class="k">return</span> <span class="k">new</span> <span class="nf">QueryFactory</span><span class="p">(</span><span class="n">connection</span><span class="p">,</span> <span class="n">compiler</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IUserRepository</span><span class="p">,</span> <span class="n">UserRepository</span><span class="p">&gt;();</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IUserService</span><span class="p">,</span> <span class="n">UserService</span><span class="p">&gt;();</span>

<span class="p">....</span>
</code></pre></div></div>

<h3 id="controller">Controller</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Sample.Api.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"[controller]"</span><span class="p">)]</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">UserController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>

        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IUserService</span> <span class="n">_service</span><span class="p">;</span>
        <span class="k">public</span> <span class="nf">UserController</span><span class="p">(</span><span class="n">IUserService</span> <span class="n">service</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_service</span> <span class="p">=</span> <span class="n">service</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">UserDTO</span> <span class="nf">GetUserByUsername</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_service</span><span class="p">.</span><span class="nf">GetUser</span><span class="p">(</span><span class="n">username</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 class="space" id="summary">Summary</h2>

<p>With the new project structure in place:</p>

<ul>
  <li>
    <p>Business logic lives in the service layer</p>
  </li>
  <li>
    <p>Data access is cleanly separated in a repository</p>
  </li>
  <li>
    <p>Models are split into entities and DTOs</p>
  </li>
  <li>
    <p>The API is minimal and focused on routing and HTTP interaction</p>
  </li>
</ul>

<p>This is a scalable foundation that makes future growth and testing straightforward.</p>

<h2 class="space" id="coming-up-next">Coming Up Next…</h2>

<p>In Part 4, we’ll focus more on testing and API versioning.</p>

<p>Stay tuned!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="dotnet" /><category term="dotnet9" /><category term="cleancode" /><category term="csharp" /><category term="softwaredesign" /><category term="services" /><category term="repository" /><category term="sqlkata" /><category term="dapper" /><summary type="html"><![CDATA[In the previous post, we added global error handling and logging using NLog. Now that we’ve made our API more resilient, it’s time to go forwards with cleaning up the architecture and separate concerns.]]></summary></entry><entry><title type="html">Global Exception Handling and Logging in ASP.NET Core Web API with NLog</title><link href="https://optimalcoder.net/net-core-web-api-exceptions-and-logger/" rel="alternate" type="text/html" title="Global Exception Handling and Logging in ASP.NET Core Web API with NLog" /><published>2025-05-05T00:00:00+00:00</published><updated>2025-05-05T00:00:00+00:00</updated><id>https://optimalcoder.net/net-core-web-api-exceptions-and-logger</id><content type="html" xml:base="https://optimalcoder.net/net-core-web-api-exceptions-and-logger/"><![CDATA[<p>In the <a href="https://optimalcoder.net/net-core-web-api-project-setup">previous post</a>, we set up a basic .NET 9 Web API project. Now it’s time to take things further by adding <strong>global error handling</strong> and <strong>logging</strong> to make our API more robust.</p>

<p>In this post, we’ll cover:</p>
<ul>
  <li>How to implement <strong>global error handling</strong> with middleware</li>
  <li>Setting up <strong>NLog</strong> to log errors to both files and a <strong>database table</strong></li>
  <li>Creating a <strong>migration</strong> for the log table in the database</li>
</ul>

<h2 class="space" id="why-global-error-handling">Why Global Error Handling?</h2>

<p>Handling errors in one centralized place makes your API easier to maintain and more consistent. Instead of repeating error-catching logic in every controller, you can handle unexpected issues in a single middleware.</p>

<p>This approach allows you to return uniform error responses and capture useful logs automatically when something goes wrong.</p>

<h2 class="space" id="creating-global-error-handling-middleware">Creating Global Error Handling Middleware</h2>

<p>Create a <code class="language-plaintext highlighter-rouge">Middleware</code> folder in your project and add a file called <code class="language-plaintext highlighter-rouge">ExceptionHandlingMiddleware.cs</code>:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Http</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">ExceptionHandlingMiddleware</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">ExceptionHandlingMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">httpContext</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">httpContext</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">await</span> <span class="nf">HandleExceptionAsync</span><span class="p">(</span><span class="n">httpContext</span><span class="p">,</span> <span class="n">ex</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="n">Task</span> <span class="nf">HandleExceptionAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">Exception</span> <span class="n">exception</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">StatusCode</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">HttpStatusCode</span><span class="p">.</span><span class="n">InternalServerError</span><span class="p">;</span>
        <span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">ContentType</span> <span class="p">=</span> <span class="s">"application/json"</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">new</span> <span class="p">{</span> <span class="n">message</span> <span class="p">=</span> <span class="s">"An unexpected error occurred. Please try again later."</span> <span class="p">};</span>
        <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="nf">WriteAsJsonAsync</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="registering-the-middleware">Registering the Middleware</h2>

<p>Update your <code class="language-plaintext highlighter-rouge">Program.cs</code> to use this middleware:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">FluentMigrator.Runner</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Sample.Api.Middleware</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Sample.Migrations.Migrations</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services to the container.</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>

<span class="c1">// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenApi</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="n">UseMiddleware</span><span class="p">&lt;</span><span class="n">ExceptionHandlingMiddleware</span><span class="p">&gt;();</span>

<span class="c1">// Configure the HTTP request pipeline.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">MapOpenApi</span><span class="p">();</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>

</code></pre></div></div>

<h2 id="setting-up-nlog-in-net-9">Setting Up NLog in .NET 9</h2>

<h3 id="install-the-nlog-packages">Install the NLog packages</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package NLog.Web.AspNetCore
dotnet add package NLog.Database
</code></pre></div></div>

<h3 id="add-a-nlog-config-to-appsettingsjson-file">Add a <code class="language-plaintext highlighter-rouge">nlog</code> config to <code class="language-plaintext highlighter-rouge">appsettings.json</code> file</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Server=localhost;Database=SampleDb;Trusted_Connection=True;TrustServerCertificate=True"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"NLog"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"IncludeScopes"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"RemoveLoggerFactoryFilter"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"NLog"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"autoReload"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"throwConfigExceptions"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"internalLogLevel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Info"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"internalLogFile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${basedir}/internal-nlog.txt"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"extensions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"assembly"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NLog.Extensions.Logging"</span><span class="w"> </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"assembly"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NLog.Web.AspNetCore"</span><span class="w"> </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"assembly"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NLog.Database"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"variables"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"var_logdir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c:/temp"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"time"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AccurateUTC"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"targetDefaultWrapper"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AsyncWrapper"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"overflowAction"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Block"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"all-file"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"File"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"fileName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${var_logdir}/nlog-all-${shortdate}.log"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JsonLayout"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"Attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"timestamp"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${date:format=o}"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${level}"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"logger"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${logger}"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"message"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${message:raw=true}"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"properties"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"encode"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
              </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JsonLayout"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"includeallproperties"</span><span class="p">:</span><span class="w"> </span><span class="s2">"true"</span><span class="w">
              </span><span class="p">}</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"database"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Database"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"dbProvider"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Microsoft.Data.SqlClient.SqlConnection,Microsoft.Data.SqlClient"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"connectionString"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data Source=localhost;Initial Catalog=SampleDb;Trusted_Connection=True;TrustServerCertificate=True"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"keepConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"true"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"commandText"</span><span class="p">:</span><span class="w"> </span><span class="s2">"insert into dbo.[Logs] (TimeStamp,Level,Message,Logger,Exception) values (@Timestamp, @Level, @Message, @Logger, @Exception);"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"parameters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@Timestamp"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${date:format=o}"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"dbType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DbType.DateTime"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@Level"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${level}"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@Message"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${message}"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@Logger"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${logger}"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@Exception"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${exception:format=toString}"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"logger"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"minLevel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Trace"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"writeTo"</span><span class="p">:</span><span class="w"> </span><span class="s2">"all-file"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"logger"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"minLevel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Error"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"writeTo"</span><span class="p">:</span><span class="w"> </span><span class="s2">"database"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">

</span></code></pre></div></div>

<h3 id="load-nlog-config-in--programcs">Load NLog config in  <code class="language-plaintext highlighter-rouge">Program.cs</code></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">....</span>

<span class="n">LogManager</span><span class="p">.</span><span class="nf">Setup</span><span class="p">().</span><span class="nf">LoadConfigurationFromAppSettings</span><span class="p">();</span>

<span class="p">....</span>
</code></pre></div></div>

<h3 id="add-logger-to-exceptionhandlingmiddlewarecs">Add logger to <code class="language-plaintext highlighter-rouge">ExceptionHandlingMiddleware.cs</code></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ExceptionHandlingMiddleware</span>
<span class="p">{</span>
    <span class="p">...</span>

    <span class="k">private</span> <span class="k">readonly</span> <span class="n">Logger</span> <span class="n">_logger</span> <span class="p">=</span> <span class="n">LogManager</span><span class="p">.</span><span class="nf">GetCurrentClassLogger</span><span class="p">();</span>

    <span class="p">...</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">httpContext</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">httpContext</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
            <span class="k">await</span> <span class="nf">HandleExceptionAsync</span><span class="p">(</span><span class="n">httpContext</span><span class="p">,</span> <span class="n">ex</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 class="space" id="creating-the-logs-table-with-fluentmigrator">Creating the <code class="language-plaintext highlighter-rouge">Logs</code> Table with FluentMigrator</h2>

<p>Let’s create a migration to add a Logs table to our database where NLog can store log entries.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">FluentMigrator</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Sample.Migrations.Migrations</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Migration</span><span class="p">(</span><span class="m">20250426001</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Mig20250426001_CreateLogsTable</span><span class="p">:</span> <span class="n">Migration</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Up</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Create</span><span class="p">.</span><span class="nf">Table</span><span class="p">(</span><span class="s">"Logs"</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Id"</span><span class="p">).</span><span class="nf">AsInt32</span><span class="p">().</span><span class="nf">PrimaryKey</span><span class="p">().</span><span class="nf">Identity</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"TimeStamp"</span><span class="p">).</span><span class="nf">AsDateTime</span><span class="p">().</span><span class="nf">NotNullable</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Level"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">50</span><span class="p">).</span><span class="nf">NotNullable</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Logger"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">100</span><span class="p">).</span><span class="nf">NotNullable</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Message"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">1000</span><span class="p">).</span><span class="nf">NotNullable</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Exception"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">100</span><span class="p">).</span><span class="nf">Nullable</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Down</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Delete</span><span class="p">.</span><span class="nf">Table</span><span class="p">(</span><span class="s">"Logs"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 class="space" id="coming-up-next">Coming Up Next…</h2>

<p>In Part 3, we’ll introduce a project structure with service and repository layers, and use dependency injection to organize your business logic.</p>

<p>Stay tuned!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="dotnet" /><category term="dotnet9" /><category term="cleancode" /><category term="csharp" /><category term="softwaredesign" /><category term="exceptions" /><category term="nlog" /><summary type="html"><![CDATA[In the previous post, we set up a basic .NET 9 Web API project. Now it’s time to take things further by adding global error handling and logging to make our API more robust.]]></summary></entry><entry><title type="html">ASP.NET Core Web API Setup with FluentMigrator: Complete Beginner Guide</title><link href="https://optimalcoder.net/net-core-web-api-project-setup/" rel="alternate" type="text/html" title="ASP.NET Core Web API Setup with FluentMigrator: Complete Beginner Guide" /><published>2025-04-28T00:00:00+00:00</published><updated>2025-04-28T00:00:00+00:00</updated><id>https://optimalcoder.net/net-core-web-api-project-setup</id><content type="html" xml:base="https://optimalcoder.net/net-core-web-api-project-setup/"><![CDATA[<p>Welcome to the first post in the <strong>“.NET Core Web API – Step-by-Step Best Practices”</strong> series. In this series, we’ll walk through building a modern, clean, and maintainable Web API using .NET 9. Each post will focus on a specific aspect—from project setup and architecture to testing, migrations, and deployment.</p>

<p><img src="/assets/images/posts/flowchart-design.jpg" alt="Software Design" /></p>

<h2 class="space" id="in-this-post">In This Post</h2>

<p>We’ll cover:</p>

<ul>
  <li>Creating a new .NET 9 Web API project</li>
  <li>Understanding the default project structure</li>
  <li>Setting up <strong>FluentMigrator</strong></li>
  <li>Writing and running your first database migration</li>
</ul>

<h2 class="space" id="step-1-create-the-api-project">Step 1: Create the API Project</h2>

<p>Start by creating a new Web API project using the .NET CLI:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new webapi <span class="nt">-n</span> Sample.Api <span class="nt">--use-controllers</span>
</code></pre></div></div>

<h2 class="space" id="understanding-the-project-structure">Understanding the Project Structure</h2>

<p>After creating the project, you’ll see something like this:</p>

<div class="language-plaintext space highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sample.Api/
├── Controllers/
│   └── WeatherForecastController.cs
├── Program.cs
├── appsettings.json
└── Sample.Api.csproj
.
.
.
</code></pre></div></div>
<h3 id="programcs"><code class="language-plaintext highlighter-rouge">Program.cs</code></h3>

<p>This is the entry point of the app. It sets up services and configures the request pipeline. Here’s the full code of <code class="language-plaintext highlighter-rouge">Program.cs</code>:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services to the container.</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>

<span class="c1">// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenApi</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Configure the HTTP request pipeline.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">MapOpenApi</span><span class="p">();</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>

</code></pre></div></div>

<p>Let’s break it down:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">var builder = WebApplication.CreateBuilder(args);</code> initializes the app’s builder, where you configure services and settings. The <code class="language-plaintext highlighter-rouge">args</code> parameter allows command-line arguments to be passed in.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">builder.Services.AddControllers();</code> adds support for controllers, enabling your Web API to handle incoming HTTP requests.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">builder.Services.AddOpenApi(); </code>, this is simplified in dotnet 9, and used instead of <code class="language-plaintext highlighter-rouge">builder.Services.AddEndpointsApiExplorer();</code> and <code class="language-plaintext highlighter-rouge">builder.Services.AddSwaggerGen();</code>. It adds Swagger support for generating API documentation. This is useful for testing and exploring your API.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">var app = builder.Build();</code> builds the <code class="language-plaintext highlighter-rouge">WebApplication</code> object, finalizing the app configuration and preparing it for running.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">if (app.Environment.IsDevelopment()) { app.MapOpenApi(); // same as app.UseSwagger(); app.UseSwaggerUI(); }</code> enables Swagger UI for development environments, making it easy to explore and test the API.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">app.UseHttpsRedirection();</code> redirects all http requests to https</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">app.UseAuthorization();</code> adds middleware for handling authorization, which is necessary if your API needs to control access to certain routes.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">app.MapControllers();</code> maps the controller routes to the HTTP request pipeline, allowing the API to handle requests.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">app.Run();</code> starts the app and begins listening for incoming requests.</p>
  </li>
</ul>

<h3 id="appsettingsjson"><code class="language-plaintext highlighter-rouge">appsettings.json</code></h3>

<p>This file is used for configuration—stuff like connection strings, logging, and environment-specific settings:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Server=localhost;Database=SampleDb;Trusted_Connection=True;TrustServerCertificate=True"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="controllers"><code class="language-plaintext highlighter-rouge">Controllers/</code></h3>

<p>This folder holds your API endpoints. It’s where we’ll define controllers.</p>

<h2 class="space" id="step-2-create-a-separate-migrations-project">Step 2: Create a Separate Migrations Project</h2>

<p>We want to keep our code <strong>modular and focused</strong>, so we’ll separate database migration logic from our main Web API project and create a <strong>dedicated class library</strong> just for migrations.</p>

<h3 class="space" id="why-put-migrations-in-a-separate-class-library">Why put migrations in a separate class library?</h3>

<p>Here are a couple of reasons:</p>

<ul>
  <li><strong>Separation of concerns</strong>: Your Web API project should focus on handling HTTP requests and responses—not managing database schema. Keeping migrations in their own project ensures the API stays lean and focused.</li>
  <li><strong>Cleaner dependencies</strong>: The migration project will only reference what’s needed for database changes.</li>
  <li><strong>Better organization</strong>: As your project grows, having migrations away from API, in a dedicated project, keeps things easier to manage and navigate.</li>
</ul>

<h3 class="space" id="create-the-migration-library">Create the migration library</h3>

<p>Let’s create the class library:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new classlib <span class="nt">-n</span> Sample.Migrations
</code></pre></div></div>

<p>Then install the FluentMigrator packages:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>Sample.Migrations
dotnet add package FluentMigrator.Runner
dotnet add package FluentMigrator.Extensions.SqlServer
</code></pre></div></div>

<p>Now go back to your Web API project and link it to the migration library:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ../Sample.Api
dotnet add reference ../Sample.Migrations/Sample.Migrations.csproj
</code></pre></div></div>

<p>At this point, your solution structure looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sample.Api/
├── Program.cs
├── appsettings.json
└── ...

Sample.Migrations/
└── Sample.Migrations.csproj
</code></pre></div></div>

<h2 class="space" id="step-3-create-your-first-migration">Step 3: Create Your First Migration</h2>

<p>Inside the <code class="language-plaintext highlighter-rouge">Sample.Migrations</code> project, create folder <code class="language-plaintext highlighter-rouge">Migrations/</code>, and add this migration class:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">FluentMigrator</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Sample.Migrations.Migrations</span><span class="p">;</span>

<span class="p">[</span><span class="nf">Migration</span><span class="p">(</span><span class="m">20250421001</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Mig20250421001_InitialMigration</span> <span class="p">:</span> <span class="n">Migration</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Up</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">Create</span><span class="p">.</span><span class="nf">Table</span><span class="p">(</span><span class="s">"Users"</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Id"</span><span class="p">).</span><span class="nf">AsInt32</span><span class="p">().</span><span class="nf">PrimaryKey</span><span class="p">().</span><span class="nf">Identity</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Username"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">100</span><span class="p">).</span><span class="nf">NotNullable</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">WithColumn</span><span class="p">(</span><span class="s">"Email"</span><span class="p">).</span><span class="nf">AsString</span><span class="p">(</span><span class="m">200</span><span class="p">).</span><span class="nf">NotNullable</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Down</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">Delete</span><span class="p">.</span><span class="nf">Table</span><span class="p">(</span><span class="s">"Users"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 class="space" id="about-the-migration-attribute">About the <code class="language-plaintext highlighter-rouge">[Migration]</code> attribute</h3>

<p>The number you pass into the <code class="language-plaintext highlighter-rouge">[Migration(...)]</code> attribute is a unique ID for the migration. A common and helpful naming convention is to use a <strong>timestamp</strong> format, followed by a sequence number. Example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>YYYYMMDD + sequence
</code></pre></div></div>

<p>So in our case:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>20250421001
</code></pre></div></div>

<p>That means this is the first migration created on April 21, 2025. This format is nice because:</p>

<ul>
  <li>Migrations are naturally sorted in order</li>
  <li>You can tell when each migration was created at a glance</li>
  <li>It avoids name conflicts even if multiple migrations are added in the same day</li>
</ul>

<h2 class="space" id="step-4-configure-fluentmigrator-in-the-web-api">Step 4: Configure FluentMigrator in the Web API</h2>

<p>Back in <code class="language-plaintext highlighter-rouge">Program.cs</code> of <code class="language-plaintext highlighter-rouge">Sample.Api</code>, register FluentMigrator and point it to the <code class="language-plaintext highlighter-rouge">Sample.Migrations</code> assembly:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">FluentMigrator.Runner</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Sample.Migrations.Migrations</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenApi</span><span class="p">();</span>

<span class="c1">// Configure FluentMigrator using the separate migrations project</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddFluentMigratorCore</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">ConfigureRunner</span><span class="p">(</span><span class="n">rb</span> <span class="p">=&gt;</span> <span class="n">rb</span>
        <span class="p">.</span><span class="nf">AddSqlServer</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">WithGlobalConnectionString</span><span class="p">(</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)!)</span>
        <span class="p">.</span><span class="nf">ScanIn</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">InitialMigration</span><span class="p">).</span><span class="n">Assembly</span><span class="p">).</span><span class="n">For</span><span class="p">.</span><span class="nf">Migrations</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">AddLogging</span><span class="p">(</span><span class="n">lb</span> <span class="p">=&gt;</span> <span class="n">lb</span><span class="p">.</span><span class="nf">AddFluentMigratorConsole</span><span class="p">());</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Apply pending migrations on startup</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">runner</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">IMigrationRunner</span><span class="p">&gt;();</span>
    <span class="n">runner</span><span class="p">.</span><span class="nf">MigrateUp</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">MapOpenApi</span><span class="p">();</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>

<h2 class="space" id="coming-up-next">Coming Up Next…</h2>

<p>In Part 2, we’ll dive into <strong>global error handling and logging</strong>. We’ll cover:</p>

<ul>
  <li>Setting up custom error handling middleware</li>
  <li>Implementing logging for your API using <strong>NLog</strong></li>
  <li>How to effectively capture and log error details</li>
</ul>

<p>Thanks for reading! See you in Part 2!</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="dotnet" /><category term="dotnet9" /><category term="cleancode" /><category term="csharp" /><category term="softwaredesign" /><category term="fluentmigrator" /><category term="migrations" /><summary type="html"><![CDATA[Welcome to the first post in the “.NET Core Web API – Step-by-Step Best Practices” series. In this series, we’ll walk through building a modern, clean, and maintainable Web API using .NET 9. Each post will focus on a specific aspect—from project setup and architecture to testing, migrations, and deployment.]]></summary></entry><entry><title type="html">State Machines in C#: Complete Guide with Practical Examples</title><link href="https://optimalcoder.net/understanding-state-machines-with-a-practical-example-in-csharp/" rel="alternate" type="text/html" title="State Machines in C#: Complete Guide with Practical Examples" /><published>2025-04-21T00:00:00+00:00</published><updated>2025-04-21T00:00:00+00:00</updated><id>https://optimalcoder.net/understanding-state-machines-with-a-practical-example-in-csharp</id><content type="html" xml:base="https://optimalcoder.net/understanding-state-machines-with-a-practical-example-in-csharp/"><![CDATA[<p>State machines might sound like a complex topic, but at their core, they’re just a way to manage different states in a system and control how things transition from one state to another.</p>

<p>In this post, we’ll break down what state machines are, why they’re useful, and how to implement one in C# using a straightforward approach. We’ll also walk through a real-world example to see them in action.</p>

<h2 id="what-is-a-state-machine">What is a State Machine?</h2>

<p>A state machine is a pattern used to control the flow of a system by defining possible states and the rules that determine how transitions between those states happen. It consists of:</p>

<ul>
  <li>
    <p><strong>States</strong> : Different conditions or modes the system can be in.</p>
  </li>
  <li>
    <p><strong>Transitions</strong> : Rules that define how the system moves from one state to another.</p>
  </li>
  <li>
    <p><strong>Events</strong> : Triggers that cause state transitions.</p>
  </li>
  <li>
    <p><strong>Actions</strong> : Operations performed when entering, exiting, or staying in a state.</p>
  </li>
</ul>

<h2 id="why-use-a-state-machine">Why Use a State Machine?</h2>

<p>State machines provide several benefits when managing workflows:</p>

<ul>
  <li>
    <p><strong>Improved Readability</strong> : Helps organize complex logic in an understandable way.</p>
  </li>
  <li>
    <p><strong>Encapsulation of State Logic</strong> : Keeps state-specific behavior separate from other business logic.</p>
  </li>
  <li>
    <p><strong>Predictability and Maintainability</strong> : Makes it easier to track and debug state changes.</p>
  </li>
  <li>
    <p><strong>Flexibility</strong> : New states and transitions can be added without major changes.</p>
  </li>
</ul>

<h2 id="implementing-a-state-machine-in-c">Implementing a State Machine in C#</h2>

<p>Let’s implement a simple state machine using a dictionary to manage state transitions.</p>

<h3 id="use-case-order-processing-system">Use Case: Order Processing System</h3>

<p>Imagine you’re building an e-commerce application that handles order statuses. An order can go through several states before completion.</p>

<h3 id="states-and-transitions">States and Transitions</h3>

<ul>
  <li>
    <p><strong>States</strong> : <code class="language-plaintext highlighter-rouge">PendingPayment</code>, <code class="language-plaintext highlighter-rouge">Processing</code>, <code class="language-plaintext highlighter-rouge">Shipped</code>, <code class="language-plaintext highlighter-rouge">Delivered</code>, <code class="language-plaintext highlighter-rouge">Cancelled</code></p>
  </li>
  <li>
    <p><strong>Events</strong> :</p>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">Pay</code> (moves from PendingPayment to Processing)</li>
      <li><code class="language-plaintext highlighter-rouge">Ship</code> (moves from Processing to Shipped)</li>
      <li><code class="language-plaintext highlighter-rouge">Deliver</code> (moves from Shipped to Delivered)</li>
      <li><code class="language-plaintext highlighter-rouge">Cancel</code> (moves from PendingPayment to Cancelled or from Processing to Cancelled)</li>
    </ul>
  </li>
</ul>

<h3 id="c-implementation">C# Implementation</h3>

<ul>
  <li><strong>State Machine Logic</strong> :</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>

<span class="k">class</span> <span class="nc">OrderStateMachine</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">enum</span> <span class="n">OrderState</span> <span class="p">{</span> <span class="n">PendingPayment</span><span class="p">,</span> <span class="n">Processing</span><span class="p">,</span> <span class="n">Shipped</span><span class="p">,</span> <span class="n">Delivered</span><span class="p">,</span> <span class="n">Cancelled</span> <span class="p">}</span>
    <span class="k">public</span> <span class="k">enum</span> <span class="n">OrderTrigger</span> <span class="p">{</span> <span class="n">Pay</span><span class="p">,</span> <span class="n">Ship</span><span class="p">,</span> <span class="n">Deliver</span><span class="p">,</span> <span class="n">Cancel</span> <span class="p">}</span>

    <span class="k">private</span> <span class="n">Dictionary</span><span class="p">&lt;(</span><span class="n">OrderState</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">&gt;</span> <span class="n">_stateTransitions</span><span class="p">;</span>
    <span class="k">public</span> <span class="n">OrderState</span> <span class="n">CurrentState</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

    <span class="k">public</span> <span class="nf">OrderStateMachine</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">CurrentState</span> <span class="p">=</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">PendingPayment</span><span class="p">;</span>
        <span class="n">_stateTransitions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p">&lt;(</span><span class="n">OrderState</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">&gt;</span>
        <span class="p">{</span>
            <span class="p">{</span> <span class="p">(</span><span class="n">OrderState</span><span class="p">.</span><span class="n">PendingPayment</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Pay</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">Processing</span> <span class="p">},</span>
            <span class="p">{</span> <span class="p">(</span><span class="n">OrderState</span><span class="p">.</span><span class="n">PendingPayment</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Cancel</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">Cancelled</span> <span class="p">},</span>
            <span class="p">{</span> <span class="p">(</span><span class="n">OrderState</span><span class="p">.</span><span class="n">Processing</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Ship</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">Shipped</span> <span class="p">},</span>
            <span class="p">{</span> <span class="p">(</span><span class="n">OrderState</span><span class="p">.</span><span class="n">Processing</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Cancel</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">Cancelled</span> <span class="p">},</span>
            <span class="p">{</span> <span class="p">(</span><span class="n">OrderState</span><span class="p">.</span><span class="n">Shipped</span><span class="p">,</span> <span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Deliver</span><span class="p">),</span> <span class="n">OrderState</span><span class="p">.</span><span class="n">Delivered</span> <span class="p">}</span>
        <span class="p">};</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">ProcessTransition</span><span class="p">(</span><span class="n">OrderTrigger</span> <span class="n">trigger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">_stateTransitions</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">((</span><span class="n">CurrentState</span><span class="p">,</span> <span class="n">trigger</span><span class="p">),</span> <span class="k">out</span> <span class="n">OrderState</span> <span class="n">newState</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Moving from </span><span class="p">{</span><span class="n">CurrentState</span><span class="p">}</span><span class="s"> to </span><span class="p">{</span><span class="n">newState</span><span class="p">}</span><span class="s"> due to </span><span class="p">{</span><span class="n">trigger</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="n">CurrentState</span> <span class="p">=</span> <span class="n">newState</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Invalid transition: </span><span class="p">{</span><span class="n">CurrentState</span><span class="p">}</span><span class="s"> cannot handle </span><span class="p">{</span><span class="n">trigger</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li><strong>Client</strong> :</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">orderStateMachine</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">OrderStateMachine</span><span class="p">();</span>
        <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Initial state: </span><span class="p">{</span><span class="n">orderStateMachine</span><span class="p">.</span><span class="n">CurrentState</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

        <span class="c1">// Simulating order processing</span>
        <span class="n">orderStateMachine</span><span class="p">.</span><span class="nf">ProcessTransition</span><span class="p">(</span><span class="n">OrderStateMachine</span><span class="p">.</span><span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Pay</span><span class="p">);</span>
        <span class="n">orderStateMachine</span><span class="p">.</span><span class="nf">ProcessTransition</span><span class="p">(</span><span class="n">OrderStateMachine</span><span class="p">.</span><span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Ship</span><span class="p">);</span>
        <span class="n">orderStateMachine</span><span class="p">.</span><span class="nf">ProcessTransition</span><span class="p">(</span><span class="n">OrderStateMachine</span><span class="p">.</span><span class="n">OrderTrigger</span><span class="p">.</span><span class="n">Deliver</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="how-it-works">How It Works</h3>

<ol>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">OrderStateMachine</code> class encapsulates state logic, making the main program cleaner.</p>
  </li>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">CurrentState</code> property tracks the current order state.</p>
  </li>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">_stateTransitions</code> dictionary holds valid state transitions.</p>
  </li>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">ProcessTransition()</code> method checks for valid transitions and updates the state.</p>
  </li>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">Main</code> method initializes the state machine and processes transitions.</p>
  </li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>State machines are an excellent way to manage complex workflows in a clean, predictable manner. Using a simple dictionary in C#, we can build a flexible and maintainable system to handle order states. By encapsulating state logic in a class, we make our code cleaner, more modular, and easier to maintain. Whether you’re working on UI workflows, business processes, or game logic, state machines provide a structured approach to managing state transitions effectively.</p>]]></content><author><name>Mirnes Mrkaljevic</name></author><category term="cleancode" /><category term="csharp" /><category term="designpatterns" /><category term="statemachine" /><summary type="html"><![CDATA[State machines might sound like a complex topic, but at their core, they’re just a way to manage different states in a system and control how things transition from one state to another.]]></summary></entry></feed>