<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ .NET - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ .NET - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 04:42:53 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/net/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Migrate from ASP.NET Framework to ASP.NET Core ]]>
                </title>
                <description>
                    <![CDATA[ Migrating to ASP.NET Core is a strategic upgrade that improves performance, scalability, and cross-platform support. Instead of a risky full rewrite, you can use an incremental approach, refactor for  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/migrate-from-asp-net-to-asp-net-core/</link>
                <guid isPermaLink="false">6a0d28828837277411cbeeb3</guid>
                
                    <category>
                        <![CDATA[ dotnet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data migration ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gopinath Karunanithi ]]>
                </dc:creator>
                <pubDate>Wed, 20 May 2026 03:20:34 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/b403b6fe-7924-4c2c-8ae2-ce089c743c76.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Migrating to ASP.NET Core is a strategic upgrade that improves performance, scalability, and cross-platform support. Instead of a risky full rewrite, you can use an incremental approach, refactor for dependency injection, and prioritize testing. Start small, migrate APIs first, and gradually transition UI components to ensure a smooth and reliable modernization process.</p>
<p>In this article, you'll learn how to modernize legacy applications by migrating from ASP.NET Framework to ASP.NET Core. This guide covers architectural differences, migration strategies, step-by-step implementation, and best practices for building scalable, high-performance web applications.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-understanding-the-architectural-shift">Understanding the Architectural Shift</a></p>
</li>
<li><p><a href="#heading-key-challenges-in-migration">Key Challenges in Migration</a></p>
</li>
<li><p><a href="#heading-migration-strategies">Migration Strategies</a></p>
</li>
<li><p><a href="#heading-pre-migration-assessment">Pre-Migration Assessment</a></p>
</li>
<li><p><a href="#heading-step-by-step-migration-process">Step-by-Step Migration Process</a></p>
</li>
<li><p><a href="#heading-performance-and-scalability-gains">Performance and Scalability Gains</a></p>
</li>
<li><p><a href="#heading-deployment-modernization">Deployment Modernization</a></p>
</li>
<li><p><a href="#heading-common-pitfalls">Common Pitfalls</a></p>
</li>
<li><p><a href="#heading-real-world-use-cases">Real-World Use Cases</a></p>
</li>
<li><p><a href="#heading-best-practices-checklist">Best Practices Checklist</a></p>
</li>
<li><p><a href="#heading-when-you-should-not-migrate">When You Should NOT Migrate</a></p>
</li>
<li><p><a href="#heading-future-enhancements">Future Enhancements</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>Legacy systems built on the ASP.NET Framework have powered enterprise applications for over a decade. While stable and mature, these systems often struggle to meet modern requirements such as cross-platform deployment, cloud-native scalability, and high-performance workloads. As businesses evolve, the need to modernize these applications becomes unavoidable.</p>
<p>This is where ASP.NET Core comes in. Designed as a lightweight, modular, and high-performance Framework, ASP.NET Core enables developers to build scalable applications that run on Windows, Linux, and macOS.</p>
<p>In this article, we’ll explore a <strong>practical and technical approach</strong> to migrating legacy ASP.NET Framework applications to ASP.NET Core MVC. Instead of focusing on theory, the emphasis will be on <strong>architectural differences, migration strategies, and step-by-step execution</strong>.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before migrating to ASP.NET Core, developers should have a solid understanding of the MVC architecture, C#, and basic .NET development concepts. Familiarity with dependency injection, REST APIs, and Entity Framework will make the migration process significantly easier.</p>
<p>You should also have:</p>
<ul>
<li><p>.NET SDK installed (.NET 6 or later recommended)</p>
</li>
<li><p>Basic knowledge of CLI commands</p>
</li>
<li><p>Experience with NuGet package management</p>
</li>
<li><p>Understanding of IIS or web hosting environments</p>
</li>
<li><p>A version control system such as Git</p>
</li>
</ul>
<p>For enterprise projects, access to staging environments and automated testing pipelines is highly recommended before beginning migration.</p>
<h2 id="heading-understanding-the-architectural-shift"><strong>Understanding the Architectural Shift</strong></h2>
<p>Migrating to ASP.NET Core is not just a version upgrade—it’s a <strong>fundamental architectural shift.</strong></p>
<h3 id="heading-from-monolithic-to-modular"><strong>From Monolithic to Modular</strong></h3>
<p>ASP.NET relies heavily on the System.Web assembly, which tightly couples components such as HTTP handling, session state, and caching. In contrast, ASP.NET Core removes this dependency and introduces a modular middleware pipeline.</p>
<h3 id="heading-built-in-dependency-injection"><strong>Built-in Dependency Injection</strong></h3>
<p>Dependency Injection (DI) in ASP.NET required third-party libraries like Autofac or Ninject. ASP.NET Core includes DI natively, promoting better separation of concerns.</p>
<h3 id="heading-unified-runtime"><strong>Unified Runtime</strong></h3>
<p>ASP.NET Core runs on the modern .NET ecosystem (for example, <code>.NET 6+</code>), which unifies previously fragmented runtimes and improves performance.</p>
<h3 id="heading-configuration-overhaul"><strong>Configuration Overhaul</strong></h3>
<p>Configuration has moved from XML-based <code>web.config</code> files to flexible <code>JSON</code>-based systems like <code>appsettings.json</code>, supporting environment-specific configurations.</p>
<h2 id="heading-key-challenges-in-migration"><strong>Key Challenges in Migration</strong></h2>
<p>Before diving into the process, it’s important to understand the challenges:</p>
<ul>
<li><p><strong>Tightly coupled codebases</strong>: Legacy applications often mix business logic, UI, and data access.</p>
</li>
<li><p><strong>Unsupported APIs</strong>: Some APIs used in ASP.NET are not available in Core.</p>
</li>
<li><p><strong>Third-party dependencies</strong>: Older libraries may not support .NET Core.</p>
</li>
<li><p><strong>Authentication differences</strong>: Forms authentication and legacy identity systems require refactoring.</p>
</li>
<li><p><strong>Large monoliths</strong>: Breaking down large applications is time-consuming.</p>
</li>
</ul>
<p>Ignoring these challenges often leads to failed or incomplete migrations.</p>
<h2 id="heading-migration-strategies"><strong>Migration Strategies</strong></h2>
<p>Choosing the right migration approach is critical.</p>
<h3 id="heading-big-bang-migration"><strong>Big Bang Migration</strong></h3>
<p>This approach involves rewriting the entire application at once.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Clean architecture</p>
</li>
<li><p>No legacy baggage</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>High risk</p>
</li>
<li><p>Long timelines</p>
</li>
<li><p>Requires full regression testing</p>
</li>
</ul>
<p>This approach is rarely recommended for large systems.</p>
<h3 id="heading-incremental-migration-recommended"><strong>Incremental Migration (Recommended)</strong></h3>
<p>The <strong>Strangler Fig Pattern</strong> is commonly used here. New features are built in ASP.NET Core while gradually replacing legacy components.</p>
<p>The Strangler Fig Pattern is named after a vine that grows around a host tree and gradually replaces it over time. In software modernization, this pattern involves building new functionality alongside the existing application and routing specific requests to the new system as components are migrated. Instead of replacing the entire application at once, teams incrementally “strangle” the legacy system until the new <a href="http://ASP.NET">ASP.NET</a> Core application fully takes over.</p>
<p><strong>Benefits:</strong></p>
<ul>
<li><p>Reduced risk</p>
</li>
<li><p>Continuous delivery</p>
</li>
<li><p>Easier debugging</p>
</li>
</ul>
<h3 id="heading-hybrid-approach"><strong>Hybrid Approach</strong></h3>
<p>Run ASP.NET Framework and ASP.NET Core side by side. Certain modules (for example, <code>APIs</code>) can be migrated first while the UI remains unchanged.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Lower migration risk</p>
</li>
<li><p>Minimal disruption</p>
</li>
<li><p>Supports phased rollout</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Increased operational complexity</p>
</li>
<li><p>Additional deployment overhead</p>
</li>
<li><p>Temporary architectural duplication</p>
</li>
</ul>
<p>This approach is especially useful for large enterprise systems where maintaining business continuity is more important than completing the migration quickly.</p>
<h2 id="heading-pre-migration-assessment"><strong>Pre-Migration Assessment</strong></h2>
<p>A successful migration starts with proper planning.</p>
<h3 id="heading-codebase-audit"><strong>Codebase Audit</strong></h3>
<ul>
<li><p>Identify reusable modules</p>
</li>
<li><p>Detect tightly coupled components</p>
</li>
<li><p>Analyze dependencies</p>
</li>
</ul>
<h3 id="heading-tooling-support"><strong>Tooling Support</strong></h3>
<p>Use tools like:</p>
<ul>
<li><p>.NET Portability Analyzer</p>
</li>
<li><p>API analyzers</p>
</li>
</ul>
<p>These help determine compatibility with ASP.NET Core.</p>
<h3 id="heading-define-scope"><strong>Define Scope</strong></h3>
<p>Decide what to migrate first:</p>
<ul>
<li><p>APIs</p>
</li>
<li><p>UI (MVC Views)</p>
</li>
<li><p>Background services</p>
</li>
</ul>
<h2 id="heading-step-by-step-migration-process"><strong>Step-by-Step Migration Process</strong></h2>
<h3 id="heading-step-1-upgrade-existing-application"><strong>Step 1: Upgrade Existing Application</strong></h3>
<p>Ensure your application is running on the latest version of ASP.NET Framework. This minimizes compatibility issues.</p>
<h3 id="heading-step-2-create-a-new-aspnet-core-mvc-project"><strong>Step 2: Create a New</strong> <strong>ASP.NET</strong> <strong>Core MVC Project</strong></h3>
<p>Use the CLI:</p>
<pre><code class="language-shell">dotnet new mvc -n ModernApp
</code></pre>
<p>This creates a clean project with a modern structure:</p>
<ul>
<li><p><code>Program.cs (entry point)</code></p>
</li>
<li><p><code>Controllers/</code></p>
</li>
<li><p><code>Views/</code></p>
</li>
<li><p><code>wwwroot/</code></p>
</li>
</ul>
<h3 id="heading-step-3-migrate-configuration"><strong>Step 3: Migrate Configuration</strong></h3>
<p>Replace <code>web.config</code> with <code>appsettings.json</code>.</p>
<p><strong>Old (</strong><code>web.config</code><strong>):</strong></p>
<pre><code class="language-xml">&lt;appSettings&gt;
  &lt;add key="ApiUrl" value="https://api.example.com" /&gt;
&lt;/appSettings&gt;
</code></pre>
<p><strong>New (</strong><code>appsettings.json</code><strong>):</strong></p>
<pre><code class="language-json">{
  "ApiSettings": {
    "BaseUrl": "https://api.example.com"
  }
}
</code></pre>
<p>Access in code:</p>
<pre><code class="language-csharp">public class HomeController : Controller
{
    private readonly IConfiguration _config;

    public HomeController(IConfiguration config)
    {
        _config = config;
    }

    public IActionResult Index()
    {
        var url = _config["ApiSettings:BaseUrl"];
        return View();
    }
}
</code></pre>
<h3 id="heading-step-4-replace-globalasax-with-middleware"><strong>Step 4: Replace</strong> <code>Global.asax</code> <strong>with</strong> <code>Middleware</code></h3>
<p>ASP.NET Core uses a middleware pipeline instead of lifecycle events.</p>
<pre><code class="language-csharp">var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseRouting();
app.UseAuthorization();

app.MapControllers();

app.Run();
</code></pre>
<p>This pipeline provides more control and flexibility compared to the old event-driven model.</p>
<h3 id="heading-step-5-migrate-controllers-and-views"><strong>Step 5: Migrate Controllers and Views</strong></h3>
<p>Controller logic remains similar, but return types change.</p>
<p><strong>ASP.NET</strong> <strong>Framework:</strong></p>
<pre><code class="language-csharp">public ActionResult Index()
{
    return View();
}
</code></pre>
<p><strong>ASP.NET</strong> <strong>Core:</strong></p>
<pre><code class="language-csharp">public IActionResult Index()
{
    return View();
}
</code></pre>
<p>Views (Razor) require minor updates, especially for tag helpers.</p>
<h3 id="heading-step-6-implement-dependency-injection"><strong>Step 6: Implement Dependency Injection</strong></h3>
<p>Replace tightly coupled services with DI.</p>
<pre><code class="language-csharp">public interface IProductService
{
    List&lt;string&gt; GetProducts();
}

public class ProductService : IProductService
{
    public List&lt;string&gt; GetProducts()
    {
        return new List&lt;string&gt; { "Laptop", "Phone" };
    }
}
</code></pre>
<p>Register service:</p>
<pre><code class="language-csharp">builder.Services.AddScoped&lt;IProductService, ProductService&gt;();
</code></pre>
<p>Use in controller:</p>
<pre><code class="language-csharp">public class ProductController : Controller
{
    private readonly IProductService _service;

    public ProductController(IProductService service)
    {
        _service = service;
    }

    public IActionResult Index()
    {
        var products = _service.GetProducts();
        return View(products);
    }
}
</code></pre>
<h3 id="heading-step-7-migrate-data-access-layer"><strong>Step 7: Migrate Data Access Layer</strong></h3>
<p>Most legacy apps use Entity Framework. In ASP.NET Core, you’ll use Entity Framework Core.</p>
<p><strong>DbContext Example:</strong></p>
<pre><code class="language-csharp">public class AppDbContext : DbContext
{
    public DbSet&lt;Product&gt; Products { get; set; }

    public AppDbContext(DbContextOptions&lt;AppDbContext&gt; options)
        : base(options) { }
}
</code></pre>
<p>Register in <code>Program.cs</code>:</p>
<pre><code class="language-csharp">builder.Services.AddDbContext&lt;AppDbContext&gt;(options =&gt;
    options.UseSqlServer("YourConnectionString"));
</code></pre>
<h3 id="heading-step-8-authentication-and-authorization"><strong>Step 8: Authentication and Authorization</strong></h3>
<p>ASP.NET Core provides flexible authentication mechanisms:</p>
<ul>
<li><p>Cookie-based authentication</p>
</li>
<li><p>JWT tokens</p>
</li>
<li><p>OAuth providers</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-csharp">builder.Services.AddAuthentication("CookieAuth")
    .AddCookie("CookieAuth", config =&gt;
    {
        config.LoginPath = "/Account/Login";
    });
</code></pre>
<h3 id="heading-step-9-testing-and-validation"><strong>Step 9: Testing and Validation</strong></h3>
<p>Testing becomes critical during migration:</p>
<ul>
<li><p>Unit testing (<code>xUnit</code>, <code>NUnit</code>)</p>
</li>
<li><p>Integration testing</p>
</li>
<li><p>Regression testing</p>
</li>
</ul>
<p>Ensure feature parity between old and new systems.</p>
<p>For example, after migrating a legacy ASP.NET Framework API to ASP.NET Core, you can validate behavior using integration tests to ensure responses match the original system.</p>
<p><strong>Example: Integration Test (xUnit)</strong></p>
<pre><code class="language-csharp">[Fact]
public async Task GetProducts_ShouldReturnSuccessStatus()
{
    // Arrange
    var client = _factory.CreateClient();

    // Act
    var response = await client.GetAsync("/api/products");

    // Assert
    response.EnsureSuccessStatusCode();
}
</code></pre>
<p>You can also compare legacy and migrated endpoints side by side during transition:</p>
<ul>
<li><p>/legacy/api/products → ASP.NET Framework</p>
</li>
<li><p>/api/products → ASP.NET Core</p>
</li>
</ul>
<p>Then run parallel requests and validate response consistency in structure, status codes, and data integrity. This helps ensure that migration does not introduce behavioral regressions before fully decommissioning the legacy system.</p>
<h2 id="heading-performance-and-scalability-gains"><strong>Performance and Scalability Gains</strong></h2>
<p>ASP.NET Core introduces significant improvements:</p>
<ul>
<li><p><strong>Kestrel Web Server</strong>: High-performance, cross-platform server</p>
</li>
<li><p><strong>Async-first design</strong>: Efficient request handling</p>
</li>
<li><p><strong>Lower memory usage</strong></p>
</li>
<li><p><strong>Better throughput under load</strong></p>
</li>
</ul>
<p>These improvements make it ideal for microservices and cloud deployments.</p>
<h2 id="heading-deployment-modernization"><strong>Deployment Modernization</strong></h2>
<p>Legacy applications were traditionally tightly coupled to IIS-based hosting environments, which limited flexibility in deployment and scaling. With ASP.NET Core, applications can now be deployed using modern, portable, and automated infrastructure approaches that better support cloud-native development and continuous delivery.</p>
<h3 id="heading-containerization"><strong>Containerization</strong></h3>
<p>Containerization allows applications to be packaged with all their dependencies into a single, portable unit that can run consistently across different environments. With tools like Docker, ASP.NET Core applications can be deployed reliably across development, staging, and production without environment-specific configuration issues. This approach also simplifies scaling and rollback strategies in distributed systems.</p>
<pre><code class="language-dockerfile">FROM mcr.microsoft.com/dotnet/aspnet:6.0
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "ModernApp.dll"]
</code></pre>
<h3 id="heading-cicd-integration"><strong>CI/CD Integration</strong></h3>
<p>Continuous Integration and Continuous Deployment (CI/CD) pipelines automate the process of building, testing, and deploying applications. In migration scenarios, CI/CD becomes especially important because it ensures that both legacy and modernized components remain stable during incremental rollout. Platforms like GitHub Actions and Azure DevOps help teams validate changes quickly and reduce the risk of regression when transitioning from ASP.NET Framework to ASP.NET Core.</p>
<p>Using CI/CD also enables:</p>
<ul>
<li><p>Faster release cycles during phased migration</p>
</li>
<li><p>Automated testing of migrated modules</p>
</li>
<li><p>Safe rollback in case of deployment failures</p>
</li>
</ul>
<p>This ensures automated builds, tests, and deployments.</p>
<h2 id="heading-common-pitfalls"><strong>Common Pitfalls</strong></h2>
<h3 id="heading-underestimating-complexity"><strong>Underestimating Complexity</strong></h3>
<p>Migration is not just about converting code—it involves rethinking the entire architecture. Differences in middleware, configuration, and hosting models mean teams must redesign key components rather than simply port them.</p>
<h3 id="heading-ignoring-dependencies"><strong>Ignoring Dependencies</strong></h3>
<p>Many legacy applications rely on third-party libraries that may not support ASP.NET Core. Failing to assess compatibility early can lead to blockers, forcing last-minute rewrites or replacements.</p>
<h3 id="heading-skipping-incremental-approach"><strong>Skipping Incremental Approach</strong></h3>
<p>Attempting a full “Big Bang” migration increases risk and often results in delays or failures. An incremental strategy allows teams to validate changes gradually and maintain system stability throughout the transition.</p>
<h3 id="heading-poor-testing"><strong>Poor Testing</strong></h3>
<p>Insufficient testing can introduce critical bugs into production systems. Comprehensive unit, integration, and regression testing are essential to ensure the new application matches the behavior of the legacy system.</p>
<h2 id="heading-real-world-use-cases"><strong>Real-World Use Cases</strong></h2>
<h3 id="heading-enterprise-erp-modernization"><strong>Enterprise ERP Modernization</strong></h3>
<p>Large ERP systems built on ASP.NET often become difficult to scale and maintain due to tightly coupled architectures. Migrating these systems to ASP.NET Core allows organizations to modularize components, improve performance, and enable cloud deployment. This transition also makes it easier to introduce microservices and modern DevOps practices.</p>
<h3 id="heading-e-commerce-platform-scaling"><strong>E-commerce Platform Scaling</strong></h3>
<p>E-commerce applications require high availability and the ability to handle traffic spikes. By moving to ASP.NET Core, businesses can take advantage of asynchronous processing, improved request handling, and container-based scaling. This ensures faster page loads, better user experience, and the ability to handle peak demand efficiently.</p>
<h3 id="heading-api-first-backend-transformation"><strong>API-First Backend Transformation</strong></h3>
<p>Modern applications increasingly adopt an API-first approach, where the backend is designed as a set of independent services. ASP.NET Core simplifies building RESTful APIs with better routing, serialization, and performance. Organizations often migrate APIs first to establish a scalable backend, then gradually transition UI layers to align with the new architecture.</p>
<h2 id="heading-best-practices-checklist"><strong>Best Practices Checklist</strong></h2>
<h3 id="heading-start-with-a-small-module"><strong>Start with a Small Module</strong></h3>
<p>Instead of migrating the entire application at once, begin with a low-risk module such as a reporting feature or internal API. This helps teams understand the migration process, identify challenges early, and build confidence before scaling the effort across the system.</p>
<h3 id="heading-use-incremental-migration"><strong>Use Incremental Migration</strong></h3>
<p>An incremental approach, such as the Strangler Fig pattern, allows legacy and modern systems to coexist during the transition. This reduces downtime, minimizes risk, and ensures continuous delivery while gradually replacing legacy components with ASP.NET Core services.</p>
<h3 id="heading-refactor-for-dependency-injection-early"><strong>Refactor for Dependency Injection Early</strong></h3>
<p>Dependency injection is a core principle in ASP.NET Core, and adopting it early simplifies future development. Refactoring tightly coupled components into loosely coupled services improves testability, maintainability, and overall code quality during and after migration.</p>
<h3 id="heading-monitor-performance-continuously"><strong>Monitor Performance Continuously</strong></h3>
<p>Performance should be tracked throughout the migration process using logging and monitoring tools. Comparing metrics between the legacy system and the new implementation helps validate improvements and ensures that performance regressions are identified and addressed quickly.</p>
<h3 id="heading-maintain-backward-compatibility"><strong>Maintain Backward Compatibility</strong></h3>
<p>During migration, it is essential to ensure that existing clients and integrations continue to function correctly. Maintaining backward compatibility through versioned APIs or adapters allows a smooth transition without disrupting users or dependent systems.</p>
<h2 id="heading-when-you-should-not-migrate">When You Should NOT Migrate</h2>
<p>Migration is not always the right decision. If a legacy application is stable, rarely updated, and meeting business requirements, a full migration may not justify the cost and engineering effort. Some enterprise systems also depend heavily on Windows-specific technologies or third-party libraries that have no ASP.NET Core equivalents.</p>
<p>You should avoid immediate migration when:</p>
<ul>
<li><p>Business risk outweighs modernization benefits</p>
</li>
<li><p>The application is nearing retirement</p>
</li>
<li><p>Critical dependencies are unsupported in .NET Core</p>
</li>
<li><p>The team lacks resources for testing and refactoring</p>
</li>
<li><p>Downtime or instability could severely impact operations</p>
</li>
</ul>
<p>In such cases, maintaining the existing system while gradually modernizing specific modules may be a more practical strategy.</p>
<h2 id="heading-future-enhancements"><strong>Future Enhancements</strong></h2>
<p>As organizations continue modernizing applications with ASP.NET Core, future enhancements often focus on scalability, automation, and cloud-native development practices. Migrating to ASP.NET Core creates a foundation that makes it easier to adopt newer architectural patterns and infrastructure technologies.</p>
<h3 id="heading-microservices-adoption"><strong>Microservices Adoption</strong></h3>
<p>After migration, many organizations gradually break large monolithic systems into microservices. This improves scalability, fault isolation, and independent deployment of application components while enabling faster development cycles.</p>
<h3 id="heading-cloud-native-deployment"><strong>Cloud-Native Deployment</strong></h3>
<p>ASP.NET Core applications are well-suited for deployment on cloud platforms such as Microsoft Azure, Amazon Web Services (AWS), and Google Cloud. Future enhancements may include auto-scaling, serverless workloads, and managed container orchestration.</p>
<h3 id="heading-container-orchestration-with-kubernetes"><strong>Container Orchestration with Kubernetes</strong></h3>
<p>Containerized ASP.NET Core applications can be managed using Kubernetes for automated scaling, service discovery, and high availability. This is especially useful for enterprise-grade distributed systems.</p>
<h3 id="heading-advanced-observability-and-monitoring"><strong>Advanced Observability and Monitoring</strong></h3>
<p>Modern systems increasingly integrate observability platforms such as centralized logging, distributed tracing, and performance monitoring. Tools like Prometheus and Grafana help teams proactively detect issues and optimize performance.</p>
<h3 id="heading-api-gateway-and-service-mesh-integration"><strong>API Gateway and Service Mesh Integration</strong></h3>
<p>As applications evolve into distributed architectures, API gateways and service meshes become valuable for traffic management, authentication, and security. This enhances communication between services while improving resilience and governance.</p>
<h3 id="heading-ai-assisted-development-and-automation"><strong>AI-Assisted Development and Automation</strong></h3>
<p>Modern .NET ecosystems increasingly integrate AI-powered coding assistants, automated testing, and intelligent CI/CD pipelines. These tools can reduce development time, improve code quality, and simplify long-term maintenance of migrated applications.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Migrating from ASP.NET Framework to ASP.NET Core MVC is a strategic modernization effort, not just a technical upgrade. While the process involves challenges—ranging from architectural changes to dependency issues—the long-term benefits are substantial.</p>
<p>By adopting an incremental approach, leveraging modern tools, and focusing on clean architecture, organizations can successfully transition to a platform built for <strong>performance, scalability, and cloud-native development</strong>.</p>
<p>ASP.NET Core is not just the future of .NET—it’s the foundation for building resilient, modern applications in today’s distributed world.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Improve Developer Experience in Microservices Applications with .NET Aspire ]]>
                </title>
                <description>
                    <![CDATA[ Since the advent of microservices, development teams have gained the flexibility to deploy services independently, without coordinating with the entire engineering organization. Bug fixes can be released in isolation without full regression testing, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/improve-developer-experience-with-net-aspire/</link>
                <guid isPermaLink="false">68fb8c95d81014dabb030226</guid>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ developer experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ dotnet ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Opaluwa Emidowojo ]]>
                </dc:creator>
                <pubDate>Fri, 24 Oct 2025 14:26:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761315727860/7321f413-ec87-47a8-b194-523c026f495b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Since the advent of microservices, development teams have gained the flexibility to deploy services independently, without coordinating with the entire engineering organization. Bug fixes can be released in isolation without full regression testing, and multiple teams can ship updates simultaneously, sometimes ten or more deploys a day per team.</p>
<p>But we rarely talk about the downsides of microservices. In medium to large-scale systems, the number of services can grow quickly. Netflix reportedly runs over seven hundred microservices, and Uber manages more than two thousand. That kind of scale introduces a lot of moving parts, testing complexity, and debugging challenges across service boundaries. And all of this can severely impact developer experience (DX).</p>
<p>Recently, I came across a new framework called <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview"><strong>.NET Aspire</strong></a>, which dramatically simplifies local microservices development. Aspire handles service discovery, configuration management, and observability for distributed applications, giving you a complete view of your system through a built-in dashboard. This results in a much simpler, smoother local development experience compared to manually wiring up multiple services. In this guide, we'll explore how Aspire works and how it can help improve developer experience in microservices-based systems.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we begin, ensure you have the following installed:</p>
<ul>
<li><p><a target="_blank" href="https://dotnet.microsoft.com/download"><strong>.NET 8 SDK</strong></a> <strong>or later</strong></p>
</li>
<li><p><a target="_blank" href="https://www.docker.com/products/docker-desktop/"><strong>Docker Desktop</strong></a></p>
<ul>
<li><p>Aspire uses Docker to run dependencies like Redis, PostgreSQL, and so on.</p>
</li>
<li><p>Ensure Docker is running before starting</p>
</li>
</ul>
</li>
<li><p><strong>Visual Studio 2022 (v17.9+)</strong> or <strong>Visual Studio Code</strong> with C# Dev Kit</p>
</li>
<li><p><strong>Basic understanding of:</strong></p>
<ul>
<li><p>C# and .NET development</p>
</li>
<li><p>Microservices architecture concepts</p>
</li>
<li><p>REST APIs and service communication</p>
</li>
</ul>
</li>
</ul>
<p><strong>Optional but Recommended:</strong></p>
<ul>
<li><p>Familiarity with Docker and containerization</p>
</li>
<li><p>Experience with distributed application development</p>
</li>
<li><p>Knowledge of observability concepts (logging, tracing, metrics)</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-developer-experience-in-microservices">Understanding Developer Experience in Microservices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-introducing-net-aspire">Introducing .NET Aspire</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-net-aspire-in-your-project">How to Set Up .NET Aspire in Your Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-this-matters-for-developer-experience">Why This Matters for Developer Experience</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-framework-how-to-adopt-net-aspire-incrementally">Framework: How to Adopt .NET Aspire Incrementally</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-net-aspire-dashboard">How to Use the .NET Aspire Dashboard</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-scenarios-solving-real-world-dx-challenges-with-net-aspire">Practical Scenarios: Solving Real-World DX Challenges with .NET Aspire</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-going-further">Going Further</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-takeaways-and-when-to-use-net-aspire">Key Takeaways and When to Use .NET Aspire</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-and-when-not-to-use-net-aspire">When (and When Not) to Use .NET Aspire</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-understanding-developer-experience-in-microservices"><strong>Understanding Developer Experience in Microservices</strong></h2>
<p>When people talk about DX, they often think of it as tooling or ergonomics, things like good documentation, fast build times, and clean APIs. But in distributed systems, DX becomes much broader. It’s about how easily developers can set up, run, and reason about the systems they’re building.</p>
<p>In a monolithic application, starting your development environment might mean running a single command like <code>dotnet run</code>. But in a microservices-based system, you might need to start multiple APIs, databases, background workers, and queues, all with specific configuration dependencies. This extra overhead doesn’t just slow you down, it breaks your focus and adds friction to daily development.</p>
<p>Over time, that friction compounds.</p>
<ul>
<li><p>Onboarding new developers becomes slower.</p>
</li>
<li><p>Debugging across service boundaries gets harder.</p>
</li>
<li><p>Teams spend more time managing environments than writing features.</p>
</li>
</ul>
<p>That’s why DX is so important in microservices architectures. It is not just about developer happiness, it’s about velocity, consistency, and confidence. If your local environment isn’t easy to run or reason about, every other process in your development lifecycle suffers.</p>
<p>This is where orchestration frameworks like <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview"><strong>.NET Aspire</strong></a> start to make a real difference. They handle the complexity of coordinating services, so developers can focus on building and iterating faster, the way modern software development is meant to work.</p>
<h2 id="heading-introducing-net-aspire"><strong>Introducing .NET Aspire</strong></h2>
<p>As microservice systems grow, local development environments often become a patchwork of scripts, Docker Compose files, and manual setup steps. Each developer ends up managing their own version of “how to get things running,” and small differences in configuration can lead to big inconsistencies across teams.</p>
<p><strong>.NET Aspire</strong> is an orchestration framework designed to simplify this process. It provides a way to define, configure, and run your distributed applications as a single unit, directly within your .NET solution.</p>
<p>In practical terms, Aspire helps developers by handling three key areas automatically:</p>
<ol>
<li><p><strong>Service Orchestration</strong><br> Aspire can start multiple projects (APIs, workers, databases, and so on) in the correct order. It takes care of service dependencies so that, for example, your API doesn’t try to start before the database it depends on is ready.</p>
</li>
<li><p><strong>Configuration Management</strong><br> Instead of juggling dozens of <code>appsettings.json</code> files or environment variables, Aspire provides a centralized configuration model. It shares connection strings, ports, and environment settings across services in a consistent way.</p>
</li>
<li><p><strong>Observability and Insights</strong><br> Aspire includes built-in OpenTelemetry support and a dashboard that gives you real-time visibility into your running services, including their health, logs, and endpoints. This makes debugging and local monitoring much easier.</p>
</li>
</ol>
<p>In many ways, Aspire does for services what Kubernetes does for containers, but with a sharper focus on local development and developer experience. It’s not meant to replace your production orchestration tools, it’s designed to make your everyday development smoother, faster, and less error-prone.</p>
<h2 id="heading-how-to-set-up-net-aspire-in-your-project"><strong>How to Set Up .NET Aspire in Your Project</strong></h2>
<p>We'll create a microservices setup and watch Aspire orchestrate it with minimal code. Make sure you're running .NET 8 or later. Aspire requires it.</p>
<p><strong>Create a New Aspire Project</strong></p>
<p>Start by creating a new Aspire app host using the .NET CLI:</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">new</span> aspire-app -n MyCompany.AppHost
</code></pre>
<p>This command creates a new Aspire “host” project, the entry point that orchestrates your other microservices, APIs, and dependencies.</p>
<p>You’ll notice that the generated project contains a <code>Program.cs</code> file with an <code>AppHostBuilder</code>. This builder acts as the control center for your distributed system.</p>
<p><strong>Add Your Microservices</strong></p>
<p>You can now reference your existing projects or create new ones directly in the same solution. For example:</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">new</span> webapi -n CatalogService
dotnet <span class="hljs-keyword">new</span> webapi -n OrderService
dotnet <span class="hljs-keyword">new</span> worker -n NotificationWorker
</code></pre>
<p>Then, add them to your Aspire host by editing <code>Program.cs</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> builder = DistributedApplication.CreateBuilder(args);

<span class="hljs-keyword">var</span> catalog = builder.AddProject&lt;Projects.CatalogService&gt;(<span class="hljs-string">"catalog"</span>);
<span class="hljs-keyword">var</span> order = builder.AddProject&lt;Projects.OrderService&gt;(<span class="hljs-string">"order"</span>)
                   .WaitFor(catalog); <span class="hljs-comment">// ensure this starts after CatalogService</span>
<span class="hljs-keyword">var</span> notifications = builder.AddProject&lt;Projects.NotificationWorker&gt;(<span class="hljs-string">"notifications"</span>);

builder.Build().Run();
</code></pre>
<p>In this example:</p>
<ul>
<li><p><code>AddProject</code> registers each service with Aspire.</p>
</li>
<li><p><code>.WaitFor()</code> enforces startup dependencies (for example, <code>OrderService</code> depends on <code>CatalogService</code>).</p>
</li>
<li><p>Aspire takes care of starting these services in the right order, sharing environment variables, and managing ports automatically.</p>
</li>
</ul>
<p><strong>Run All Services with One Command</strong></p>
<p>Now, from your app host directory, run:</p>
<pre><code class="lang-csharp">dotnet run
</code></pre>
<p>Aspire will:</p>
<ul>
<li><p>Start all the registered services.</p>
</li>
<li><p>Allocate available ports.</p>
</li>
<li><p>Inject shared configurations.</p>
</li>
<li><p>Launch a local dashboard showing service health, endpoints, and logs.</p>
</li>
</ul>
<p>You should see output like this:</p>
<pre><code class="lang-csharp">Starting CatalogService...
Starting OrderService...
Starting NotificationWorker...
AppHost running <span class="hljs-keyword">on</span> http:<span class="hljs-comment">//localhost:18888</span>
</code></pre>
<p>And when you open the dashboard in your browser, you’ll see all your services, their statuses, and links to their APIs.</p>
<p><strong>Add a Local Database (Optional)</strong></p>
<p>To show how Aspire handles dependencies, let’s add a PostgreSQL container:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> db = builder.AddPostgres(<span class="hljs-string">"postgres"</span>);
builder.AddProject&lt;Projects.CatalogService&gt;(<span class="hljs-string">"catalog"</span>)
       .WithReference(db); <span class="hljs-comment">// injects connection string automatically</span>
</code></pre>
<p>Now when you run the app, Aspire will start PostgreSQL first, generate a connection string, and pass it to <code>CatalogService</code>. No manual setup or <code>.env</code> files required.</p>
<h2 id="heading-why-this-matters-for-developer-experience"><strong>Why This Matters for Developer Experience</strong></h2>
<p>Before Aspire, getting your local environment running meant opening multiple terminals, waiting around for databases to start, and copying connection strings between projects. With Aspire, it's just one command. Everything starts automatically, configuration is shared across services, and you get observability built in. That's the developer experience win. Less time fighting your setup, more time actually coding.</p>
<h2 id="heading-framework-how-to-adopt-net-aspire-incrementally"><strong>Framework: How to Adopt .NET Aspire Incrementally</strong></h2>
<p>If you’re considering trying Aspire in your own team, you don’t have to migrate everything at once. In fact, the best approach is incremental adoption. Start small and expand gradually.</p>
<p>Here’s a simple framework you can follow:</p>
<p><strong>Step 1: Start Small</strong></p>
<p>Create an Aspire host and connect one or two key services.<br>This helps your team understand the orchestration flow before scaling up.</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">new</span> aspire-app -n MyCompany.AppHost
</code></pre>
<p><strong>Step 2: Add Dependencies Incrementally</strong></p>
<p>As you grow, include more services and use <code>.WaitFor()</code> to define dependencies and startup order.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> builder = DistributedApplication.CreateBuilder(args);

<span class="hljs-keyword">var</span> db = builder.AddPostgres(<span class="hljs-string">"postgres"</span>);
builder.AddProject&lt;Projects.CatalogService&gt;(<span class="hljs-string">"catalog"</span>)
       .WithReference(db);
builder.AddProject&lt;Projects.ApiGateway&gt;(<span class="hljs-string">"gateway"</span>)
       .WaitFor(<span class="hljs-string">"catalog"</span>);

builder.Build().Run();
</code></pre>
<p><strong>Step 3: Integrate Observability</strong></p>
<p>Leverage Aspire’s built-in <strong>OpenTelemetry</strong> integration for metrics and traces. You’ll instantly gain better insight into service interactions even without external tools.</p>
<p><strong>Step 4: Share Your Setup</strong></p>
<p>Commit your Aspire host to source control so every developer uses the same setup.<br>This ensures consistency across environments, reducing the classic “works on my machine” problem.</p>
<p><strong>Note</strong>: Aspire doesn’t require a full rewrite. It works great as a starting layer while your team continues evolving your existing orchestration setup.</p>
<h2 id="heading-how-to-use-the-net-aspire-dashboard"><strong>How to Use the .NET Aspire Dashboard</strong></h2>
<p>One of the standout features of .NET Aspire is its built-in dashboard, which gives you real-time visibility into your microservices while they run locally.</p>
<p>When you start your Aspire app host with <code>dotnet run</code>, it automatically spins up a local dashboard (by default at <a target="_blank" href="http://localhost:18888"><code>http://localhost:18888</code></a>). This dashboard provides a centralized view of all your services — APIs, databases, background workers, and any connected dependencies.</p>
<p>Here’s what you’ll find inside:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761073706691/bf60d044-4e73-4fdf-a276-a41f58d48fab.png" alt="Screenshot of the &quot;Resources&quot; view in the .NET Aspire dashboard named testhost. It shows three running resources, cache, apiservice, and webfrontend, each listed with their state, start time, source, and URLs. The cache service uses a Redis image from Docker Hub, while apiservice and webfrontend reference local project files (AspireSample.ApiService.csproj and AspireSample.Web.csproj). All three resources show a “Running” status with localhost URLs for access." class="image--center mx-auto" width="1280" height="400" loading="lazy"></p>
<h3 id="heading-service-overview"><strong>Service Overview</strong></h3>
<p>The dashboard home page lists every service in your distributed application. For each one, you can see:</p>
<ul>
<li><p><strong>Name and type</strong> (for example, cache, apiservice, webfrontend)</p>
</li>
<li><p><strong>Current state</strong> (Running, Starting, Stopped)</p>
</li>
<li><p><strong>Source</strong></p>
</li>
<li><p><strong>Port and endpoint</strong> information</p>
</li>
<li><p><strong>Startup time</strong> and uptime</p>
</li>
<li><p><strong>Logs and metrics shortcuts</strong></p>
</li>
</ul>
<p>This immediately replaces the need to track multiple terminal windows or scroll through dozens of logs just to confirm everything started correctly.</p>
<p>The dashboard automatically detects unhealthy or failed services and highlights them, so you can identify startup issues early.</p>
<h3 id="heading-navigating-to-endpoints"><strong>Navigating to Endpoints</strong></h3>
<p>Each service card includes quick links to its exposed endpoint, providing easy access to relevant tools and interfaces. For example, APIs may include links to Swagger UI or Scalar, databases may link to pgAdmin or similar management tools, and internal services may offer links to custom dashboards.</p>
<p>This setup allows users to test APIs or verify database connections directly from the dashboard without needing to remember specific ports or manually construct URLs.</p>
<h3 id="heading-real-time-logs"><strong>Real-Time Logs</strong></h3>
<p>Clicking into a specific service opens a detailed view showing real-time logs streamed directly from that service.</p>
<p>This is especially helpful when debugging startup issues or service interactions. Instead of running <code>dotnet run</code> in separate terminals, you can view logs for all your services in one place, color-coded and timestamped for clarity.</p>
<h3 id="heading-observability-built-in-opentelemetry"><strong>Observability Built-In (OpenTelemetry)</strong></h3>
<p>Aspire includes OpenTelemetry by default, which means that even without additional configuration, you automatically gain access to several powerful observability features. These include distributed traces across service boundaries, metrics for performance monitoring, and correlated logs that help track requests spanning multiple services.</p>
<p>For teams already using tools like Grafana, Jaeger, or SigNoz, Aspire can export this telemetry data to your preferred observability platform with minimal setup.</p>
<p>With tracing enabled, you can follow a request as it travels from your API to your database, through background workers, and back, all from within the dashboard.</p>
<h3 id="heading-why-the-dashboard-improves-developer-experience"><strong>Why the Dashboard Improves Developer Experience</strong></h3>
<p>Without Aspire, running a local microservices environment typically requires managing multiple terminal windows, tracking ports manually, and searching through log files to diagnose failures.</p>
<p>Aspire consolidates these tasks into a single visual interface where developers can view all services, check dependencies, inspect logs, and monitor system health directly from the browser.</p>
<p>This integrated environment enables faster debugging, maintains developer focus, and simplifies work with complex systems by reducing the overhead of manual coordination.</p>
<h2 id="heading-practical-scenarios-solving-real-world-dx-challenges-with-net-aspire"><strong>Practical Scenarios: Solving Real-World DX Challenges with .NET Aspire</strong></h2>
<p>So far, we have looked at how Aspire works and what it provides out of the box. But to really understand its impact on developer experience, let’s go through a few real-world pain points that almost every team building with microservices has faced, and how Aspire helps solve them.</p>
<h3 id="heading-starting-multiple-services-in-the-right-order"><strong>Starting Multiple Services in the Right Order</strong></h3>
<p><strong>The Problem:</strong> In most microservices setups, service startup order matters. For instance, your API Gateway might depend on the User Service and Catalog Service, which both depend on a Database.<br>If you start these in the wrong order, the gateway fails to connect, and you end up restarting services manually until everything stabilizes.</p>
<p><strong>How Aspire Solves It:</strong> Aspire provides a simple way to express dependencies using <code>.WaitFor()</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> builder = DistributedApplication.CreateBuilder(args);

<span class="hljs-keyword">var</span> db = builder.AddPostgres(<span class="hljs-string">"postgres"</span>);
<span class="hljs-keyword">var</span> user = builder.AddProject&lt;Projects.UserService&gt;(<span class="hljs-string">"user"</span>)
                  .WithReference(db);

<span class="hljs-keyword">var</span> catalog = builder.AddProject&lt;Projects.CatalogService&gt;(<span class="hljs-string">"catalog"</span>)
                     .WithReference(db);

<span class="hljs-keyword">var</span> gateway = builder.AddProject&lt;Projects.ApiGateway&gt;(<span class="hljs-string">"gateway"</span>)
                     .WaitFor(user)
                     .WaitFor(catalog);

builder.Build().Run();
</code></pre>
<p>Aspire automatically ensures that each service only starts after the services it depends on are fully ready.<br>No more manual sequencing or “start this one first” instructions in your <code>README</code>.</p>
<h3 id="heading-port-conflicts-and-configuration-drift"><strong>Port Conflicts and Configuration Drift</strong></h3>
<p><strong>The Problem:</strong> Developers often encounter the dreaded “Port 5000 is already in use” or spend time editing configuration files to avoid conflicts. Over time, local setups diverge across the team, making onboarding and debugging harder.</p>
<p><strong>How Aspire Solves It:</strong> Aspire dynamically manages ports and configuration at runtime. Each service gets a unique port assignment, and Aspire automatically shares connection information across services.</p>
<p>You can still set explicit ports when needed:</p>
<pre><code class="lang-csharp">builder.AddProject&lt;Projects.Frontend&gt;(<span class="hljs-string">"frontend"</span>)
       .WithHttpEndpoint(port: <span class="hljs-number">5173</span>);
</code></pre>
<p>This removes guesswork, keeps environments consistent, and ensures new developers can clone the repo and start everything without editing config files.</p>
<h3 id="heading-simplifying-new-developer-onboarding"><strong>Simplifying New Developer Onboarding</strong></h3>
<p><strong>The Problem:</strong> For many teams, onboarding means following a long README with dozens of setup steps, manual database migrations, and environment variable configurations. It can take hours, or even days before a new developer can run the system locally.</p>
<p><strong>How Aspire Solves It:</strong> Aspire defines your entire environment in code. That means the setup process becomes as simple as cloning the repository and running one command:</p>
<pre><code class="lang-plaintext">dotnet run
</code></pre>
<p>Aspire will start all necessary services, configure dependencies, and bring up the dashboard for visibility. This transforms onboarding from a multi-hour process into something that can be completed in minutes, with far fewer setup issues.</p>
<h3 id="heading-improving-debugging-and-cross-service-visibility"><strong>Improving Debugging and Cross-Service Visibility</strong></h3>
<p><strong>The Problem:</strong> Debugging in microservices often means jumping between logs, tracing requests across multiple services, or reproducing issues that only appear when several services run together.</p>
<p><strong>How Aspire Solves It:</strong> With built-in observability and the Aspire dashboard, you can view logs across all services in one place, inspect health checks and metrics, and trace requests using OpenTelemetry. This makes it much easier to identify issues across service boundaries and speeds up debugging, especially during integration testing or local development.</p>
<h3 id="heading-running-optional-or-external-services"><strong>Running Optional or External Services</strong></h3>
<p><strong>The Problem:</strong> Sometimes you don’t need to run every service locally. For example, you might connect to a shared staging API or external dependency instead of running a local instance.</p>
<p><strong>How Aspire Solves It:</strong> Aspire lets you make services optional using conditional checks:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">if</span> (Directory.Exists(<span class="hljs-string">"../Frontend"</span>))
{
    builder.AddProject&lt;Projects.Frontend&gt;(<span class="hljs-string">"frontend"</span>);
}
</code></pre>
<p>This makes your setup flexible: you can run a minimal environment for development or a full environment for integration testing, all using the same configuration.</p>
<h3 id="heading-why-these-scenarios-matter"><strong>Why These Scenarios Matter</strong></h3>
<p>Each of these examples solves a specific friction point in the developer experience. Startup complexity, environment drift, onboarding time, and debugging difficulty.</p>
<p>By automating orchestration and configuration, Aspire frees developers from repetitive setup work and lets them focus on building features instead of managing infrastructure.</p>
<h2 id="heading-going-further"><strong>Going Further</strong></h2>
<p>Once you’re comfortable with Aspire’s basics, you can extend it beyond local orchestration to streamline other parts of your workflow.</p>
<ul>
<li><p><strong>Integrate front-end applications</strong><br>  Orchestrate React, Angular, or Node.js apps alongside your .NET services for a unified full-stack setup.</p>
</li>
<li><p><strong>Export telemetry data</strong><br>  Send Aspire’s OpenTelemetry output to platforms like Grafana, Jaeger, or Azure Application Insights for deeper analysis.</p>
</li>
<li><p><strong>Use Aspire in CI/CD pipelines</strong><br>  Bring up full environments for integration or smoke testing during continuous integration runs, all using your existing Aspire configuration.</p>
</li>
<li><p><strong>Explore community examples</strong><br>  Check out the official Aspire samples and templates for advanced orchestration patterns, cloud integration, and observability setups.</p>
</li>
</ul>
<h2 id="heading-key-takeaways-and-when-to-use-net-aspire"><strong>Key Takeaways and When to Use .NET Aspire</strong></h2>
<p>As we’ve seen throughout this guide, .NET Aspire isn’t just another developer tool, it’s a framework built specifically to improve developer experience in microservices-based applications.</p>
<p>By orchestrating all your services in a consistent, declarative way, Aspire helps teams reduce friction, speed up setup, and make local environments more reliable and observable.</p>
<p><strong>Key Takeaways</strong></p>
<ol>
<li><p><strong>Developer Experience (DX) matters as your system grows.</strong><br> Microservices introduce flexibility and scalability, but they also add complexity; multiple services, ports, dependencies, and startup sequences. Without good orchestration, DX quickly degrades.</p>
</li>
<li><p><strong>Aspire simplifies orchestration for local development.</strong><br> It automatically handles service startup, dependencies, configuration sharing, and observability all defined in code, right within your .NET solution.</p>
</li>
<li><p><strong>The Aspire dashboard improves visibility.</strong><br> You get a centralized, real-time view of your entire system; services, logs, health, and endpoints eliminating the need for multiple terminals or manual tracking.</p>
</li>
<li><p><strong>Onboarding new developers becomes faster and smoother.</strong><br> A single <code>dotnet run</code> command can spin up your entire development environment, reducing setup time from hours or days to minutes.</p>
</li>
<li><p><strong>Built-in observability means better debugging and confidence.</strong><br> With OpenTelemetry integrated out of the box, developers can trace requests, monitor performance, and diagnose issues across services with minimal setup.</p>
</li>
</ol>
<h2 id="heading-when-and-when-not-to-use-net-aspire"><strong>When (and When Not) to Use .NET Aspire</strong></h2>
<p><strong>Use Aspire when:</strong></p>
<p>Aspire makes sense if you're building .NET microservices and tired of complex local setup. It's especially valuable when your team is dealing with environment drift, slow onboarding, or startup sequences that feel like juggling. If you want one command to spin up your entire system, with observability built in from day one, Aspire is worth trying.</p>
<p><strong>You might not need Aspire when:</strong></p>
<p>Aspire might not be worth it if your current setup already works well. Maybe you're using Kubernetes or Docker Compose locally and everything runs smoothly. Or you're building a monolith or single service that doesn't need orchestration. Or your stack has a lot of non-.NET components that would need custom wiring. If your local development is already simple and stable, don't fix what isn't broken.</p>
<p>In other words:<br>Aspire shines in the local development and onboarding phase. Helping developers build, test, and iterate on distributed systems with minimal friction.<br>It’s not meant to replace production orchestrators like Kubernetes but to complement them by improving the developer’s day-to-day workflow.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Developer Experience is often overlooked when teams move to microservices, but it directly impacts productivity, quality, and morale. By using <strong>.NET Aspire</strong>, you can bring order, visibility, and simplicity back to your local development environment.</p>
<p>If you’re looking to streamline your microservices workflow, give Aspire a try. You’ll spend less time fighting your setup and more time building what actually matters; great software.</p>
<p>Ready to get started? Check out the official <a target="_blank" href="https://learn.microsoft.com/dotnet/aspire/">.NET Aspire documentation</a> or clone one of the <a target="_blank" href="https://github.com/dotnet/aspire-samples">sample projects</a> to see it in action.</p>
<p>If you made it to the end of this tutorial, thanks for reading! You can also connect with me on <a target="_blank" href="https://www.linkedin.com/in/emidowojo/">LinkedIn</a> if you’d like to stay in touch.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Get Started with ASP.NET Core and gRPC: A Handbook for Developers ]]>
                </title>
                <description>
                    <![CDATA[ In today's distributed computing landscape, efficient service-to-service communication is crucial for building scalable, high-performance applications. gRPC (Google Remote Procedure Call) has emerged as one of the most powerful frameworks for creatin... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/get-started-with-aspnet-core-and-grpc-handbook/</link>
                <guid isPermaLink="false">689ca3344cac10c21b1f670c</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Csharhp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Isaiah Clifford Opoku ]]>
                </dc:creator>
                <pubDate>Wed, 13 Aug 2025 14:37:40 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755043329753/f5ff4a61-79b7-44f0-9871-9dfef9f8d08a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In today's distributed computing landscape, efficient service-to-service communication is crucial for building scalable, high-performance applications. gRPC (Google Remote Procedure Call) has emerged as one of the most powerful frameworks for creating robust, type-safe APIs that can handle thousands of requests per second with minimal latency.</p>
<p>gRPC is a modern, open-source RPC framework that leverages HTTP/2, Protocol Buffers, and advanced streaming capabilities to deliver exceptional performance. Unlike traditional REST APIs, gRPC offers strongly-typed contracts, automatic code generation, and built-in support for multiple programming languages. This makes it an ideal choice for microservices architectures and cross-platform development.</p>
<p>In this handbook, I’ll take you on a journey from absolute beginner to building production-ready gRPC services with <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core. Whether you're migrating from REST APIs or starting fresh with gRPC, this guide will provide you with practical, hands-on experience and real-world examples.</p>
<p><strong>What you'll learn:</strong></p>
<ul>
<li><p>How to set up your first gRPC service in .NET</p>
</li>
<li><p>How to define service contracts with Protocol Buffers</p>
</li>
<li><p>How to implement unary, server streaming, and client streaming operations</p>
</li>
<li><p>How to build CRUD (Create, Read, Update, Delete) operations</p>
</li>
</ul>
<p>Let's dive in and discover how gRPC can revolutionize your API development experience!</p>
<p>You can find all the code in this <a target="_blank" href="https://github.com/Clifftech123/IsaiahCliffordOpokuBlog"><strong>GitHub Repository</strong></a><strong>.</strong></p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-grpc-overview-and-how-it-works-with-net">gRPC Overview and How It Works with .NET</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-grpc-with-net">How to Set Up gRPC with .NET</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-product-model">How to Create the Product Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-sqlite-database">How to Set Up the SQLite Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-product-protocol-buffers">How to Create Product Protocol Buffers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-crud-operations-services-with-grpc">How to Implement CRUD Operations Services with gRPC</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-grpc-crud-database-operations-with-sqlite">How to Implement gRPC CRUD Database Operations With SQLite</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-grpc-services-with-postman">How to Test gRPC Services with Postman</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-product-creation">How to Test Product Creation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-all-product-operations">How to Test All Product Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h3 id="heading-perquisites">Perquisites</h3>
<p>Before we start, make sure you have the following installed:</p>
<ul>
<li><p><a target="_blank" href="https://dotnet.microsoft.com/download">.NET SDK</a></p>
</li>
<li><p><a target="_blank" href="https://code.visualstudio.com/download">Visual Studio Code</a></p>
</li>
<li><p><a target="_blank" href="https://www.postman.com/downloads/">Postman</a></p>
</li>
</ul>
<h2 id="heading-grpc-overview-and-how-it-works-with-net">gRPC Overview and How It Works with .NET</h2>
<p>gRPC is a high-performance, cross-platform framework that works seamlessly with many technologies, including .NET Core.</p>
<h3 id="heading-why-choose-grpc-with-net">Why choose gRPC with .NET?</h3>
<p>There are many reasons why this is a good combination. First, of all, this combo is up to 8x faster than using REST APIs with JSON. Its strongly-typed contracts also help prevent runtime errors.</p>
<p>It also has built-in support for client, server, and bidirectional streaming, as well as seamless integration across different languages and platforms. Finally, it leverages HTTP/2 for multiplexing and header compression – so as you can see, these two tools are a super effective pair.</p>
<p>To understand in more detail why gRPC is so valuable, let's explore a common real-world scenario.</p>
<h3 id="heading-the-challenge-microservices-communication">The Challenge: Microservices Communication</h3>
<p>Imagine you're building a large e-commerce application. For better maintainability and scalability, you decide to split your monolithic application into smaller, focused services:</p>
<ul>
<li><p><strong>Product Service</strong> – Handles product catalog, inventory, and product management</p>
</li>
<li><p><strong>Authentication Service</strong> – Manages user authentication, authorization, and user profiles</p>
</li>
</ul>
<p>These services need to communicate with each other frequently. For example, before a user can add a product to their cart, the Product Service must verify with the Authentication Service that the user is logged in and has the proper permissions.</p>
<h3 id="heading-traditional-approach-http-rest-apis">Traditional Approach: HTTP REST APIs</h3>
<p>Traditionally, in .NET applications, we solve this inter-service communication using <code>HttpClient</code> to make REST API calls between services. While this works, it comes with several challenges:</p>
<ul>
<li><p>Network failures: API calls can fail unexpectedly, even when everything appears correct</p>
</li>
<li><p>Performance bottlenecks: JSON serialization/deserialization adds overhead</p>
</li>
<li><p>Slow response times: HTTP/1.1 limitations affect performance under high load</p>
</li>
<li><p>Type safety: No compile-time contract validation between services</p>
</li>
<li><p>Verbose payloads: JSON can be bulky compared to binary formats</p>
</li>
</ul>
<h3 id="heading-the-grpc-solution">The gRPC Solution</h3>
<p>This is where gRPC shines. It addresses these challenges by providing some really helpful features in addition to the ones we’ve already discussed above like protocol buffers, code generation for client and server, and more.</p>
<h3 id="heading-when-to-use-grpc-in-net">When to Use gRPC in .NET</h3>
<p>gRPC is particularly beneficial in certain scenarios, but it’s not a great choice in others. Here are some example use cases, as well as some to avoid:</p>
<p><strong>✅ Perfect for:</strong></p>
<ul>
<li><p><strong>Microservices architecture</strong>: High-frequency service-to-service communication</p>
</li>
<li><p><strong>Real-time applications</strong>: Chat applications, live updates, gaming</p>
</li>
<li><p><strong>High-performance APIs</strong>: When speed and efficiency are critical</p>
</li>
<li><p><strong>Polyglot environments</strong>: Services written in different programming languages</p>
</li>
<li><p><strong>Internal APIs</strong>: Backend services that don't need browser compatibility</p>
</li>
</ul>
<p><strong>❌ Consider Alternatives When:</strong></p>
<ul>
<li><p><strong>Browser-based applications</strong>: Limited browser support (use gRPC-Web instead)</p>
</li>
<li><p><strong>Public APIs</strong>: REST might be more familiar to external developers</p>
</li>
<li><p><strong>Simple CRUD operations</strong>: Where REST's simplicity is sufficient</p>
</li>
<li><p><strong>Legacy system integration</strong>: When existing systems only support HTTP/1.1</p>
</li>
</ul>
<h3 id="heading-grpc-vs-rest-a-quick-comparison">gRPC vs REST: A Quick Comparison</h3>
<p>Here’s a quick side-by-side comparison of their main features:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>gRPC</td><td>REST</td></tr>
</thead>
<tbody>
<tr>
<td>Protocol</td><td>HTTP/2</td><td>HTTP/1.1</td></tr>
<tr>
<td>Data Format</td><td>Protocol Buffers (Binary)</td><td>JSON (Text)</td></tr>
<tr>
<td>Performance</td><td>High</td><td>Moderate</td></tr>
<tr>
<td>Browser Support</td><td>Limited (needs gRPC-Web)</td><td>Full</td></tr>
<tr>
<td>Streaming</td><td>Built-in</td><td>Limited</td></tr>
<tr>
<td>Code Generation</td><td>Automatic</td><td>Manual</td></tr>
</tbody>
</table>
</div><p>In this handbook, we'll build a complete Product Management system using gRPC with .NET, demonstrating how to implement efficient service-to-service communication with full CRUD operations.</p>
<h2 id="heading-how-to-set-up-grpc-with-net">How to Set Up gRPC with .NET</h2>
<p>In this tutorial, we'll use Visual Studio Code to build our complete gRPC application. Let's start by creating a new gRPC project using the .NET CLI.</p>
<h3 id="heading-creating-your-first-grpc-project">Creating Your First gRPC Project</h3>
<p>Start by opening your terminal (you can use VS Code's integrated terminal or your system terminal) and navigate to your desired directory where you want to create the project.</p>
<p>Run the following command to create a new gRPC project:</p>
<pre><code class="lang-bash">dotnet new grpc -o ProductGrpc
</code></pre>
<p><strong>What this command does:</strong></p>
<ul>
<li><p><code>dotnet new grpc</code> creates a new project using the gRPC template</p>
</li>
<li><p><code>-o ProductGrpc</code> specifies the output directory name for our project</p>
</li>
</ul>
<p>Next, navigate into the project directory:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ProductGrpc
</code></pre>
<p>Then open the project in Visual Studio Code:</p>
<pre><code class="lang-bash">code .
</code></pre>
<h3 id="heading-understanding-the-project-structure">Understanding the Project Structure</h3>
<p>After running the command, you should see output similar to the following in your terminal, confirming that the project was created successfully:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753873861602/6d135358-2065-40eb-9fe9-9a58bd8dc2eb.png" alt="Vs Code  Initial  Project Structure" width="1971" height="987" loading="lazy"></p>
<p>Let's explore what the .NET gRPC template has generated for us:</p>
<pre><code class="lang-makefile">ProductGrpc/
├── Protos/
│   └── greet.proto          <span class="hljs-comment"># Protocol Buffer definition file</span>
├── Services/
│   └── GreeterService.cs    <span class="hljs-comment"># Sample gRPC service implementation</span>
├── Program.cs               <span class="hljs-comment"># Application entry point</span>
├── ProductGrpc.csproj       <span class="hljs-comment"># Project file</span>
└── appsettings.json         <span class="hljs-comment"># Configuration file</span>
</code></pre>
<p>Key files:</p>
<ul>
<li><p><code>Protos/greet.proto</code>: Defines the service contract using Protocol Buffers</p>
</li>
<li><p><code>Services/GreeterService.cs</code>: Contains the actual service implementation</p>
</li>
<li><p><code>Program.cs</code>: Configures and starts the gRPC server</p>
</li>
<li><p><code>ProductGrpc.csproj</code>: Contains project dependencies and build settings</p>
</li>
</ul>
<h3 id="heading-verifying-the-setup">Verifying the Setup</h3>
<p>Let's make sure everything is working correctly by running the default application:</p>
<pre><code class="lang-yaml"><span class="hljs-string">dotnet</span> <span class="hljs-string">run</span>
</code></pre>
<p>You should see output indicating that the gRPC server is running:</p>
<pre><code class="lang-json">info: Microsoft.Hosting.Lifetime[<span class="hljs-number">14</span>]
      Now listening on: https:<span class="hljs-comment">//localhost:7042</span>
info: Microsoft.Hosting.Lifetime[<span class="hljs-number">0</span>]
      Application started. Press Ctrl+C to shut down.
</code></pre>
<p><strong>🎉 Congratulations!</strong> You've successfully created your first gRPC application using the .NET CLI. The server is now running and ready to accept gRPC requests.</p>
<p>Let's move on to the next section, where we'll start building our Product Management system.</p>
<h2 id="heading-how-to-create-the-product-model">How to Create the Product Model</h2>
<p>Now that we have our gRPC project set up, let's create our Product model. In .NET applications, models represent the data structure and business entities that our application will work with. Think of models as blueprints that define what properties our data objects should have.</p>
<h3 id="heading-understanding-models-in-grpc-applications">Understanding Models in gRPC Applications</h3>
<p>Models serve several important purposes:</p>
<ul>
<li><p><strong>Data structure</strong>: They define the shape and properties of our business entities.</p>
</li>
<li><p><strong>Type safety</strong>: They ensure compile-time validation of our data.</p>
</li>
<li><p><strong>Business logic</strong>: They represent real-world objects in our application.</p>
</li>
<li><p><strong>Database mapping</strong>: They serve as entities for database operations.</p>
</li>
</ul>
<h3 id="heading-creating-the-models-folder">Creating the Models Folder</h3>
<p>Let’s organize our code by creating a dedicated folder for our models called <code>Models</code> in your project root directory.</p>
<p>Inside the Models folder, create a new file called <code>Product.cs</code>.</p>
<p>Your project structure should now look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">ProductGrpc/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Models/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">Product.cs</span>           <span class="hljs-comment"># Our new Product model</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Protos/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Services/</span>
<span class="hljs-string">└──</span> <span class="hljs-string">...</span>
</code></pre>
<h3 id="heading-implementing-the-product-model">Implementing the Product Model</h3>
<p>Add the following code to your <code>Product.cs</code> file:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Models/Product.cs</span>
<span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">ProductGrpc.Models</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Product</span>
    {

        <span class="hljs-keyword">public</span> Guid Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> required <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> required <span class="hljs-keyword">string</span> Description { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Price { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-keyword">public</span> DateTime Created { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = DateTime.UtcNow;

        <span class="hljs-keyword">public</span> DateTime Updated { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = DateTime.UtcNow;
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? Tags { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p>Modern C# features:</p>
<ul>
<li><p><code>required</code> keyword: Ensures properties must be initialized when creating an object</p>
</li>
<li><p><code>string?</code>: Nullable reference type for optional properties</p>
</li>
<li><p>Default values: <code>Created</code> and <code>Updated</code> automatically set to the current UTC</p>
</li>
</ul>
<h3 id="heading-why-use-guid-for-id">Why Use Guid for ID?</h3>
<p>We're using <code>Guid</code> instead of <code>int</code> for our primary key for a few reasons:</p>
<ul>
<li><p><strong>Uniqueness</strong>: Guaranteed to be unique across different systems</p>
</li>
<li><p><strong>Security</strong>: Harder to guess than sequential integers</p>
</li>
<li><p><strong>Distributed systems</strong>: No need for centralized ID generation</p>
</li>
<li><p><strong>Scalability</strong>: Perfect for microservices architecture</p>
</li>
</ul>
<h3 id="heading-namespace-considerations">Namespace Considerations</h3>
<p><strong>Important Note:</strong> If you changed your project name when creating it, make sure your namespace matches your project name. For example:</p>
<ul>
<li><p>If your project is named <code>MyProductService</code>, use <code>namespace MyProductService.Models</code></p>
</li>
<li><p>If your project is named <code>ProductGrpc</code>, use <code>namespace ProductGrpc.Models</code></p>
</li>
</ul>
<p>🎉 <strong>Excellent work!</strong> You've successfully created your first business model that will serve as the foundation for our entire gRPC application.</p>
<h3 id="heading-next-steps">Next Steps</h3>
<p>Now that we have our Product model ready, let's move on to setting up SQLite as our database and configuring Entity Framework Core to handle our data persistence. This will allow us to store and retrieve our Product data efficiently.</p>
<h2 id="heading-how-to-set-up-the-sqlite-database">How to Set Up the SQLite Database</h2>
<p>To persist our product data, we need a database that can handle our CRUD (Create, Read, Update, Delete) operations efficiently. We'll use <strong>SQLite</strong> for this tutorial because it's lightweight, requires no separate server installation, and works perfectly for developing small-to-medium applications.</p>
<h3 id="heading-installing-the-required-packages">Installing the Required Packages</h3>
<p>Before we create our database context, we need to install the necessary Entity Framework Core packages. Open your terminal and make sure you're in the root directory of your project, then run these commands:</p>
<pre><code class="lang-powershell">dotnet add package Microsoft.EntityFrameworkCore.Design
</code></pre>
<pre><code class="lang-powershell">dotnet add package Microsoft.EntityFrameworkCore.Sqlite
</code></pre>
<p>What these packages do:</p>
<ul>
<li><p><strong>Microsoft.EntityFrameworkCore.Design</strong> provides design-time tools for EF Core (migrations, scaffolding)</p>
</li>
<li><p><strong>Microsoft.EntityFrameworkCore.SQLite</strong> is a SQLite database provider for Entity Framework Core</p>
</li>
</ul>
<p>You should see output confirming the packages were added successfully:</p>
<pre><code class="lang-bash">info : PackageReference <span class="hljs-keyword">for</span> <span class="hljs-string">'Microsoft.EntityFrameworkCore.Design'</span> version <span class="hljs-string">'x.x.x'</span> added to file <span class="hljs-string">'ProductGrpc.csproj'</span>.
info : PackageReference <span class="hljs-keyword">for</span> <span class="hljs-string">'Microsoft.EntityFrameworkCore.Sqlite'</span> version <span class="hljs-string">'x.x.x'</span> added to file <span class="hljs-string">'ProductGrpc.csproj'</span>.
</code></pre>
<h3 id="heading-creating-the-database-context">Creating the Database Context</h3>
<p>Now let's create our database context, which acts as a bridge between our .NET objects and the database.</p>
<p>First, create a new folder called <code>Data</code> In your project root. Inside the Data folder, create a file called <code>AppDbContext.cs</code>.</p>
<p>Your project structure should now look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">ProductGrpc/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Data/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">AppDbContext.cs</span>      <span class="hljs-comment"># Our new database context</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Models/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">Product.cs</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Protos/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Services/</span>
<span class="hljs-string">└──</span> <span class="hljs-string">...</span>
</code></pre>
<p>Add the following code to your <code>AppDbContext.cs</code> file:</p>
<pre><code class="lang-cs">
<span class="hljs-comment">// Data/AppDbContext.cs</span>
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> ProductGrpc.Models;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">ProductGrpc.Data</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AppDbContext</span> : <span class="hljs-title">DbContext</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AppDbContext</span>(<span class="hljs-params">DbContextOptions&lt;AppDbContext&gt; options</span>) : <span class="hljs-title">base</span>(<span class="hljs-params">options</span>)</span>
        {
        }

        <span class="hljs-keyword">public</span> DbSet&lt;Product&gt; Products { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p>Let’s understand the key components of DbContext:</p>
<ul>
<li><p><strong>Constructor</strong>: Accepts <code>DbContextOptions</code> for configuration (connection string, provider, and so on)</p>
</li>
<li><p><strong>DbSet Products</strong>: Represents the Products table in our database</p>
</li>
</ul>
<h3 id="heading-registering-the-database-context">Registering the Database Context</h3>
<p>Now we need to register our <code>AppDbContext</code> With the dependency injection container so our application can use it.</p>
<p>Open your <code>Program.cs</code> file and add the database configuration:</p>
<pre><code class="lang-cs"><span class="hljs-comment">// Program.cs</span>

<span class="hljs-keyword">using</span> ProductGrpc.Data;
<span class="hljs-keyword">using</span> ProductGrpc.Services;

<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext&lt;AppDbContext&gt;(opt=&gt; 
    opt.UseSqlite(<span class="hljs-string">"Data Source=ProductGrpc.db "</span>));
<span class="hljs-comment">// Add services to the container.</span>
builder.Services.AddGrpc();

<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// Configure the HTTP request pipeline.</span>
app.MapGrpcService&lt;GreeterService&gt;();
app.MapGet(<span class="hljs-string">"/"</span>, () =&gt; <span class="hljs-string">"Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"</span>);

app.Run();


app.Run();
</code></pre>
<p><code>Data Source=ProductGrpc.db</code> creates a SQLite database file named <code>ProductGrpc.db</code> in your project directory.</p>
<h3 id="heading-creating-and-running-migrations">Creating and Running Migrations</h3>
<p>Now we need to create a migration to generate the database schema based on our Product model.</p>
<p>Start by creating the initial migration:</p>
<pre><code class="lang-powershell">dotnet ef migrations add InitialCreate
</code></pre>
<p>This command will:</p>
<ul>
<li><p>Analyze your models and DbContext</p>
</li>
<li><p>Generate migration files in a <code>Migrations</code> folder</p>
</li>
<li><p>Create SQL commands needed to build your database schema</p>
</li>
</ul>
<p>You should see output like this:</p>
<pre><code class="lang-powershell">Build succeeded.
Done. To undo this action, use <span class="hljs-string">'dotnet ef migrations remove'</span>
</code></pre>
<p>Apply the migration to create the database:</p>
<pre><code class="lang-powershell">dotnet ef database update
</code></pre>
<p>This command will:</p>
<ul>
<li><p>Execute the migration SQL commands</p>
</li>
<li><p>Create the <code>ProductGrpc.db</code> file in your project directory</p>
</li>
<li><p>Set up the Products table with all the correct columns</p>
</li>
</ul>
<p>You should see output confirming the database was created:</p>
<pre><code class="lang-powershell">Build succeeded.
Applying migration <span class="hljs-string">'20240101000000_InitialCreate'</span>.
Done.
</code></pre>
<h3 id="heading-verifying-the-setup-1">Verifying the Setup</h3>
<p>After running the migration, you should see:</p>
<ol>
<li><p>A new <code>Migrations</code> folder in your project with migration files</p>
</li>
<li><p>A <code>ProductGrpc.db</code> file in your project root (this is your SQLite database)</p>
</li>
<li><p>No errors in the terminal output</p>
</li>
</ol>
<p>Your project structure should now look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">ProductGrpc/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Data/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">AppDbContext.cs</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Migrations/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">├──</span> <span class="hljs-string">20240101000000_InitialCreate.cs</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">AppDbContextModelSnapshot.cs</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Models/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">Product.cs</span>
<span class="hljs-string">├──</span> <span class="hljs-string">ProductGrpc.db</span>            <span class="hljs-comment"># Your SQLite database file</span>
<span class="hljs-string">└──</span> <span class="hljs-string">...</span>
</code></pre>
<p>Congratulations! You've successfully installed Entity Framework Core packages, created a database context, registered the context with dependency injection, generated and applied your first migration, and created a working SQLite database. Whew!</p>
<h3 id="heading-whats-next">What's Next?</h3>
<p>Now that our database is set up and ready, we can move on to creating our Protocol Buffer definitions (<code>.proto</code> files) and implementing our gRPC services for CRUD operations.</p>
<h2 id="heading-how-to-create-product-protocol-buffers">How to Create Product Protocol Buffers</h2>
<p>Protocol Buffers (protobuf) are the heart of gRPC communication. They define the structure of your data and services in a language-neutral way, which then gets compiled into native C# code. Protocol Buffers use the efficient <strong>HTTP/2</strong> protocol, making service-to-service communication fast and reliable.</p>
<h3 id="heading-understanding-protocol-buffers-vs-rest-apis">Understanding Protocol Buffers vs REST APIs</h3>
<p>To better understand Protocol Buffers, let's compare them to what you might already know from REST API development.</p>
<p>In REST API development, you typically define your API endpoints using controllers and action methods. The contract between client and server is often documented separately (like with OpenAPI/Swagger), and there's no compile-time guarantee that your documentation matches your actual implementation.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">ApiController</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ProductsController</span> : <span class="hljs-title">ControllerBase</span>
{
    [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"{id}"</span>)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;ProductDto&gt;&gt; GetProduct(<span class="hljs-keyword">int</span> id) { ... }
</code></pre>
<p>With <strong>gRPC,</strong> the service contract is defined first in the <code>.proto</code> file using the <code>service</code> keyword. This contract becomes the single source of truth, and both client and server code are generated from it, ensuring they're always in sync.</p>
<pre><code class="lang-csharp">service ProductService {
  <span class="hljs-function">rpc <span class="hljs-title">GetProduct</span>(<span class="hljs-params">GetProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">GetProductResponse</span>)</span>;
}
</code></pre>
<h3 id="heading-data-transfer-and-serialization">Data Transfer and Serialization</h3>
<p>REST APIs typically use JSON for data transfer, which is human-readable and widely supported. But JSON is text-based, which has a few negatives. First, it has larger payload sizes due to text encoding. It also comes with some runtime parsing overhead. It doesn’t have any built-in schema validation, and there’s a high potential for typos in field names</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-string">"123e4567-e89b-12d3-a456-426614174000"</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Wireless Headphones"</span>,
  <span class="hljs-attr">"price"</span>: <span class="hljs-number">99.99</span>
}
</code></pre>
<p>gRPC instead uses Protocol Buffers, which serialize data into a compact binary format. This provides significantly smaller payloads (up to 6x smaller than JSON), faster serialization/deserialization, strong typing with compile-time validation, and schema evolution without breaking changes.</p>
<h4 id="heading-transport-protocol-differences">Transport Protocol Differences</h4>
<p>REST APIs run over <strong>HTTP/1.1</strong>, which has some limitations:</p>
<ul>
<li><p>One request-response cycle per connection</p>
</li>
<li><p>Text-based headers (larger overhead)</p>
</li>
<li><p>No built-in multiplexing</p>
</li>
<li><p>Limited streaming capabilities</p>
</li>
</ul>
<p>gRPC leverages <strong>HTTP/2</strong>, which offers some advantages:</p>
<ul>
<li><p><strong>Multiplexing</strong>: Multiple requests over a single connection</p>
</li>
<li><p><strong>Header compression</strong>: Reduced overhead with HPACK</p>
</li>
<li><p><strong>Server push</strong>: The Server can initiate streams to clients</p>
</li>
<li><p><strong>Flow control</strong>: Better handling of slow consumers</p>
</li>
</ul>
<h4 id="heading-data-structure-definitions">Data Structure Definitions</h4>
<p>In REST APIs, you define <strong>DTOs (Data Transfer Objects)</strong> as regular classes:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ProductDto</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Price { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>These DTOs exist only in your specific language and need manual synchronization across different services or languages.</p>
<p>In gRPC, you define <strong>Messages</strong> in the proto file:</p>
<pre><code class="lang-csharp">message ProductModel {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">3</span>;
}
</code></pre>
<p>These message definitions are language-agnostic and automatically generate equivalent classes in any supported programming language.</p>
<p>Here's a quick comparison table to summarize these differences:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Service Contracts and Interfaces Service Contracts and Interfaces REST API Concept</td><td>gRPC Equivalent</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td>Interface</td><td>Service</td><td>Defines available operations</td></tr>
<tr>
<td>DTO (Data Transfer Object)</td><td>Message</td><td>Defines a data structure</td></tr>
<tr>
<td>JSON Request/Response</td><td>Binary Protocol Buffer</td><td>Data serialization format</td></tr>
<tr>
<td>HTTP/1.1</td><td>HTTP/2</td><td>Transport protocol</td></tr>
</tbody>
</table>
</div><h3 id="heading-creating-the-product-proto-file">Creating the Product Proto File</h3>
<p>Navigate to the <code>Protos</code> folder in your project and create a new file called <code>product.proto</code>. Make sure the file extension is <code>.proto</code>.</p>
<p>Your project structure should look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">ProductGrpc/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Protos/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">├──</span> <span class="hljs-string">greet.proto</span>          <span class="hljs-comment"># Default template file</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">product.proto</span>        <span class="hljs-comment"># Our new proto file</span>
<span class="hljs-string">└──</span> <span class="hljs-string">...</span>
</code></pre>
<h3 id="heading-setting-up-the-proto-file-header">Setting Up the Proto File Header</h3>
<p>Add the following header to your <code>product.proto</code> file:</p>
<pre><code class="lang-cs">
<span class="hljs-comment">//  Protos/product.proto</span>
syntax = <span class="hljs-string">"proto3"</span>;

option csharp_namespace = <span class="hljs-string">"ProductGrpc"</span>;

package product;
</code></pre>
<p>Here’s what’s going on:</p>
<ul>
<li><p><code>syntax = "proto3"</code>: Specifies that we're using Protocol Buffers version 3</p>
</li>
<li><p><code>option csharp_namespace = "ProductGrpc"</code>: Sets the C# namespace for generated code</p>
</li>
<li><p><code>package product</code>: Defines the protobuf package name</p>
</li>
</ul>
<p><strong>Note:</strong> If you named your project differently, make sure the <code>csharp_namespace</code> matches your project name.</p>
<h3 id="heading-defining-the-product-service">Defining the Product Service</h3>
<p>In gRPC, services define the available operations (similar to interfaces in REST APIs). Add the following service definition:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// :Protos/product.proto</span>
service  ProductsServiceProto {
  <span class="hljs-function">rpc <span class="hljs-title">CreateProduct</span>(<span class="hljs-params">CreateProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">CreateProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">GetProduct</span>(<span class="hljs-params">GetProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">GetProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">ListProducts</span>(<span class="hljs-params">ListProductsRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">ListProductsResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">UpdateProduct</span>(<span class="hljs-params">UpdateProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">UpdateProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">DeleteProduct</span>(<span class="hljs-params">DeleteProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">DeleteProductResponse</span>)</span>;
}
</code></pre>
<p>Service methods explained:</p>
<ul>
<li><p><code>rpc</code>: Defines a remote procedure call</p>
</li>
<li><p><code>CreateProduct</code>: Method name</p>
</li>
<li><p><code>(CreateProductRequest)</code>: Input message type</p>
</li>
<li><p><code>returns (CreateProductResponse)</code>: Output message type</p>
</li>
</ul>
<h3 id="heading-defining-protocol-buffer-messages">Defining Protocol Buffer Messages</h3>
<p>Messages in gRPC are equivalent to DTOs in REST APIs. They define the structure of data being exchanged. Let's create all the messages we need:</p>
<h4 id="heading-product-model-message">Product Model Message:</h4>
<pre><code class="lang-yaml"><span class="hljs-string">//</span> <span class="hljs-string">product.proto</span>
<span class="hljs-string">message</span> <span class="hljs-string">ProductModel</span> {
  <span class="hljs-string">string</span> <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">1</span><span class="hljs-string">;</span>
  <span class="hljs-string">string</span> <span class="hljs-string">name</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span><span class="hljs-string">;</span>
  <span class="hljs-string">string</span> <span class="hljs-string">description</span> <span class="hljs-string">=</span> <span class="hljs-number">3</span><span class="hljs-string">;</span>
  <span class="hljs-string">double</span> <span class="hljs-string">price</span> <span class="hljs-string">=</span> <span class="hljs-number">4</span><span class="hljs-string">;</span>
  <span class="hljs-string">string</span> <span class="hljs-string">created_at</span> <span class="hljs-string">=</span> <span class="hljs-number">5</span><span class="hljs-string">;</span>
  <span class="hljs-string">string</span> <span class="hljs-string">updated_at</span> <span class="hljs-string">=</span> <span class="hljs-number">6</span><span class="hljs-string">;</span>
  <span class="hljs-string">string</span> <span class="hljs-string">tags</span> <span class="hljs-string">=</span> <span class="hljs-number">7</span><span class="hljs-string">;</span>
}
</code></pre>
<h4 id="heading-create-operation-messages">Create Operation Messages:</h4>
<pre><code class="lang-cs"><span class="hljs-comment">// Protos/product.proto</span>
message CreateProductRequest {
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">4</span>;
}

message CreateProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}
</code></pre>
<h4 id="heading-read-operation-messages">Read Operation Messages:</h4>
<pre><code class="lang-cs"><span class="hljs-comment">// Protos/product.proto</span>
message GetProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
}

message GetProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}

message ListProductsRequest {
  int32 page = <span class="hljs-number">1</span>;
  int32 page_size = <span class="hljs-number">2</span>;
}

message ListProductsResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  repeated ProductModel products = <span class="hljs-number">3</span>;
  int32 total_count = <span class="hljs-number">4</span>;
}
</code></pre>
<h4 id="heading-update-operation-messages">Update Operation Messages:</h4>
<pre><code class="lang-cs"> <span class="hljs-comment">// Protos/product.proto</span>
message UpdateProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">4</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">5</span>;
}

message UpdateProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}
</code></pre>
<h4 id="heading-delete-operation-messages">Delete Operation Messages:</h4>
<pre><code class="lang-cs"><span class="hljs-comment">// Protos/product.proto</span>
message DeleteProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
}

message DeleteProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
}
</code></pre>
<h3 id="heading-understanding-protocol-buffer-syntax">Understanding Protocol Buffer Syntax</h3>
<p>There are a few key concepts you should understand about how protocol buffers work:</p>
<ul>
<li><p><strong>Field Numbers</strong>: Each field has a unique number (for example, <code>= 1</code>, <code>= 2</code>) used for binary encoding</p>
</li>
<li><p><strong>Field Types</strong>: <code>string</code>, <code>int32</code>, <code>double</code>, <code>bool</code> are common scalar types</p>
</li>
<li><p><strong>repeated</strong>: Indicates an array/list (for example, <code>repeated ProductModel products</code>)</p>
</li>
<li><p><strong>Message Nesting</strong>: Messages can contain other messages (for example, <code>ProductModel product</code>)</p>
</li>
</ul>
<p>Keep in mind that field numbers must be unique within a message, field numbers 1-15 use 1 byte encoding (more efficient), and you should never reuse field numbers (for backward compatibility).</p>
<h3 id="heading-complete-product-proto-file">Complete Product Proto File</h3>
<p>Here's your complete <code>product.proto</code> file:</p>
<pre><code class="lang-cs"><span class="hljs-comment">// Protos/product.proto</span>
syntax = <span class="hljs-string">"proto3"</span>;

option csharp_namespace = <span class="hljs-string">"ProductGrpc"</span>;

package product;

<span class="hljs-comment">// Product service definition</span>
service  ProductsServiceProto {
  <span class="hljs-function">rpc <span class="hljs-title">CreateProduct</span>(<span class="hljs-params">CreateProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">CreateProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">GetProduct</span>(<span class="hljs-params">GetProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">GetProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">ListProducts</span>(<span class="hljs-params">ListProductsRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">ListProductsResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">UpdateProduct</span>(<span class="hljs-params">UpdateProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">UpdateProductResponse</span>)</span>;
  <span class="hljs-function">rpc <span class="hljs-title">DeleteProduct</span>(<span class="hljs-params">DeleteProductRequest</span>) <span class="hljs-title">returns</span> (<span class="hljs-params">DeleteProductResponse</span>)</span>;
}

<span class="hljs-comment">// Product model message</span>
message ProductModel {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">4</span>;
  <span class="hljs-keyword">string</span> created_at = <span class="hljs-number">5</span>;
  <span class="hljs-keyword">string</span> updated_at = <span class="hljs-number">6</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">7</span>;
}

<span class="hljs-comment">// Create operation messages</span>
message CreateProductRequest {
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">4</span>;
}

message CreateProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}

<span class="hljs-comment">// Read operation messages</span>
message GetProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
}

message GetProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}

message ListProductsRequest {
  int32 page = <span class="hljs-number">1</span>;
  int32 page_size = <span class="hljs-number">2</span>;
}

message ListProductsResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  repeated ProductModel products = <span class="hljs-number">3</span>;
  int32 total_count = <span class="hljs-number">4</span>;
}

<span class="hljs-comment">// Update operation messages</span>
message UpdateProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">4</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">5</span>;
}

message UpdateProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
  ProductModel product = <span class="hljs-number">3</span>;
}

<span class="hljs-comment">// Delete operation messages</span>
message DeleteProductRequest {
  <span class="hljs-keyword">string</span> id = <span class="hljs-number">1</span>;
}

message DeleteProductResponse {
  <span class="hljs-keyword">bool</span> success = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> message = <span class="hljs-number">2</span>;
}
</code></pre>
<h3 id="heading-building-the-project-to-generate-c-code">Building the Project to Generate C# Code</h3>
<p>Now that we've defined our Protocol Buffer contract, we need to build the project to generate the corresponding C# code:</p>
<pre><code class="lang-bash">dotnet build
</code></pre>
<p>This command will compile your <code>.proto</code> files into C# classes, generate client and server code, and create strongly-typed request/response classes.</p>
<p>You should see output confirming the build was successful:</p>
<pre><code class="lang-bash">Restore complete (0.6s)
  ProductGrpc succeeded (9.5s) → bin\Debug\net9.0\ProductGrpc.dll

Build succeeded <span class="hljs-keyword">in</span> 11.1s
</code></pre>
<h3 id="heading-what-gets-generated">What Gets Generated?</h3>
<p>After building, the Protocol Buffer compiler generates several C# files (you won't see them directly, but they're available in your code):</p>
<ul>
<li><p><strong>ProductModel</strong>: C# class representing your product data</p>
</li>
<li><p><strong>CreateProductRequest/Response</strong>: Request and response classes for create operations</p>
</li>
<li><p><strong>ProductService.ProductServiceBase</strong>: Base class for implementing your service</p>
</li>
<li><p><strong>ProductService.ProductServiceClient</strong>: Client class for calling the service</p>
</li>
</ul>
<p>Congratulations! You've successfully created a comprehensive Protocol Buffer definition, defined a complete CRUD service contract, set up a strongly-typed message structure, and generated C# code from your proto file.</p>
<h3 id="heading-whats-next-1">What's Next?</h3>
<p>Now that we have our Protocol Buffer contract defined, we can start implementing the actual gRPC service methods. In the next section, we'll create the <code>ProductService</code> Class and implement each CRUD operation.</p>
<p><strong>Remember:</strong> Protocol Buffers are language-agnostic, so this same <code>.proto</code> file could be used to generate client code in Python, Java, Go, or any other supported language.</p>
<h2 id="heading-how-to-implement-crud-operations-services-with-grpc">How to Implement CRUD Operations Services with gRPC</h2>
<p>Now that we have our database set up and Protocol Buffer contracts defined, it's time to implement the actual CRUD (Create, Read, Update, Delete) functionality. We'll create a gRPC service that brings together our database models and Protocol Buffer definitions.</p>
<h3 id="heading-understanding-the-implementation-architecture">Understanding the Implementation Architecture</h3>
<p>Before we start coding, let's understand how the pieces fit together:</p>
<pre><code class="lang-bash">Protocol Buffer (.proto) → Generated C<span class="hljs-comment"># Code → Our Service Implementation → Database</span>
</code></pre>
<p>Key concepts in this code:</p>
<ul>
<li><p><strong>Proto Service:</strong> Interface (defines what methods are available)</p>
</li>
<li><p><strong>Proto Messages</strong>: DTOs (define data structure)</p>
</li>
<li><p><strong>Service Implementation</strong>: Business logic (what happens)</p>
</li>
<li><p><strong>Database Context</strong>: Data persistence layer</p>
</li>
</ul>
<h3 id="heading-configuring-proto-file-build">Configuring Proto File Build</h3>
<p>First, we need to ensure our <code>product.proto</code> file gets compiled into C# code during the build process.</p>
<p>Open your <code>ProductGrpc.csproj</code> file and locate the <code>&lt;ItemGroup&gt;</code> section that references proto files:</p>
<pre><code class="lang-xml">  <span class="hljs-comment">&lt;!-- ProductGrpc.csproj --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk.Web"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">TargetFramework</span>&gt;</span>net9.0<span class="hljs-tag">&lt;/<span class="hljs-name">TargetFramework</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Nullable</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">Nullable</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ImplicitUsings</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">ImplicitUsings</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Protobuf</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Protos\greet.proto"</span> <span class="hljs-attr">GrpcServices</span>=<span class="hljs-string">"Server"</span> /&gt;</span>
    <span class="hljs-comment">&lt;!-- Add this line to include our product.proto file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Protobuf</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Protos\product.proto"</span> <span class="hljs-attr">GrpcServices</span>=<span class="hljs-string">"Server"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Grpc.AspNetCore"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"2.64.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Design"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"9.0.6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IncludeAssets</span>&gt;</span>runtime; build; native; contentfiles; analyzers; buildtransitive<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeAssets</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PrivateAssets</span>&gt;</span>all<span class="hljs-tag">&lt;/<span class="hljs-name">PrivateAssets</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Sqlite"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"9.0.6"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Project</span>&gt;</span>
</code></pre>
<p>What this configuration does:</p>
<ul>
<li><p><code>Include="Protos\product.proto"</code> tells .NET to process our proto file</p>
</li>
<li><p><code>GrpcServices="Server"</code> generates server-side code (service base classes)</p>
</li>
</ul>
<h3 id="heading-building-the-project">Building the Project</h3>
<p>Now let's build the project to generate the C# code from our proto file:</p>
<pre><code class="lang-bash">dotnet build
</code></pre>
<p>This command will compile your <code>.proto</code> files into C# classes, generate the <code>ProductsServiceProto. ProductsServiceProtoBase</code> class we'll inherit from, create all the request/response message classes, and validate that everything compiles correctly.</p>
<p>You should see output like:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Build</span> <span class="hljs-string">succeeded.</span>
    <span class="hljs-number">0</span> <span class="hljs-string">Warning(s)</span>
    <span class="hljs-number">0</span> <span class="hljs-string">Error(s)</span>
</code></pre>
<h3 id="heading-creating-the-productservice-class">Creating the ProductService Class</h3>
<p>Navigate to the <code>Services</code> folder and create a new file called <code>ProductService.cs</code>. This will contain our gRPC service implementation.</p>
<p>Your project structure should now look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">ProductGrpc/</span>
<span class="hljs-string">├──</span> <span class="hljs-string">Services/</span>
<span class="hljs-string">│</span>   <span class="hljs-string">├──</span> <span class="hljs-string">GreeterService.cs</span>    <span class="hljs-comment"># Default template service</span>
<span class="hljs-string">│</span>   <span class="hljs-string">└──</span> <span class="hljs-string">ProductService.cs</span>    <span class="hljs-comment"># Our new product service</span>
<span class="hljs-string">└──</span> <span class="hljs-string">...</span>
</code></pre>
<h3 id="heading-setting-up-the-service-foundation">Setting Up the Service Foundation</h3>
<p>Start by creating the basic service class structure:</p>
<pre><code class="lang-csharp">
 <span class="hljs-comment">// Services/ProductService.cs</span>
<span class="hljs-keyword">using</span> Grpc.Core;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> ProductGrpc.Data;
<span class="hljs-keyword">using</span> ProductGrpc.Models;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">ProductGrpc.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ProductService</span> :  <span class="hljs-title">ProductsServiceProto</span> .<span class="hljs-title">ProductsServiceProtoBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> AppDbContext _dbContext;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;ProductService&gt; _logger;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductService</span>(<span class="hljs-params">AppDbContext dbContext, ILogger&lt;ProductService&gt; logger</span>)</span>
        {
            _dbContext = dbContext;
            _logger = logger;
        }

        <span class="hljs-comment">// CRUD methods will be implemented here</span>
    }
}
</code></pre>
<p>Key components of this code:</p>
<ul>
<li><p><strong>Inheritance</strong>: <code>ProductsServiceProto .ProductsServiceProtoBase</code> is generated from our proto file</p>
</li>
<li><p><strong>Dependency injection</strong>: We inject <code>AppDbContext</code> for database operations and <code>ILogger</code> for logging</p>
</li>
<li><p><strong>Constructor</strong>: Initializes our dependencies</p>
</li>
</ul>
<h3 id="heading-implementing-method-signatures">Implementing Method Signatures</h3>
<p>Now let's add all the method signatures that we defined in our proto file. These methods override the virtual methods from the base class:</p>
<pre><code class="lang-csharp"> <span class="hljs-comment">//Services/ProductService.cs</span>
<span class="hljs-keyword">using</span> Grpc.Core;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> ProductGrpc.Data;
<span class="hljs-keyword">using</span> ProductGrpc.Models;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">ProductGrpc.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ProductService</span> :  <span class="hljs-title">ProductsServiceProto</span> .<span class="hljs-title">ProductsServiceProtoBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> AppDbContext _dbContext;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;ProductService&gt; _logger;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductService</span>(<span class="hljs-params">AppDbContext dbContext, ILogger&lt;ProductService&gt; logger</span>)</span>
        {
            _dbContext = dbContext;
            _logger = logger;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;CreateProductResponse&gt; <span class="hljs-title">CreateProduct</span>(<span class="hljs-params">
            CreateProductRequest request, 
            ServerCallContext context</span>)</span>
        {
            <span class="hljs-comment">// Implementation will go here</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;GetProductResponse&gt; <span class="hljs-title">GetProduct</span>(<span class="hljs-params">
            GetProductRequest request, 
            ServerCallContext context</span>)</span>
        {
            <span class="hljs-comment">// Implementation will go here</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;ListProductsResponse&gt; <span class="hljs-title">ListProducts</span>(<span class="hljs-params">
            ListProductsRequest request, 
            ServerCallContext context</span>)</span>
        {
            <span class="hljs-comment">// Implementation will go here</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;UpdateProductResponse&gt; <span class="hljs-title">UpdateProduct</span>(<span class="hljs-params">
            UpdateProductRequest request, 
            ServerCallContext context</span>)</span>
        {
            <span class="hljs-comment">// Implementation will go here</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;DeleteProductResponse&gt; <span class="hljs-title">DeleteProduct</span>(<span class="hljs-params">
            DeleteProductRequest request, 
            ServerCallContext context</span>)</span>
        {
            <span class="hljs-comment">// Implementation will go here</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
        }
    }
}
</code></pre>
<h3 id="heading-understanding-method-parameters">Understanding Method Parameters</h3>
<p>Each gRPC method receives two parameters:</p>
<ol>
<li><p><strong>Request parameter</strong>: Contains the data sent by the client (for example, <code>CreateProductRequest</code>)</p>
</li>
<li><p><strong>ServerCallContext</strong>: Provides access to request metadata, cancellation tokens, and response headers</p>
</li>
</ol>
<p><strong>Method Signature Pattern:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;ResponseType&gt; <span class="hljs-title">MethodName</span>(<span class="hljs-params">
    RequestType request, 
    ServerCallContext context</span>)</span>
</code></pre>
<h3 id="heading-registering-the-service">Registering the Service</h3>
<p>Before we implement the methods, we need to register our service with the application. Open <code>Program.cs</code> and add the service:</p>
<pre><code class="lang-cs">
 <span class="hljs-comment">// Program.cs</span>
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> ProductGrpc.Data;
<span class="hljs-keyword">using</span> ProductGrpc.Services; <span class="hljs-comment">// Add this using statement</span>

<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

<span class="hljs-comment">// Add services to the container.</span>
builder.Services.AddGrpc();

<span class="hljs-comment">// Register our database context</span>
builder.Services.AddDbContext&lt;AppDbContext&gt;(options =&gt;
    options.UseSqlite(<span class="hljs-string">"Data Source=ProductGrpc.db"</span>));

<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// Configure the HTTP request pipeline.</span>
app.MapGrpcService&lt;GreeterService&gt;();
app.MapGrpcService&lt;ProductService&gt;(); <span class="hljs-comment">// Add this line</span>

app.MapGet(<span class="hljs-string">"/"</span>, () =&gt; <span class="hljs-string">"Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"</span>);

app.Run();
</code></pre>
<h3 id="heading-handling-compiler-warnings">Handling Compiler Warnings</h3>
<p>You might see warnings like:</p>
<pre><code class="lang-bash">This async method lacks <span class="hljs-string">'await'</span> operators and will run synchronously.
</code></pre>
<p>This is expected since we haven't implemented the actual logic yet. These warnings will disappear once we add the implementation in the following sections.</p>
<h3 id="heading-creating-helper-methods">Creating Helper Methods</h3>
<p>Before implementing CRUD operations, let's add some helper methods for converting between our database models and Protocol Buffer messages:</p>
<pre><code class="lang-cs">:Services/ProductService.cs
<span class="hljs-comment">// Add these helper methods to your ProductService class</span>


 <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ProductModel <span class="hljs-title">MapToProductModel</span>(<span class="hljs-params">Product product</span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ProductModel
    {
        Id = product.Id.ToString(),
        Name = product.Name,
        Description = product.Description,
        Price = (<span class="hljs-keyword">double</span>)product.Price,
        CreatedAt = product.Created.ToString(<span class="hljs-string">"yyyy-MM-ddTHH:mm:ssZ"</span>),
        UpdatedAt = product.Updated.ToString(<span class="hljs-string">"yyyy-MM-ddTHH:mm:ssZ"</span>),
        Tag = product.Tags ?? <span class="hljs-keyword">string</span>.Empty
    };
}
</code></pre>
<p>Excellent work! You've successfully:</p>
<ul>
<li><p>Configured proto file compilation</p>
</li>
<li><p>Created the ProductService class structure</p>
</li>
<li><p>Set up dependency injection</p>
</li>
<li><p>Defined all CRUD method signatures</p>
</li>
<li><p>Registered the service with the application</p>
</li>
<li><p>Created helper methods for data mapping</p>
</li>
</ul>
<h3 id="heading-whats-next-2">What's Next?</h3>
<p>Now that we have our service foundation ready, we'll implement each CRUD operation one by one:</p>
<ol>
<li><p><strong>CreateProduct</strong>: Add new products to the database</p>
</li>
<li><p><strong>GetProduct</strong>: Retrieve a single product by ID</p>
</li>
<li><p><strong>ListProducts</strong>: Get a paginated list of products</p>
</li>
<li><p><strong>UpdateProduct</strong>: Modify existing products</p>
</li>
<li><p><strong>DeleteProduct</strong>: Remove products from the database</p>
</li>
</ol>
<p>In the next sections, we'll dive deep into each implementation, handling error cases, validation, and best practices.</p>
<p><strong>💡 Pro Tip:</strong> The <code>ServerCallContext</code> parameter provides useful information like request cancellation tokens, client metadata, and response headers. We'll use these in our implementations for better error handling and logging.</p>
<p><strong>Note:</strong> The <code>override</code> keyword is crucial – it tells C# that we're implementing the virtual methods defined in the generated base class from our proto file.</p>
<h2 id="heading-how-to-implement-grpc-crud-database-operations-with-sqlite">How to Implement gRPC CRUD Database Operations With SQLite</h2>
<p>Now that we have our service foundation ready, let's implement each CRUD operation. Each method will handle database operations, error handling, and return appropriate responses using our Protocol Buffer messages.</p>
<h3 id="heading-understanding-the-implementation-pattern">Understanding the Implementation Pattern</h3>
<p>Each CRUD operation follows a consistent pattern:</p>
<ol>
<li><p><strong>Input Validation</strong>: Validate request parameters</p>
</li>
<li><p><strong>Database Operation</strong>: Perform the actual database work</p>
</li>
<li><p><strong>Response Mapping</strong>: Convert database models to Protocol Buffer messages</p>
</li>
<li><p><strong>Error Handling</strong>: Catch and handle exceptions gracefully</p>
</li>
</ol>
<h3 id="heading-creating-the-createproduct-service">Creating the <code>CreateProduct</code> Service</h3>
<p>The <code>CreateProduct</code> method handles adding new products to our database. This is the <strong>"C"</strong> in CRUD (Create).</p>
<pre><code class="lang-cs"><span class="hljs-comment">//Services/ProductService.cs</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;CreateProductResponse&gt; <span class="hljs-title">CreateProduct</span>(<span class="hljs-params">
    CreateProductRequest request, 
    ServerCallContext context</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-comment">// Input validation</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(request.Name))
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CreateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product name is required"</span>
            };
        }

        <span class="hljs-keyword">if</span> (request.Price &lt;= <span class="hljs-number">0</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CreateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product price must be greater than zero"</span>
            };
        }

        <span class="hljs-comment">// Create new product entity</span>
        <span class="hljs-keyword">var</span> productItem = <span class="hljs-keyword">new</span> Product
        {
            Id = Guid.NewGuid(),
            Name = request.Name,
            Description = request.Description,
            Price = Convert.ToDecimal(request.Price),
            Created = DateTime.UtcNow,
            Updated = DateTime.UtcNow,
            Tags = request.Tags
        };

        <span class="hljs-comment">// Add to database</span>
        _dbContext.Products.Add(productItem);
        <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

        _logger.LogInformation(<span class="hljs-string">"Product created successfully with ID: {ProductId}"</span>, productItem.Id);

        <span class="hljs-comment">// Return success response</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CreateProductResponse
        {
            Success = <span class="hljs-literal">true</span>,
            Message = <span class="hljs-string">"Product created successfully"</span>,
            Product = MapToProductModel(productItem)
        };
    }
    <span class="hljs-keyword">catch</span> (Exception ex)
    {
        _logger.LogError(ex, <span class="hljs-string">"Error creating product"</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CreateProductResponse
        {
            Success = <span class="hljs-literal">false</span>,
            Message = <span class="hljs-string">$"Error creating product: <span class="hljs-subst">{ex.Message}</span>"</span>
        };
    }
}
</code></pre>
<p>These are the important implementation details:</p>
<ul>
<li><p><strong>Unique ID generation</strong>: <code>Guid.NewGuid()</code> Creates a unique identifier</p>
</li>
<li><p><strong>Timestamp management</strong>: <code>DateTime.UtcNow</code> Ensures consistent timezone handling</p>
</li>
<li><p><strong>Type conversion</strong>: <code>Convert.ToDecimal()</code> converts double to decimal for database storage</p>
</li>
<li><p><strong>Input validation</strong>: Checks for required fields and valid values</p>
</li>
<li><p><strong>Logging</strong>: Records successful operations and errors for debugging</p>
</li>
</ul>
<h3 id="heading-creating-the-getproduct-service">Creating the <code>GetProduct</code> Service</h3>
<p>The <code>GetProduct</code> method retrieves a single product by its ID. This is the <strong>"R"</strong> in CRUD (Read).</p>
<pre><code class="lang-cs"><span class="hljs-comment">//Services/ProductService.cs</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;GetProductResponse&gt; <span class="hljs-title">GetProduct</span>(<span class="hljs-params">
    GetProductRequest request, 
    ServerCallContext context</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-comment">// Validate and parse the product ID</span>
        <span class="hljs-keyword">if</span> (!Guid.TryParse(request.Id, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> productId))
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> GetProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Invalid product ID format. Please provide a valid GUID."</span>
            };
        }

        <span class="hljs-comment">// Find product in database</span>
        <span class="hljs-keyword">var</span> product = <span class="hljs-keyword">await</span> _dbContext.Products.FindAsync(productId);

        <span class="hljs-keyword">if</span> (product == <span class="hljs-literal">null</span>)
        {
            _logger.LogWarning(<span class="hljs-string">"Product not found with ID: {ProductId}"</span>, productId);

            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> GetProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product not found"</span>
            };
        }

        _logger.LogInformation(<span class="hljs-string">"Product retrieved successfully with ID: {ProductId}"</span>, productId);

        <span class="hljs-comment">// Return success response with product data</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> GetProductResponse
        {
            Success = <span class="hljs-literal">true</span>,
            Message = <span class="hljs-string">"Product retrieved successfully"</span>,
            Product = MapToProductModel(product)
        };
    }
    <span class="hljs-keyword">catch</span> (Exception ex)
    {
        _logger.LogError(ex, <span class="hljs-string">"Error retrieving product with ID: {ProductId}"</span>, request.Id);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> GetProductResponse
        {
            Success = <span class="hljs-literal">false</span>,
            Message = <span class="hljs-string">$"Error retrieving product: <span class="hljs-subst">{ex.Message}</span>"</span>
        };
    }
}
</code></pre>
<p>Important implementation details:</p>
<ul>
<li><p><strong>ID validation</strong>: <code>Guid.TryParse()</code> safely validates the ID format</p>
</li>
<li><p><strong>Database query</strong>: <code>FindAsync()</code> efficiently finds records by primary key</p>
</li>
<li><p><strong>Null checking</strong>: Handles cases where the product doesn't exist</p>
</li>
<li><p><strong>Detailed logging</strong>: Tracks both successful retrievals and missing products</p>
</li>
</ul>
<h3 id="heading-creating-the-listproducts-service">Creating the <code>ListProducts</code> Service</h3>
<p>The <code>ListProducts</code> method retrieves multiple products with pagination support. This is also part of the <strong>"R"</strong> in CRUD (Read).</p>
<pre><code class="lang-cs"><span class="hljs-comment">// Services/ProductService.cs</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;ListProductsResponse&gt; <span class="hljs-title">ListProducts</span>(<span class="hljs-params">
    ListProductsRequest request, 
    ServerCallContext context</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-comment">// Set default pagination values</span>
        <span class="hljs-keyword">var</span> pageSize = request.PageSize &lt;= <span class="hljs-number">0</span> ? <span class="hljs-number">10</span> : Math.Min(request.PageSize, <span class="hljs-number">100</span>); <span class="hljs-comment">// Max 100 items per page</span>
        <span class="hljs-keyword">var</span> page = request.Page &lt;= <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : request.Page;

        <span class="hljs-comment">// Calculate skip amount for pagination</span>
        <span class="hljs-keyword">var</span> skip = (page - <span class="hljs-number">1</span>) * pageSize;

        <span class="hljs-comment">// Get total count for pagination metadata</span>
        <span class="hljs-keyword">var</span> totalCount = <span class="hljs-keyword">await</span> _dbContext.Products.CountAsync();

        <span class="hljs-comment">// Retrieve paginated products</span>
        <span class="hljs-keyword">var</span> products = <span class="hljs-keyword">await</span> _dbContext.Products
            .OrderBy(p =&gt; p.Created) <span class="hljs-comment">// Consistent ordering</span>
            .Skip(skip)
            .Take(pageSize)
            .ToListAsync();

        <span class="hljs-comment">// Create response</span>
        <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">new</span> ListProductsResponse
        {
            Success = <span class="hljs-literal">true</span>,
            Message = products.Any() 
                ? <span class="hljs-string">$"Retrieved <span class="hljs-subst">{products.Count}</span> products (Page <span class="hljs-subst">{page}</span> of <span class="hljs-subst">{Math.Ceiling((<span class="hljs-keyword">double</span>)totalCount / pageSize)}</span>)"</span>
                : <span class="hljs-string">"No products found"</span>,
            TotalCount = totalCount
        };

        <span class="hljs-comment">// Add products to response</span>
        response.Products.AddRange(products.Select(MapToProductModel));

        _logger.LogInformation(<span class="hljs-string">"Listed {ProductCount} products for page {Page}"</span>, products.Count, page);

        <span class="hljs-keyword">return</span> response;
    }
    <span class="hljs-keyword">catch</span> (Exception ex)
    {
        _logger.LogError(ex, <span class="hljs-string">"Error retrieving products list"</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ListProductsResponse
        {
            Success = <span class="hljs-literal">false</span>,
            Message = <span class="hljs-string">$"Error retrieving products: <span class="hljs-subst">{ex.Message}</span>"</span>,
            TotalCount = <span class="hljs-number">0</span>
        };
    }
}
</code></pre>
<p>Here are the key implementation details:</p>
<ul>
<li><p><strong>Pagination logic</strong>: Calculates <code>skip</code> and <code>take</code> values for efficient data retrieval</p>
</li>
<li><p><strong>Default values</strong>: Sets sensible defaults for page size and page number</p>
</li>
<li><p><strong>Performance optimization</strong>: Uses <code>Skip()</code> and <code>Take()</code> for database-level pagination</p>
</li>
<li><p><strong>Consistent ordering</strong>: <code>OrderBy()</code> ensures predictable results across requests</p>
</li>
<li><p><strong>Metadata</strong>: Returns total count for client-side pagination UI</p>
</li>
</ul>
<h3 id="heading-creating-the-updateproduct-service">Creating the <code>UpdateProduct</code> Service</h3>
<p>The <code>UpdateProduct</code> method modifies existing products. This is the <strong>"U"</strong> in CRUD (Update).</p>
<pre><code class="lang-cs"><span class="hljs-comment">// Services/ProductService.cs</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;UpdateProductResponse&gt; <span class="hljs-title">UpdateProduct</span>(<span class="hljs-params">
    UpdateProductRequest request, 
    ServerCallContext context</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-comment">// Validate product ID</span>
        <span class="hljs-keyword">if</span> (!Guid.TryParse(request.Id, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> productId))
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Invalid product ID format. Please provide a valid GUID."</span>
            };
        }

        <span class="hljs-comment">// Input validation</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(request.Name))
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product name is required"</span>
            };
        }

        <span class="hljs-keyword">if</span> (request.Price &lt;= <span class="hljs-number">0</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product price must be greater than zero"</span>
            };
        }

        <span class="hljs-comment">// Find existing product</span>
        <span class="hljs-keyword">var</span> existingProduct = <span class="hljs-keyword">await</span> _dbContext.Products.FindAsync(productId);

        <span class="hljs-keyword">if</span> (existingProduct == <span class="hljs-literal">null</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product not found"</span>
            };
        }

        <span class="hljs-comment">// Update product properties</span>
        existingProduct.Name = request.Name;
        existingProduct.Description = request.Description;
        existingProduct.Price = Convert.ToDecimal(request.Price);
        existingProduct.Tags = request.Tags;
        existingProduct.Updated = DateTime.UtcNow; <span class="hljs-comment">// Track when updated</span>

        <span class="hljs-comment">// Save changes to database</span>
        <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

        _logger.LogInformation(<span class="hljs-string">"Product updated successfully with ID: {ProductId}"</span>, productId);

        <span class="hljs-comment">// Return success response with updated product</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
        {
            Success = <span class="hljs-literal">true</span>,
            Message = <span class="hljs-string">"Product updated successfully"</span>,
            Product = MapToProductModel(existingProduct)
        };
    }
    <span class="hljs-keyword">catch</span> (Exception ex)
    {
        _logger.LogError(ex, <span class="hljs-string">"Error updating product with ID: {ProductId}"</span>, request.Id);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> UpdateProductResponse
        {
            Success = <span class="hljs-literal">false</span>,
            Message = <span class="hljs-string">$"Error updating product: <span class="hljs-subst">{ex.Message}</span>"</span>
        };
    }
}
</code></pre>
<p>Key details:</p>
<ul>
<li><p><strong>Existence check</strong>: Verifies the product exists before attempting updates</p>
</li>
<li><p><strong>Selective updates</strong>: Only updates the fields provided in the request</p>
</li>
<li><p><strong>Timestamp tracking</strong>: Updates the <code>Updated</code> field to track modification time</p>
</li>
<li><p><strong>Input validation</strong>: Ensures data integrity before saving</p>
</li>
<li><p><strong>Atomic operation</strong>: All changes are saved together or not at all</p>
</li>
</ul>
<h3 id="heading-creating-the-deleteproduct-service">Creating the <code>DeleteProduct</code> Service</h3>
<p>The <code>DeleteProduct</code> method removes products from the database. This is the <strong>"D"</strong> in CRUD (Delete).</p>
<pre><code class="lang-cs"> <span class="hljs-comment">// Services/ProductService.cs</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task&lt;DeleteProductResponse&gt; <span class="hljs-title">DeleteProduct</span>(<span class="hljs-params">
    DeleteProductRequest request, 
    ServerCallContext context</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-comment">// Validate product ID</span>
        <span class="hljs-keyword">if</span> (!Guid.TryParse(request.Id, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> productId))
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DeleteProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Invalid product ID format. Please provide a valid GUID."</span>
            };
        }

        <span class="hljs-comment">// Find the product to delete</span>
        <span class="hljs-keyword">var</span> product = <span class="hljs-keyword">await</span> _dbContext.Products.FindAsync(productId);

        <span class="hljs-keyword">if</span> (product == <span class="hljs-literal">null</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DeleteProductResponse
            {
                Success = <span class="hljs-literal">false</span>,
                Message = <span class="hljs-string">"Product not found"</span>
            };
        }

        <span class="hljs-comment">// Remove product from database</span>
        _dbContext.Products.Remove(product);
        <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

        _logger.LogInformation(<span class="hljs-string">"Product deleted successfully with ID: {ProductId}"</span>, productId);

        <span class="hljs-comment">// Return success response</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DeleteProductResponse
        {
            Success = <span class="hljs-literal">true</span>,
            Message = <span class="hljs-string">"Product deleted successfully"</span>
        };
    }
    <span class="hljs-keyword">catch</span> (Exception ex)
    {
        _logger.LogError(ex, <span class="hljs-string">"Error deleting product with ID: {ProductId}"</span>, request.Id);

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DeleteProductResponse
        {
            Success = <span class="hljs-literal">false</span>,
            Message = <span class="hljs-string">$"Error deleting product: <span class="hljs-subst">{ex.Message}</span>"</span>
        };
    }
}
</code></pre>
<p>Key details:</p>
<ul>
<li><p><strong>Soft vs hard delete</strong>: This implements hard delete (permanent removal)</p>
</li>
<li><p><strong>Existence verification</strong>: Checks if the product exists before deletion</p>
</li>
<li><p><strong>Clean removal</strong>: Uses Entity Framework's <code>Remove()</code> method</p>
</li>
<li><p><strong>Confirmation</strong>: Returns a success message confirming deletion</p>
</li>
</ul>
<h3 id="heading-complete-productservice-implementation">Complete ProductService Implementation</h3>
<p>Here's your complete <code>ProductService.cs</code> file with all CRUD operations:</p>
<pre><code class="lang-cs"><span class="hljs-comment">// Services/ProductService.cs</span>
<span class="hljs-keyword">using</span> Grpc.Core;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> ProductGrpc.Data;
<span class="hljs-keyword">using</span> ProductGrpc.Models;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">ProductGrpc.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ProductService</span> : <span class="hljs-title">Product.ProductServiceBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> AppDbContext _dbContext;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;ProductService&gt; _logger;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductService</span>(<span class="hljs-params">AppDbContext dbContext, ILogger&lt;ProductService&gt; logger</span>)</span>
        {
            _dbContext = dbContext;
            _logger = logger;
        }

        <span class="hljs-comment">// Helper method to map Product entity to ProductModel message</span>
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ProductModel <span class="hljs-title">MapToProductModel</span>(<span class="hljs-params">Product product</span>)</span>
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ProductModel
            {
                Id = product.Id.ToString(),
                Name = product.Name,
                Description = product.Description,
                Price = (<span class="hljs-keyword">double</span>)product.Price,
                CreatedAt = product.Created.ToString(<span class="hljs-string">"yyyy-MM-ddTHH:mm:ssZ"</span>),
                UpdatedAt = product.Updated.ToString(<span class="hljs-string">"yyyy-MM-ddTHH:mm:ssZ"</span>),
                Tags = product.Tags ?? <span class="hljs-keyword">string</span>.Empty
            };
        }

        <span class="hljs-comment">// All CRUD methods go here (as implemented above)</span>
        <span class="hljs-comment">// CreateProduct, GetProduct, ListProducts, UpdateProduct, DeleteProduct</span>
    }
}
</code></pre>
<p>Excellent work! You've successfully implemented all CRUD operations:</p>
<ul>
<li><p><strong>Create</strong>: Add new products with validation</p>
</li>
<li><p><strong>Read</strong>: Retrieve single products and paginated lists</p>
</li>
<li><p><strong>Update</strong>: Modify existing products with validation</p>
</li>
<li><p><strong>Delete</strong>: Remove products safely</p>
</li>
</ul>
<h3 id="heading-whats-next-3">What's Next?</h3>
<p>Now that our gRPC service is fully implemented, we need to test it! In the next section, we'll learn how to test our gRPC endpoints using Postman.</p>
<h2 id="heading-how-to-test-grpc-services-with-postman">How to Test gRPC Services with Postman</h2>
<p>Testing gRPC services requires different tools and approaches compared to traditional REST APIs. While REST APIs use HTTP/1.1 with JSON payloads, gRPC uses HTTP/2 with binary Protocol Buffer messages. Fortunately, Postman provides excellent support for gRPC testing, making it easy to test our service without writing client code.</p>
<h3 id="heading-why-grpc-testing-is-different">Why gRPC Testing is Different</h3>
<p>Here are the important differences between gRPC and REST API testing summarized:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>REST API</td><td>gRPC</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Protocol</strong></td><td>HTTP/1.1</td><td>HTTP/2</td></tr>
<tr>
<td><strong>Data Format</strong></td><td>JSON/XML</td><td>Protocol Buffers (Binary)</td></tr>
<tr>
<td><strong>Schema</strong></td><td>Optional (OpenAPI/Swagger)</td><td>Required (.proto files)</td></tr>
<tr>
<td><strong>Content-Type</strong></td><td>application/json</td><td>application/grpc</td></tr>
<tr>
<td><strong>Testing Tools</strong></td><td>Any HTTP client</td><td>Specialized gRPC clients</td></tr>
</tbody>
</table>
</div><p>Why we need the proto file:</p>
<ul>
<li><p>gRPC requires the service contract (.proto file) to understand available methods</p>
</li>
<li><p>Protocol Buffers need schema definition for serialization/deserialization</p>
</li>
<li><p>Postman uses the proto file to generate the correct request/response structure</p>
</li>
</ul>
<h3 id="heading-setting-up-postman-for-grpc-testing">Setting Up Postman for gRPC Testing</h3>
<h4 id="heading-step-1-launch-postman">Step 1: Launch Postman</h4>
<p>Open Postman on your machine. You should see the main dashboard similar to this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879108777/a98c6fa4-08c7-49f1-94a4-9138b69ad0a1.png" alt="Postman main dashboard showing the workspace interface" width="1908" height="1040" loading="lazy"></p>
<h4 id="heading-step-2-create-a-new-grpc-request">Step 2: Create a New gRPC Request</h4>
<p>Click on "New" in the top-left corner or use the "+" button. Then select "gRPC Request" from the available options.</p>
<p>You should see a modal dialog with different request types:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879545910/840f2004-73c5-4c1e-97a6-62b39eb278f9.png" alt="Postman's new request modal showing gRPC option" width="2560" height="1371" loading="lazy"></p>
<p>Click on "gRPC Request" to create a new gRPC request.</p>
<h4 id="heading-step-3-configure-the-grpc-request-interface">Step 3: Configure the gRPC Request Interface</h4>
<p>After creating a gRPC request, you'll see the gRPC request interface:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879652894/b339e198-27e9-4618-8cdb-54718da65841.png" alt="Postman gRPC request interface with service definition section" width="2560" height="1388" loading="lazy"></p>
<p>These are the notable components of the gRPC interface:</p>
<ul>
<li><p><strong>Server URL</strong>: Where your gRPC service is running</p>
</li>
<li><p><strong>Service definition</strong>: Where you import your .proto file</p>
</li>
<li><p><strong>Method selection</strong>: Choose which RPC method to call</p>
</li>
<li><p><strong>Message body</strong>: Request payload based on proto definitions</p>
</li>
</ul>
<h3 id="heading-importing-the-proto-file">Importing the Proto File</h3>
<h4 id="heading-step-4-access-service-definition">Step 4: Access Service Definition</h4>
<p>Locate the "Service definition" section in the gRPC request interface. Then click on "Import .proto file" or a similar option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879690499/b2d53cab-3d0c-4c84-8a12-7777480e6860.png" alt="Service definition section where proto files are imported" width="2560" height="1393" loading="lazy"></p>
<h4 id="heading-step-5-import-your-proto-file">Step 5: Import Your Proto File</h4>
<ol>
<li><p>Click "Select Files" or "Import" button</p>
</li>
<li><p>Navigate to your project directory</p>
</li>
<li><p>Go to the <code>Protos</code> folder</p>
</li>
<li><p>Select <code>product.proto</code> file</p>
</li>
<li><p>Click "Open" to import</p>
</li>
</ol>
<p><strong>File Path Structure:</strong></p>
<pre><code class="lang-bash">YourProject/
├── Protos/
│   ├── greet.proto
│   └── product.proto    ← Select this file
└── ...
</code></pre>
<h4 id="heading-step-6-configure-import-settings">Step 6: Configure Import Settings</h4>
<p>When importing, you'll see options like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879892390/625b1fbd-cdd6-4437-af9e-441a39780c5e.png" alt="Proto file import configuration in Postman" width="2560" height="1393" loading="lazy"></p>
<p><strong>Import Configuration:</strong></p>
<ul>
<li><p><strong>Import as</strong>: Select "API"</p>
</li>
<li><p><strong>API name</strong>: Choose a descriptive name (for example, "ProductGrpc API")</p>
</li>
<li><p><strong>Import location</strong>: Select your workspace</p>
</li>
</ul>
<p>You want to import as an API because it creates a reusable API definition, allows multiple team members to use the same proto definitions, and provides better organization for multiple services.</p>
<h4 id="heading-step-7-verify-successful-import">Step 7: Verify Successful Import</h4>
<p>After successful import, you should see:</p>
<ol>
<li><p><strong>API Collection</strong>: Your named API appears in the left sidebar under "APIs."</p>
</li>
<li><p><strong>Available Methods</strong>: All RPC methods from your proto file are listed</p>
</li>
<li><p><strong>Request/Response Schemas</strong>: Postman understands your message structures</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753879919558/a9c264a6-b021-4c7a-8221-557350c07f53.png" alt="Successfully imported proto file showing available methods" width="2560" height="1393" loading="lazy"></p>
<h3 id="heading-understanding-the-grpc-request-interface">Understanding the gRPC Request Interface</h3>
<p>Once connected, you'll see a method selection dropdown with the following:</p>
<ul>
<li><p>CreateProduct</p>
</li>
<li><p>GetProduct</p>
</li>
<li><p>ListProducts</p>
</li>
<li><p>UpdateProduct</p>
</li>
<li><p>DeleteProduct</p>
</li>
</ul>
<p>Excellent! You've successfully created a gRPC request in Postman, imported your proto file, configured the server connection, and set up the API collection for reuse.</p>
<h3 id="heading-whats-next-4">What's Next?</h3>
<p>Now that Postman is configured with your proto file, you're ready to test each CRUD operation:</p>
<ol>
<li><p><strong>CreateProduct</strong>: Test adding new products</p>
</li>
<li><p><strong>GetProduct</strong>: Test retrieving single products</p>
</li>
<li><p><strong>ListProducts</strong>: Test pagination and listing</p>
</li>
<li><p><strong>UpdateProduct</strong>: Test modifying existing products</p>
</li>
<li><p><strong>DeleteProduct</strong>: Test removing products</p>
</li>
</ol>
<p>In the next section, we'll walk through testing each operation with sample data and expected responses.</p>
<h2 id="heading-how-to-test-product-creation">How to Test Product Creation</h2>
<p>Now that we have Postman configured with our proto file, it's time to test our gRPC service! We'll start by testing the <code>CreateProduct</code> Method to add a new product to our database.</p>
<h3 id="heading-request-structure">Request Structure</h3>
<p>Before sending your first request in Postman, select the RPC method from the dropdown. The request body’s shape comes directly from the Protocol Buffer definitions in your <code>.proto</code> file. Postman renders those proto messages as JSON for easier editing, but the proto types still apply: each field must match the type defined in the schema (including nested messages, enums, and repeated fields).</p>
<h3 id="heading-step-1-select-the-createproduct-method">Step 1: Select the CreateProduct Method</h3>
<p>Open your gRPC request in Postman and click the method dropdown (should show available methods). Select "CreateProduct" from the list.</p>
<p>You should see all the methods we defined in our proto file:</p>
<ul>
<li><p>CreateProduct</p>
</li>
<li><p>GetProduct</p>
</li>
<li><p>ListProducts</p>
</li>
<li><p>UpdateProduct</p>
</li>
<li><p>DeleteProduct</p>
</li>
</ul>
<h3 id="heading-step-2-request-schema">Step 2: Request Schema</h3>
<p>When you select <code>CreateProduct</code>, Postman automatically generates the request structure based on our <code>CreateProductRequest</code> message from the proto file:</p>
<p><strong>Proto Definition Reminder:</strong></p>
<pre><code class="lang-csharp">message CreateProductRequest {
  <span class="hljs-keyword">string</span> name = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">string</span> description = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">double</span> price = <span class="hljs-number">3</span>;
  <span class="hljs-keyword">string</span> tags = <span class="hljs-number">4</span>;
}
</code></pre>
<p><strong>Postman JSON Representation:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"price"</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">"tags"</span>: <span class="hljs-string">""</span>
}
</code></pre>
<h3 id="heading-step-3-prepare-test-data">Step 3: Prepare Test Data</h3>
<p>Let's create our first product with meaningful test data. In the request body, enter:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD"</span>,
  <span class="hljs-attr">"price"</span>: <span class="hljs-number">2499.99</span>,
  <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional"</span>
}
</code></pre>
<p>So what are these fields?</p>
<ul>
<li><p><strong>name</strong>: Product title (required, string)</p>
</li>
<li><p><strong>description</strong>: Detailed product information (string)</p>
</li>
<li><p><strong>price</strong>: Product cost (double/number, must be &gt; 0)</p>
</li>
<li><p><strong>tags</strong>: Comma-separated keywords (string)</p>
</li>
</ul>
<h3 id="heading-step-4-send-the-request">Step 4: Send the Request</h3>
<p>Click the "Invoke" button (or "Send" depending on Postman version) and wait for the response (should be very fast for local testing). Then check the response status (should show success).</p>
<h3 id="heading-step-5-analyze-the-response">Step 5: Analyze the Response</h3>
<p>If everything works correctly, you should receive a response like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Product created successfully"</span>,
  <span class="hljs-attr">"product"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD"</span>,
    <span class="hljs-attr">"price"</span>: <span class="hljs-number">2499.99</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
    <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
    <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional"</span>
  }
}
</code></pre>
<p>Response fields:</p>
<ul>
<li><p><strong>success</strong>: Boolean indicating operation success</p>
</li>
<li><p><strong>message</strong>: Human-readable status message</p>
</li>
<li><p><strong>product</strong>: The created product with generated fields</p>
<ul>
<li><p><strong>id</strong>: Auto-generated GUID</p>
</li>
<li><p><strong>created_at/updated_at</strong>: UTC timestamps</p>
</li>
<li><p><strong>Other fields</strong>: Echo of the input data</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-visual-confirmation-in-postman">Visual Confirmation in Postman</h3>
<p>Here's how a successful request looks in Postman:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753880143034/f7e81267-8d45-4835-b1d9-ad091563b99c.png" alt="Postman interface showing successful product creation with request and response" width="2511" height="1342" loading="lazy"></p>
<p>Congratulations! You've successfully made your first gRPC request using Postman! You’ve also created a product in the database, received a properly formatted response, and verified the auto-generated ID and timestamps.</p>
<h3 id="heading-whats-next-5">What's Next?</h3>
<p>Now that we've successfully tested product creation, let's test the other CRUD operations:</p>
<ol>
<li><p><strong>GetProduct</strong>: Retrieve the product we just created</p>
</li>
<li><p><strong>ListProducts</strong>: See all products with pagination</p>
</li>
<li><p><strong>UpdateProduct</strong>: Modify the existing product</p>
</li>
<li><p><strong>DeleteProduct</strong>: Remove the product from the database</p>
</li>
</ol>
<p>Each operation will help us verify that our complete gRPC service is working correctly.</p>
<h2 id="heading-how-to-test-all-product-operations">How to Test All Product Operations</h2>
<p>Now that we've successfully created a product, let's test all the remaining CRUD operations to ensure our complete gRPC service works correctly.</p>
<h3 id="heading-get-all-products-listproducts">Get All Products (ListProducts)</h3>
<p>The <code>ListProducts</code> method retrieves all products from our database with pagination support. Since we've created some products, we should be able to see them in the response.</p>
<h4 id="heading-step-1-select-listproducts-method">Step 1: Select ListProducts Method</h4>
<p>Click the method dropdown in your Postman gRPC request. Then select "ListProducts" from the available methods. Notice the request structure – it includes pagination parameters.</p>
<h4 id="heading-step-2-configure-the-request">Step 2: Configure the Request</h4>
<p>The <code>ListProductsRequest</code> supports pagination parameters:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"page"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"pageSize"</span>: <span class="hljs-number">10</span>
}
</code></pre>
<p>Here’s what’s going on with these parameters:</p>
<ul>
<li><p><strong>page</strong>: Which page of results to retrieve (default: 1)</p>
</li>
<li><p><strong>pageSize</strong>: Number of products per page (default: 10, max: 100)</p>
</li>
</ul>
<h4 id="heading-step-3-send-the-request">Step 3: Send the Request</h4>
<p>Click "Invoke" to send the request and wait for the response containing all your products.</p>
<h4 id="heading-step-4-response">Step 4: Response</h4>
<p>You should receive a response like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Retrieved 2 products (Page 1 of 1)"</span>,
  <span class="hljs-attr">"totalCount"</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">"products"</span>: [
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD"</span>,
      <span class="hljs-attr">"price"</span>: <span class="hljs-number">2499.99</span>,
      <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
      <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
      <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional"</span>
    },
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"a1b2c3d4-5e6f-7890-abcd-ef1234567890"</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"iPhone 15 Pro"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Latest iPhone with titanium design"</span>,
      <span class="hljs-attr">"price"</span>: <span class="hljs-number">999.99</span>,
      <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2024-01-15T16:15:22Z"</span>,
      <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2024-01-15T16:15:22Z"</span>,
      <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"smartphone, apple, premium"</span>
    }
  ]
}
</code></pre>
<p>Response structure:</p>
<ul>
<li><p><strong>success</strong>: Operation status</p>
</li>
<li><p><strong>message</strong>: Descriptive message with pagination info</p>
</li>
<li><p><strong>totalCount</strong>: Total number of products in the database</p>
</li>
<li><p><strong>products</strong>: Array of product objects</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753880306062/af0bd1d4-24d0-4be8-96a1-f59404f36672.png" alt="Postman showing successful retrieval of all products with pagination" width="2469" height="1384" loading="lazy"></p>
<h3 id="heading-testing-pagination">Testing Pagination</h3>
<p>Let’s now test some different pagination scenarios:</p>
<p><strong>Get the first 5 products:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"page"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"pageSize"</span>: <span class="hljs-number">5</span>
}
</code></pre>
<p><strong>Get the second page:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"page"</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">"pageSize"</span>: <span class="hljs-number">5</span>
}
</code></pre>
<h3 id="heading-get-product-by-id-getproduct">Get Product By ID (GetProduct)</h3>
<p>The <code>GetProduct</code> method retrieves a single product using its unique ID. Unlike REST APIs, where the ID is part of the URL path, gRPC passes the ID in the message body.</p>
<h4 id="heading-step-1-select-the-getproduct-method">Step 1: Select the GetProduct Method</h4>
<p>Select "GetProduct" from the method dropdown. Notice that the request structure requires an ID field.</p>
<h4 id="heading-step-2-prepare-the-request">Step 2: Prepare the Request</h4>
<p>Copy a product ID from your previous <code>ListProducts</code> response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>
}
</code></pre>
<p>Important notes:</p>
<ul>
<li><p><strong>ID Format</strong>: Must be a valid GUID string</p>
</li>
<li><p><strong>Case Sensitivity</strong>: GUIDs are case-insensitive</p>
</li>
<li><p><strong>Validation</strong>: Invalid GUIDs will return an error</p>
</li>
</ul>
<h4 id="heading-step-3-send-the-request-1">Step 3: Send the Request</h4>
<p>Paste a valid product ID from your ListProducts response. Click "Invoke" to send the request.</p>
<h4 id="heading-step-4-analyze-the-response">Step 4: Analyze the Response</h4>
<p>Successful response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Product retrieved successfully"</span>,
  <span class="hljs-attr">"product"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD"</span>,
    <span class="hljs-attr">"price"</span>: <span class="hljs-number">2499.99</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
    <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
    <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional"</span>
  }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753880592637/ebbacee5-1333-4328-8c46-ab7dd24d824d.png" alt="Postman showing successful retrieval of a single product by ID" width="2550" height="1377" loading="lazy"></p>
<h3 id="heading-update-product-updateproduct">Update Product (UpdateProduct)</h3>
<p>The <code>UpdateProduct</code> method modifies an existing product. You need to provide the product ID and the fields you want to update.</p>
<h4 id="heading-step-1-select-the-updateproduct-method">Step 1: Select the UpdateProduct Method</h4>
<p>Select "UpdateProduct" from the method dropdown. Review the request structure which includes ID and all updatable fields.</p>
<h4 id="heading-step-2-prepare-the-update-request">Step 2: Prepare the Update Request</h4>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch (Updated)"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 1TB SSD - Updated Storage"</span>,
  <span class="hljs-attr">"price"</span>: <span class="hljs-number">2799.99</span>,
  <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional, updated"</span>
}
</code></pre>
<p>Update guidelines:</p>
<ul>
<li><p><strong>ID</strong>: Must match an existing product</p>
</li>
<li><p><strong>All Fields</strong>: Currently required (not partial updates)</p>
</li>
<li><p><strong>Price</strong>: Must be greater than 0</p>
</li>
<li><p><strong>Name</strong>: Cannot be empty</p>
</li>
</ul>
<h4 id="heading-step-3-send-the-update-request">Step 3: Send the Update Request</h4>
<p>Make sure the ID exists (use one from your ListProducts response). Then click "Invoke" to send the update.</p>
<h4 id="heading-step-4-verify-the-update">Step 4: Verify the Update</h4>
<p>Successful response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Product updated successfully"</span>,
  <span class="hljs-attr">"product"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"920b98d2-4feb-4705-8303-ce6e28bd3694"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MacBook Pro 16-inch (Updated)"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Apple MacBook Pro with M2 Pro chip, 16GB RAM, 1TB SSD - Updated Storage"</span>,
    <span class="hljs-attr">"price"</span>: <span class="hljs-number">2799.99</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2024-01-15T16:11:38Z"</span>,
    <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2024-01-15T16:25:14Z"</span>,
    <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"laptop, apple, professional, updated"</span>
  }
}
</code></pre>
<p>Notice the changes:</p>
<ul>
<li><p><strong>updated_at</strong>: Timestamp changed to reflect the update</p>
</li>
<li><p><strong>Modified Fields</strong>: All updated fields reflect new values</p>
</li>
<li><p><strong>created_at</strong>: Remains unchanged (original creation time)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753880928160/d12e58ff-3c14-49f6-a3dd-1d1d2c452e09.png" alt="Postman showing successful product update with modified fields" width="2524" height="1327" loading="lazy"></p>
<h3 id="heading-delete-product-by-id-deleteproduct">Delete Product By ID (DeleteProduct)</h3>
<p>The <code>DeleteProduct</code> method permanently removes a product from the database using its ID.</p>
<h4 id="heading-step-1-select-deleteproduct-method">Step 1: Select DeleteProduct Method</h4>
<p>Select "DeleteProduct" from the method dropdown. Note the simple request structure – it only requires an ID.</p>
<h4 id="heading-step-2-prepare-the-delete-request">Step 2: Prepare the Delete Request</h4>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-string">"a1b2c3d4-5e6f-7890-abcd-ef1234567890"</span>
}
</code></pre>
<p><strong>⚠️ Warning</strong>: This operation permanently deletes the product. Make sure you're using the correct ID.</p>
<h4 id="heading-step-3-send-the-delete-request">Step 3: Send the Delete Request</h4>
<p>Double-check the product ID you want to delete. Click "Invoke" to send the delete request.</p>
<h4 id="heading-step-4-confirm-deletion">Step 4: Confirm Deletion</h4>
<p>Successful response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Product deleted successfully"</span>
}
</code></pre>
<p>Verification steps:</p>
<ol>
<li><p><strong>Try GetProduct</strong> with the same ID – it should return "Product not found"</p>
</li>
<li><p><strong>Run ListProducts</strong> – the product should no longer appear in the list</p>
</li>
<li><p><strong>Check totalCount</strong> – should be reduced by 1</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753880987953/23bb19f0-7ca0-4cec-b9bb-3774737f98c9.png" alt="Postman showing successful product deletion" width="2514" height="1372" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! 🎉 You've successfully completed this comprehensive journey into building gRPC services with ASP.NET Core. Throughout this handbook, you've gained hands-on experience with one of the most powerful and efficient communication frameworks available for modern distributed applications.</p>
<h3 id="heading-what-youve-accomplished">What You've Accomplished</h3>
<p>Let's recap the impressive skills and knowledge you've acquired:</p>
<h4 id="heading-foundation-building">Foundation building</h4>
<ul>
<li><p>Set up a complete gRPC project from scratch using .NET CLI</p>
</li>
<li><p>Configured SQLite database with Entity Framework Core</p>
</li>
<li><p>Created robust data models with proper validation</p>
</li>
<li><p>Implemented database migrations and seeding</p>
</li>
</ul>
<h4 id="heading-learning-protocol-buffers">Learning protocol buffers</h4>
<ul>
<li><p>Designed comprehensive .proto files with service definitions</p>
</li>
<li><p>Created strongly-typed message contracts for all CRUD operations</p>
</li>
<li><p>Understood the advantages of binary serialization over JSON</p>
</li>
<li><p>Implemented efficient data transfer objects (DTOs)</p>
</li>
</ul>
<h4 id="heading-service-implementation">Service implementation</h4>
<ul>
<li><p>Built a complete ProductService with all CRUD operations</p>
</li>
<li><p>Implemented proper error handling and validation</p>
</li>
<li><p>Added comprehensive logging for debugging and monitoring</p>
</li>
<li><p>Created efficient pagination for large datasets</p>
</li>
<li><p>Handled data mapping between entities and Protocol Buffer messages</p>
</li>
</ul>
<h4 id="heading-testing-and-validation">Testing and validation</h4>
<ul>
<li><p>Configured Postman for gRPC testing</p>
</li>
<li><p>Tested all CRUD operations with real data</p>
</li>
<li><p>Verified data integrity and proper response formatting</p>
</li>
</ul>
<h3 id="heading-key-technical-skills-gained">Key Technical Skills Gained</h3>
<p><strong>gRPC Expertise:</strong></p>
<ul>
<li><p>Understanding of the HTTP/2 protocol advantages</p>
</li>
<li><p>Protocol Buffer schema design and evolution</p>
</li>
<li><p>Service-to-service communication patterns</p>
</li>
<li><p>Performance optimization techniques</p>
</li>
</ul>
<h4 id="heading-you-can-access-the-code-in-this-github-repositoryhttpsgithubcomclifftech123isaiahcliffordopokublog">🔗 You can access the code <a target="_blank" href="https://github.com/Clifftech123/IsaiahCliffordOpokuBlog">in this GitHub Repository</a>.</h4>
<h3 id="heading-thank-you">Thank You!</h3>
<p>Thank you for following along with this comprehensive tutorial. Your dedication to learning these advanced concepts will serve you well in building the next generation of distributed applications.</p>
<p><strong>Happy coding, and may your services be fast, reliable, and scalable!</strong></p>
<p>If you want to learn more about .NET Core, you can subscribe to my YouTube channel <a target="_blank" href="https://youtube.com/@clifftech?si=QgdE39q4iYIPEN23">here</a>.</p>
<p><strong>🔗 Connect with the author:</strong></p>
<ul>
<li><p>GitHub: <a target="_blank" href="https://github.com/Clifftech123">@CliffTech123</a></p>
</li>
<li><p>Twitter: <a target="_blank" href="https://x.com/Clifftech_Dev">@Clifftech_Dev</a></p>
</li>
<li><p>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/isaiah-clifford-opoku/">Isaiah Clifford Opoku</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Minimal API in .NET Core – A Step By Step Handbook ]]>
                </title>
                <description>
                    <![CDATA[ Minimal APIs are an exciting feature introduced in .NET 6, designed to revolutionize how you create APIs. Imagine building robust APIs with minimal code and zero boilerplate—no more wrestling with controllers, routing, or middleware. That’s what mini... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-minimal-api-in-net-core-handbook/</link>
                <guid isPermaLink="false">674de68794b89afe5676934e</guid>
                
                    <category>
                        <![CDATA[ dotnet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MinimalApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Isaiah Clifford Opoku ]]>
                </dc:creator>
                <pubDate>Mon, 02 Dec 2024 16:55:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733158500882/9af04a12-2121-4efd-a66f-00330896e358.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Minimal APIs are an exciting feature introduced in .NET 6, designed to revolutionize how you create APIs.</p>
<p>Imagine building robust APIs with minimal code and zero boilerplate—no more wrestling with controllers, routing, or middleware. That’s what minimal APIs allow you to do. The idea with these APIs is to streamline the development process, making it incredibly easy and efficient.</p>
<p>In this article, we'll dive into the world of minimal APIs in .NET 8 and guide you through creating a fully functional bookstore API. You'll learn how to get all books, retrieve a book by its ID, add new books, and even delete books. Let’s get started.</p>
<h1 id="heading-table-of-contents">Table of Contents</h1>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-introduction-to-minimal-apis">Introduction to Minimal APIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-minimal-api">How to Create a Minimal API</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-http-methods-in-controller-based-and-minimal-apis">HTTP Methods in Controller-based and Minimal APIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-minimal-api-project-files">Minimal API Project Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-models">How to Create the Models</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-database-context">How to Create the Database Context</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-contract">How to Create a Contract</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-services">How to Add Services</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-exceptions">How to Create Exceptions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-api-endpoints">How to Create the API Endpoints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-seed-data-to-the-database">How to Add Seed Data to the Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-perform-a-migration">How to Perform a Migration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-api-endpoints">How to Test the API Endpoints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we get going, make sure you have the following prerequisites installed on your machine:</p>
<ul>
<li><p><a target="_blank" href="https://dotnet.microsoft.com/download/dotnet/8.0">.NET 8 SDK</a></p>
</li>
<li><p><a target="_blank" href="https://code.visualstudio.com/download">Visual Studio Code</a> or any other code editor of your choice</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp">C# Dev Kit</a> for Visual Studio Code</p>
</li>
</ul>
<p>Alternatively, you can use Visual Studio 2022, which comes with built-in support for .NET 8. But in this article, we'll be using Visual Studio Code. It’s lightweight, easy to use, and cross-platform.</p>
<p>We’ll use Swagger UI to test our API. Swagger UI is a powerful tool that allows you to interact with your API directly from your browser. It provides a user-friendly interface to test your API endpoints, making it easier to test and debug your API.</p>
<p>When you create a new project, it will automatically install the necessary packages and configure the project to use Swagger UI. .NET 8 includes Swagger UI by default, so whether you create your application in Visual Studio or with .NET, Swagger UI will be configured for you.</p>
<p>Run your application, and the Swagger UI will automatically open in your browser – but since we are using VS Code, we need to click on the port number on our terminal.</p>
<p>You can find the source code for this project on <a target="_blank" href="https://github.com/Clifftech123/bookapi-minimal">GitHub</a>.</p>
<h2 id="heading-introduction-to-minimal-apis">Introduction to Minimal APIs</h2>
<p>Imagine working in a codebase with numerous endpoints, making it quite large and complex. Traditionally, building an API in <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core involves using controllers, routing, middleware, and a significant amount of boilerplate code. But there are two approaches to building an API in ASP.NET Core: the traditional way and the minimal way.</p>
<p>The traditional way is familiar to most developers, involving controllers and extensive infrastructure code. The minimal way, introduced in <code>.NET 6</code>, allows you to create APIs with minimal code and zero boilerplate. This approach simplifies the development process, enabling you to focus on writing business logic rather than dealing with infrastructure code.</p>
<p>Minimal APIs are lightweight, fast, and perfect for building small to medium-sized APIs. They are ideal for prototyping, building microservices, or creating simple APIs that don't require much complexity. In this handbook, we'll explore the world of minimal APIs in .NET 6 and learn how to create a fully functional bookstore API from scratch.</p>
<h2 id="heading-how-to-create-a-minimal-api">How to Create a Minimal API</h2>
<p>Creating a minimal API is straightforward when using the <code>dotnet CLI</code>, as the default template is already a minimal API. But if you use Visual Studio, you'll need to remove the boilerplate code that comes with the project template.</p>
<p>Let's start by using the <code>dotnet CLI</code> to create a minimal API project.</p>
<pre><code class="lang-bash">
dotnet new webapi  -n BookStoreApi
</code></pre>
<p>The <code>dotnet new webapi</code> command creates a new minimal API project named <code>BookStoreApi</code>. This project contains the necessary files and folders to get you started.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732623879052/3db8614b-7b27-43ce-ad84-9fa66001b535.png" alt="Minimal API Project Files   Structure  " width="2556" height="1370" loading="lazy"></p>
<p>Let's explore the project structure:</p>
<ul>
<li><p><code>Program.cs</code>: The entry point of the application, where the host is configured.</p>
</li>
<li><p><code>bookapi-minimal.sln</code>: The solution file that contains the project.</p>
</li>
<li><p><code>bookapi-minimal.http</code>: A file that contains sample HTTP requests to test the API.</p>
</li>
<li><p><code>bookapi-minimal.csproj</code>: The project file that contains the project configuration.</p>
</li>
<li><p><code>appsettings.json</code>: The configuration file that stores application settings.</p>
</li>
<li><p><code>appsettings.Development.json</code> : The configuration file for the development environment.</p>
</li>
</ul>
<p>When you open the program.cs file, you'll notice that the code is minimal. The <code>Program.cs</code> file contains the following code:</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

<span class="hljs-comment">// Add services to the container.</span>
<span class="hljs-comment">// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle</span>
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// Configure the HTTP request pipeline.</span>
<span class="hljs-keyword">if</span> (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

<span class="hljs-keyword">var</span> summaries = <span class="hljs-keyword">new</span>[]
{
    <span class="hljs-string">"Freezing"</span>, <span class="hljs-string">"Bracing"</span>, <span class="hljs-string">"Chilly"</span>, <span class="hljs-string">"Cool"</span>, <span class="hljs-string">"Mild"</span>, <span class="hljs-string">"Warm"</span>, <span class="hljs-string">"Balmy"</span>, <span class="hljs-string">"Hot"</span>, <span class="hljs-string">"Sweltering"</span>, <span class="hljs-string">"Scorching"</span>
};

app.MapGet(<span class="hljs-string">"/weatherforecast"</span>, () =&gt;
{
    <span class="hljs-keyword">var</span> forecast =  Enumerable.Range(<span class="hljs-number">1</span>, <span class="hljs-number">5</span>).Select(index =&gt;
        <span class="hljs-keyword">new</span> WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(<span class="hljs-number">-20</span>, <span class="hljs-number">55</span>),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    <span class="hljs-keyword">return</span> forecast;
})
.WithName(<span class="hljs-string">"GetWeatherForecast"</span>)
.WithOpenApi();

app.Run();

<span class="hljs-keyword">record</span> <span class="hljs-title">WeatherForecast</span>(<span class="hljs-title">DateOnly</span> <span class="hljs-title">Date</span>, <span class="hljs-title">int</span> <span class="hljs-title">TemperatureC</span>, <span class="hljs-title">string</span>? <span class="hljs-title">Summary</span>)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> TemperatureF =&gt; <span class="hljs-number">32</span> + (<span class="hljs-keyword">int</span>)(TemperatureC / <span class="hljs-number">0.5556</span>);
}
</code></pre>
<p>If you don't fully understand the code yet, don't worry—we'll cover it in detail in the upcoming sections. The key takeaway is that minimal APIs require very little code, which is one of their main advantages.</p>
<p>The default code sets up a simple weather forecast API that you can use to test your setup. It generates a list of weather forecasts and returns them when you make a <code>GET</code> request to the <code>/weatherforecast</code> endpoint. Also, the code includes Swagger UI to help you test the API.</p>
<p>Pay special attention to the <code>app.MapGet</code> method, which maps a route to a handler function. In this case, it maps the <code>/weatherforecast</code> route to a function that returns a list of weather forecasts. We'll use similar methods to create our own endpoints in the next sections.</p>
<p>Before we start creating our project folder structure, let's understand the HTTP methods in both Controller-based and Minimal APIs.</p>
<h2 id="heading-http-methods-in-controller-based-and-minimal-apis">HTTP Methods in Controller-based and Minimal APIs</h2>
<p>In a Controller-based approach, which is the traditional way of creating web APIs, you need to create a controller class and define methods for each HTTP method. For example:</p>
<ul>
<li><p>To create a <code>GET</code> method, you use the <code>[HttpGet]</code> attribute.</p>
</li>
<li><p>To create a <code>POST</code> method, you use the <code>[HttpPost]</code> attribute.</p>
</li>
<li><p>To create a <code>PUT</code> method, you use the <code>[HttpPut]</code> attribute.</p>
</li>
<li><p>To create a <code>DELETE</code> method, you use the <code>[HttpDelete]</code> attribute.</p>
</li>
</ul>
<p>This is how endpoints are created in a Controller-based approach.</p>
<p>In contrast, Minimal APIs use methods like <code>app.MapGet</code>, <code>app.MapPost</code>, <code>app.MapPut</code>, and <code>app.MapDelete</code> to create endpoints. This is the main difference between the two approaches: Controller-based APIs use attributes to define endpoints, while Minimal APIs use methods.</p>
<p>Now that you understand how to handle HTTP requests in both Controller-based and Minimal APIs, let's create our project folder structure.</p>
<p>Before we create our project folder structure, let's first run what we have. As we learned earlier, when you create a project with either Visual Studio or .NET CLI, it comes with a default WeatherForecast project which we can run and see on the UI. Let's run it to ensure everything works before we go on to create our project folder.</p>
<p>Run this command:</p>
<pre><code class="lang-bash">
dotnet run
</code></pre>
<p>You should see the following output:</p>
<pre><code class="lang-bash">info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5228
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Devolopemnt\Dotnet\bookapi-minimal
</code></pre>
<p>This means the application is running and listening on <a target="_blank" href="http://localhost:5228"><code>http://localhost:5228</code></a>. As I mentioned above, since we are using the <code>dotnet CLI</code> and Visual Studio Code, the application will not automatically open the browser for us. We need to do this manually.</p>
<p>Open your browser and navigate to <a target="_blank" href="http://localhost:5228/swagger/index.html"><code>http://localhost:5228/swagger/index.html</code></a> to see the default response from the API.</p>
<p>You should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732623894640/b882a1ee-3957-4958-8f59-20b44fe7fb7d.png" alt="Swagger UI " width="2436" height="1169" loading="lazy"></p>
<p>Now the next thing for us to do is find a way to structure our project and create the necessary files and folders to get us started.</p>
<h2 id="heading-minimal-api-project-files">Minimal API Project Files</h2>
<p>To organize our project, we will create a structured folder hierarchy. This will help keep our code clean and maintainable. Here is the folder structure we will use:</p>
<ul>
<li><p><strong>AppContext</strong>: Contains the database context and related configurations.</p>
</li>
<li><p><strong>Configurations</strong>: Holds Entity Framework Core configurations and seed data for the database.</p>
</li>
<li><p><strong>Contracts</strong>: Contains Data Transfer Objects (DTOs) used in our application.</p>
</li>
<li><p><strong>Endpoints</strong>: Where we define and configure our minimal API endpoints.</p>
</li>
<li><p><strong>Exceptions</strong>: Contains custom exception classes used in the project.</p>
</li>
<li><p><strong>Extensions</strong>: Holds extension methods that we will use throughout the project.</p>
</li>
<li><p><strong>Models</strong>: Contains business logic models.</p>
</li>
<li><p><strong>Services</strong>: Contains service classes that implement business logic.</p>
</li>
<li><p><strong>Interfaces</strong>: Holds interface definitions used to map our services.</p>
</li>
</ul>
<p>In Visual Studio Code, you can create this folder structure as follows:</p>
<pre><code class="lang-bash">- AppContext
- Configurations
- Contracts
- Endpoints
- Exceptions
- Extensions
- Models
- Services
- Interfaces
</code></pre>
<p>After setting up, your project folder structure should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732623997951/8118c444-0d28-4bb7-8cad-2a9fd88c8c25.png" alt="BookApi Project Folder Structure " width="1920" height="890" loading="lazy"></p>
<p>Now that our project Structure is set up we can go ahead and start writing our code. Let's start by creating our models.</p>
<h2 id="heading-how-to-create-the-models">How to Create the Models</h2>
<p>In this section, we will create models for our application. Models are the building blocks of our application, representing the data that our application will work with. For our example, we will create a model for a book.</p>
<p>To get started, create a folder named <code>Models</code> in your project directory. Inside this folder, create a file named <code>BookModel.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Models/BookModel.cs</span>


<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Models</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookModel</span>
    {
        <span class="hljs-keyword">public</span> Guid Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Author { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Description { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Category { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Language { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> TotalPages { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p>This <code>BookModel class</code> defines the properties that represent the details of a book, such as its <code>title</code>, <code>author</code>, <code>description</code>, <code>category</code>, <code>language</code>, and <code>total pages</code>. Each property is designed to hold specific information about the book, making it easy to manage and manipulate book data within our application.</p>
<p>Now that we have created our model, let's create our database context.</p>
<h2 id="heading-how-to-create-the-database-context">How to Create the Database Context</h2>
<p>The database context is a class that represents a session with the database. It’s responsible for interacting with the database and executing database operations. In our application, we will use Entity Framework Core to interact with our database.</p>
<h3 id="heading-install-the-required-packages">Install the Required Packages</h3>
<p>Before creating our database context, we need to install the following packages:</p>
<ul>
<li><p><a target="_blank" href="http://Microsoft.EntityFrameworkCore.Design"><code>Microsoft.EntityFrameworkCore.Design</code></a></p>
</li>
<li><p><code>Microsoft.EntityFrameworkCore</code></p>
</li>
<li><p><code>Microsoft.EntityFrameworkCore.SqlServer</code></p>
</li>
<li><p><a target="_blank" href="http://Microsoft.EntityFrameworkCore.Tools"><code>Microsoft.EntityFrameworkCore.Tools</code></a></p>
</li>
<li><p><code>FluentValidation.DependencyInjectionExtensions</code></p>
</li>
</ul>
<p>You can install these packages using the following commands:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package FluentValidation.DependencyInjectionExtensions
</code></pre>
<h3 id="heading-verify-package-installation">Verify Package Installation</h3>
<p>To verify that the packages are installed, open the <code>bookapi-minimal.csproj</code> file in your project's root directory. You should see the installed packages listed as follows:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk.Web"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">TargetFramework</span>&gt;</span>net8.0<span class="hljs-tag">&lt;/<span class="hljs-name">TargetFramework</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Nullable</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">Nullable</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ImplicitUsings</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">ImplicitUsings</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">RootNamespace</span>&gt;</span>bookapi_minimal<span class="hljs-tag">&lt;/<span class="hljs-name">RootNamespace</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"FluentValidation.DependencyInjectionExtensions"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"11.9.2"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.AspNetCore.OpenApi"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.6"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Design"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IncludeAssets</span>&gt;</span>runtime; build; native; contentfiles; analyzers; buildtransitive<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeAssets</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PrivateAssets</span>&gt;</span>all<span class="hljs-tag">&lt;/<span class="hljs-name">PrivateAssets</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.SqlServer"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Tools"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IncludeAssets</span>&gt;</span>runtime; build; native; contentfiles; analyzers; buildtransitive<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeAssets</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PrivateAssets</span>&gt;</span>all<span class="hljs-tag">&lt;/<span class="hljs-name">PrivateAssets</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Swashbuckle.AspNetCore"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"6.4.0"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">Project</span>&gt;</span>
</code></pre>
<p>This confirms that the packages have been successfully installed.</p>
<p>Now let's create our database context.</p>
<p>In the AppContext folder, create a new file named <code>ApplicationContext.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// AppContext/ApplicationContext.cs</span>

<span class="hljs-keyword">using</span> bookapi_minimal.Models;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.AppContext</span>
{

    <span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">ApplicationContext</span>(<span class="hljs-params">DbContextOptions&lt;ApplicationContext&gt; options</span>) : <span class="hljs-title">DbContext</span>(<span class="hljs-params">options</span>)</span>
    {

        <span class="hljs-comment">// Default schema for the database context</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> DefaultSchema = <span class="hljs-string">"bookapi"</span>;


       <span class="hljs-comment">// DbSet to represent the collection of books in our database</span>
        <span class="hljs-keyword">public</span> DbSet&lt;BookModel&gt; Books { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-comment">// Constructor to configure the database context</span>

        <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnModelCreating</span>(<span class="hljs-params">ModelBuilder modelBuilder</span>)</span>
        {
            <span class="hljs-keyword">base</span>.OnModelCreating(modelBuilder);
            modelBuilder.HasDefaultSchema(DefaultSchema);

            modelBuilder.ApplyConfigurationsFromAssembly(<span class="hljs-keyword">typeof</span>(ApplicationContext).Assembly);

            modelBuilder.ApplyConfigurationsFromAssembly(<span class="hljs-keyword">typeof</span>(ApplicationContext).Assembly);

        }

    }
}
</code></pre>
<p>Let's break down the code above:</p>
<ul>
<li><p>We define a class named <code>ApplicationContext</code> that inherits from <code>DbContext</code>. The <code>DbContext</code> class is part of Entity Framework Core and represents a session with the database.</p>
</li>
<li><p>The constructor accepts an instance of <code>DbContextOptions&lt;ApplicationContext&gt;</code>. This constructor is used to configure the database context options.</p>
</li>
<li><p>We define a property named <code>Books</code> type <code>DbSet&lt;BookModel&gt;</code>. This property represents the collection of books in our database.</p>
</li>
<li><p>We override the <code>OnModelCreating</code> method to configure the database schema and apply any configurations defined in our application.</p>
</li>
</ul>
<p>Now that we have created our database context, let's create our extension method and register our database context in the dependency injection container.</p>
<h3 id="heading-create-an-extension-method">Create an Extension Method</h3>
<p>Before we create the extension method, let's understand what an extension method is in the context of <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core.</p>
<p>An extension method is a static method that adds new functionality to an existing type without modifying the original type. In <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core, extension methods are commonly used to extend the functionality of the <code>IServiceCollection</code> interface, which is used to register services in the dependency injection container.</p>
<p>Services are components that provide functionality to an application, such as database access, logging, and configuration. By creating an extension method for the <code>IServiceCollection</code> interface, you can simplify the process of registering your services in the dependency injection container.</p>
<p>Instead of putting everything in the <code>Program.cs</code> file, we will create an extension method to register our services in the dependency injection container. This will help us keep our code clean and organized.</p>
<p>In the <code>Extensions</code> folder, create a new file named <code>ServiceExtensions.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Reflection;
<span class="hljs-keyword">using</span> bookapi_minimal.AppContext;
<span class="hljs-keyword">using</span> FluentValidation;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Extensions</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ServiceExtensions</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AddApplicationServices</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IHostApplicationBuilder builder</span>)</span>
        {
            <span class="hljs-keyword">if</span> (builder == <span class="hljs-literal">null</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(builder));
            <span class="hljs-keyword">if</span> (builder.Configuration == <span class="hljs-literal">null</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(builder.Configuration));

            <span class="hljs-comment">// Adding the database context</span>
            builder.Services.AddDbContext&lt;ApplicationContext&gt;(configure =&gt;
            {
                configure.UseSqlServer(builder.Configuration.GetConnectionString(<span class="hljs-string">"sqlConnection"</span>));
            });

            <span class="hljs-comment">// Adding validators from the current assembly</span>
            builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        }
    }
}
</code></pre>
<p>Let's break down the code above:</p>
<ul>
<li><p>We define a static class named <code>ServiceExtensions</code> that contains an extension method named <code>AddApplicationServices</code>. This method extends the <code>IHostApplicationBuilder</code> interface, which is used to configure the application's request processing pipeline.</p>
</li>
<li><p>The <code>AddApplicationServices</code> method accepts an instance of <code>IHostApplicationBuilder</code> as a parameter. This parameter is used to access the application's configuration and services.</p>
</li>
<li><p>We add the <code>ApplicationContext</code> to the dependency injection container and configure it to use SQL Server as the database provider. We retrieve the connection string from the <code>appsettings.json</code> file using the <code>GetConnectionString</code> method.</p>
</li>
<li><p>We add <code>validators</code> from the current <code>assembly</code> using the <code>AddValidatorsFromAssembly</code> method. This method scans the current assembly for classes that implement the IValidator interface and registers them in the dependency injection container.</p>
</li>
</ul>
<p>Next, we need to add the connection string to the <code>appsettings.json</code> file. Add the following code to your <code>appsettings.json</code> file:</p>
<pre><code class="lang-json">{ 
     <span class="hljs-attr">"ConnectionStrings"</span>: {
    <span class="hljs-attr">"sqlConnection"</span>: <span class="hljs-string">"Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"</span>
  }
  }
</code></pre>
<p>Make sure to replace <code>your_password</code> it with your actual SQL Server password.</p>
<p>Your <code>appsettings.json</code> file should look like this:</p>
<pre><code class="lang-json">
{
  <span class="hljs-attr">"Logging"</span>: {
    <span class="hljs-attr">"LogLevel"</span>: {
      <span class="hljs-attr">"Default"</span>: <span class="hljs-string">"Information"</span>,
      <span class="hljs-attr">"Microsoft.AspNetCore"</span>: <span class="hljs-string">"Warning"</span>
    }
  },
  <span class="hljs-attr">"ConnectionStrings"</span>: {
    <span class="hljs-attr">"sqlConnection"</span>: <span class="hljs-string">"Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"</span>
  },
  <span class="hljs-attr">"AllowedHosts"</span>: <span class="hljs-string">"*"</span>
}
</code></pre>
<p>Congratulations! You have successfully created the database context, extension method, and connection string for your application. In the next section, we will create a Contract.</p>
<h2 id="heading-how-to-create-a-contract">How to Create a Contract</h2>
<p>Contracts are Data Transfer Objects (DTOs) that define the structure of the data exchanged between the client and the server. In our application, we will create contracts to represent the data sent and received by our API endpoints.</p>
<p>Here are the contracts we are going to create:</p>
<ul>
<li><p>CreateBookRequest: This represents the data sent when creating a new book.</p>
</li>
<li><p>UpdateBookRequest: tHI Represents the data sent when updating an existing book.</p>
</li>
<li><p>BookResponse: Represents the data returned when retrieving a book.</p>
</li>
<li><p>ErrorResponse: Represents the error response returned when an exception occurs.</p>
</li>
<li><p>ApiResponse: Represents the response returned by the API.</p>
</li>
</ul>
<p>In the <code>Contracts</code> folder, create a new file named <code>CreateBookRequest</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Contracts/CreateBookRequest.cs</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Contracts</span>
{

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">CreateBookRequest</span>
    { 

        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Author { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Description { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Category { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Language { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> TotalPages { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
    }
}
</code></pre>
<p>In the <code>Contracts</code> folder, create a new file named <code>UpdateBookRequest</code> and add the following code:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Contracts/UpdateBookRequest.cs</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Contracts</span>
{

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">UpdateBookRequest</span>
    {
       <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Author { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Description { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Category { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Language { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> TotalPages { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    }
}
</code></pre>
<p>In the <code>Contracts</code> folder, create a new file named <code>BookResponse</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Contracts/BookResponse.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Contracts</span>
{

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">BookResponse</span>
    {
        <span class="hljs-keyword">public</span> Guid Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Author { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Description { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Category { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Language { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> TotalPages { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    }
}
</code></pre>
<p>In the <code>Contracts</code> folder, create a new file named <code>ErrorResponse</code> and add the following code:</p>
<pre><code class="lang-csharp">

<span class="hljs-comment">// Contracts/ErrorResponse.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Contracts</span>
{

        <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">ErrorResponse</span>
    {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> StatusCode { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Message { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    }

}
</code></pre>
<p>In the <code>Contracts</code> folder, create a new file named <code>ApiResponse</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Contracts/ApiResponse.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Contracts</span>
{

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ApiResponse</span>&lt;<span class="hljs-title">T</span>&gt;
    {
        <span class="hljs-keyword">public</span> T Data { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Message { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApiResponse</span>(<span class="hljs-params">T data, <span class="hljs-keyword">string</span> message</span>)</span>
        {
            Data = data;
            Message = message;
        }
    }
}
</code></pre>
<p>These contracts help us define the structure of the data exchanged between the client and the server, making it easier to work with the data in our application.</p>
<p>In the next section, we will create services to implement the business logic of our application.</p>
<h2 id="heading-how-to-add-services">How to Add Services</h2>
<p>Services are components that provide functionality to an application. In our application, we will create services to implement the business logic of our application. We will create services to handle CRUD operations for books, validate book data, and handle exceptions.</p>
<p>In ASP.NET Core, services are registered in the dependency injection container and can be injected into other components, such as controllers and endpoints, But this is a minimal API so we will inject the services directly into the endpoints.</p>
<p>Let's create an interface for our services. In the <code>Interfaces</code> folder, create a new file named <code>IBookService.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"> <span class="hljs-comment">// Interfaces/IBookService.cs</span>



<span class="hljs-keyword">using</span> bookapi_minimal.Contracts;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Interfaces</span>
{
      <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IBookService</span>
    {
        <span class="hljs-function">Task&lt;BookResponse&gt; <span class="hljs-title">AddBookAsync</span>(<span class="hljs-params">CreateBookRequest createBookRequest</span>)</span>;
        <span class="hljs-function">Task&lt;BookResponse&gt; <span class="hljs-title">GetBookByIdAsync</span>(<span class="hljs-params">Guid id</span>)</span>;
        Task&lt;IEnumerable&lt;BookResponse&gt;&gt; GetBooksAsync();
        <span class="hljs-function">Task&lt;BookResponse&gt; <span class="hljs-title">UpdateBookAsync</span>(<span class="hljs-params">Guid id,  UpdateBookRequest  updateBookRequest</span>)</span>;
        <span class="hljs-function">Task&lt;<span class="hljs-keyword">bool</span>&gt; <span class="hljs-title">DeleteBookAsync</span>(<span class="hljs-params">Guid id</span>)</span>;
    }
}
</code></pre>
<p>Let's break down the code above: We have defined an interface named <code>IBookService</code> that contains methods to handle CRUD operations for books. The interface defines the following methods:</p>
<ul>
<li><p><code>AddBookAsync</code>: Adds a new book to the database.</p>
</li>
<li><p><code>GetBookByIdAsync</code>: Retrieves a book by its ID.</p>
</li>
<li><p><code>GetBooksAsync</code>: Retrieves all books from the database.</p>
</li>
<li><p><code>UpdateBookAsync</code>: Updates an existing book.</p>
</li>
</ul>
<p>We are using the Contract we created earlier in the <code>Contracts</code> folder. The <code>IBookService</code> interface defines the structure of the methods that will be implemented by the service classes. This helps us separate the interface from the implementation, making it easier to maintain and test our code.</p>
<p>Now that we have created the interface, let's create the service class that implements the interface.</p>
<h3 id="heading-how-to-implement-the-book-service">How to Implement the Book Service</h3>
<p>This service will implement the <code>IBookService</code> interface and provide the business logic for our application. In the <code>Services</code> folder, create a new file named <code>BookService.cs</code> . Your initial file should look like this:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookService</span>
    {

    }
}
</code></pre>
<p>The first thing we need to do is add the interface to the <code>BookService</code> class. Update the <code>BookService</code> class to implement the <code>IBookService</code> interface as follows:</p>
<pre><code class="lang-csharp">

<span class="hljs-comment">// Services/BookService.cs</span>



<span class="hljs-keyword">using</span> bookapi_minimal.Interfaces;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookService</span>:<span class="hljs-title">IBookService</span>
    {

    }
}
</code></pre>
<p>When you do this, your VS Code might show an error because we have not implemented the methods in the interface. Let's go ahead and implement the methods in the <code>BookService</code> class.</p>
<p>In VS Code you can use the <code>Ctrl + .</code> shortcut to implement the methods in the interface. Then you will see the following code generated for you:</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">using</span> bookapi_minimal.Contracts;
<span class="hljs-keyword">using</span> bookapi_minimal.Interfaces;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Services</span>
{
     <span class="hljs-comment">// Service class for managing books</span>
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookService</span> : <span class="hljs-title">IBookService</span>
   {
       <span class="hljs-comment">// Method to add a new book to the database</span>
       <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;BookResponse&gt; <span class="hljs-title">AddBookAsync</span>(<span class="hljs-params">CreateBookRequest createBookRequest</span>)</span>
       {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
       }

      <span class="hljs-comment">// Method to Delete a book from the database</span>
       <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;<span class="hljs-keyword">bool</span>&gt; <span class="hljs-title">DeleteBookAsync</span>(<span class="hljs-params">Guid id</span>)</span>
       {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
       }

       <span class="hljs-comment">// Method to Get a book from the database by its ID</span>

       <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;BookResponse&gt; <span class="hljs-title">GetBookByIdAsync</span>(<span class="hljs-params">Guid id</span>)</span>
       {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
       }

      <span class="hljs-comment">// Method to Get all books from the database</span>
       <span class="hljs-keyword">public</span> Task&lt;IEnumerable&lt;BookResponse&gt;&gt; GetBooksAsync()
       {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
       }

       <span class="hljs-comment">// Method to Update a book in the database</span>
       <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;BookResponse&gt; <span class="hljs-title">UpdateBookAsync</span>(<span class="hljs-params">Guid id, UpdateBookRequest updateBookRequest</span>)</span>
       {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotImplementedException();
       }
   }
}
</code></pre>
<p>Now you can see that the methods in the interface have been implemented in the <code>BookService</code> class. We will implement the business logic for each method in the next section.</p>
<p>Before we do that, let's add the necessary dependencies to the <code>BookService</code> class. We need to inject the <code>ApplicationContext</code> and <code>ILogger</code> dependencies into the <code>BookService</code> class. <code>ApplicationContext</code> is used to interact with the database, while <code>ILogger</code> is used for logging.</p>
<p>To inject the dependencies, update the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-comment">// ...</span>
 <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationContext _context; <span class="hljs-comment">// Database context</span>
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;BookService&gt; _logger; <span class="hljs-comment">// Logger for logging information and errors</span>

<span class="hljs-comment">//..</span>
</code></pre>
<p>Since we have added the dependencies, we need to update the <code>BookService</code> constructor to accept the dependencies. Update the <code>BookService</code> constructor as follows:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-comment">// ...</span>

  <span class="hljs-comment">// Constructor to initialize the database context and logger</span>
 <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookService</span>(<span class="hljs-params">ApplicationContext context, ILogger&lt;BookService&gt; logger</span>)</span>
 {
            _context = context;
            _logger = logger;
}

<span class="hljs-comment">// ...</span>
</code></pre>
<p>Now that we have added the dependencies and updated the constructor, we can implement the business logic for each method in the <code>BookService</code> class.</p>
<p>Let's create logic for the CREATE, READ, UPDATE, and DELETE operations in the <code>BookService</code> class.</p>
<h3 id="heading-how-to-implement-the-addbookasync-method">How to Implement the <code>AddBookAsync</code> Method</h3>
<p>As I mentioned earlier, we’ll use the <code>AddBookAsync</code> method to add a new book to the database. In this method, we will create a new book entity, map the data from the <code>CreateBookRequest</code> object to the book entity, and save the book entity to the database. We will also return the book entity as an <code>BookResponse</code> object.</p>
<p>Update the <code>AddBookAsync</code> method in the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-comment">// ...</span>
 <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Add a new book</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="createBookRequest"&gt;</span>Book request to be added<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the created book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt; <span class="hljs-title">AddBookAsync</span>(<span class="hljs-params">CreateBookRequest createBookRequest</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">new</span> BookModel
                {
                    Title = createBookRequest.Title,
                    Author = createBookRequest.Author,
                    Description = createBookRequest.Description,
                    Category = createBookRequest.Category,
                    Language = createBookRequest.Language,
                    TotalPages = createBookRequest.TotalPages
                };

                <span class="hljs-comment">// Add the book to the database</span>
                _context.Books.Add(book);
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">"Book added successfully."</span>);

                <span class="hljs-comment">// Return the details of the created book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error adding book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }
<span class="hljs-comment">// ...</span>
</code></pre>
<p>In this code, we are creating a new book entity from the <code>CreateBookRequest</code> object, mapping the data from the <code>CreateBookRequest</code> object to the book entity, saving the book entity to the database, and returning the book entity as a <code>BookResponse</code> object.</p>
<p>We are also logging information and errors using the <code>ILogger</code> dependency. If an exception occurs during the process, we log the error message and rethrow the exception.</p>
<p>Now that we have implemented the <code>AddBookAsync</code> method, let's implement the <code>GetBookByIdAsync</code> method.</p>
<h3 id="heading-how-to-implement-the-getbookbyidasync-method">How to Implement the <code>GetBookByIdAsync</code> Method</h3>
<p>The <code>GetBookByIdAsync</code> method is used to retrieve a book by its ID from the database. In this method, we will query the database for the book with the specified ID, map the book entity to a <code>BookResponse</code> object, and return the <code>BookResponse</code> object.</p>
<p>Update the <code>GetBookByIdAsync</code> method in the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-comment">//... </span>

    <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Get a book by its ID</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt;  <span class="hljs-title">GetBookByIdAsync</span>(<span class="hljs-params">Guid id</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the book by its ID</span>
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
                }

                <span class="hljs-comment">// Return the details of the book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error retrieving book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }

<span class="hljs-comment">//...</span>
</code></pre>
<p>In this code, we are querying the database for the book with the specified ID, mapping the book entity to a <code>BookResponse</code> object, and returning the <code>BookResponse</code> object. We are also logging information and errors using the <code>ILogger</code> dependency.</p>
<p>If the book with the specified ID is not found, we log a warning message and return null. If an exception occurs during the process, we log the error message and rethrow the exception.</p>
<p>Now that we have implemented the <code>GetBookByIdAsync</code> method, let's implement the <code>GetBooksAsync</code> method.</p>
<h3 id="heading-how-to-implement-the-getbooksasync-method">How to Implement the <code>GetBooksAsync</code> Method</h3>
<p>The <code>GetBooksAsync</code> method is used to retrieve all books from the database. In this method, we will query the database for all books, map each book entity to a <code>BookResponse</code> object, and return a list of <code>BookResponse</code> objects.</p>
<p>Update the <code>GetBooksAsync</code> method in the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp">

<span class="hljs-comment">// Services/BookService.cs</span>

<span class="hljs-comment">//... </span>


  <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Get all books</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>List of all books<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;BookResponse&gt;&gt; GetBooksAsync()
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Get all books from the database</span>
                <span class="hljs-keyword">var</span> books = <span class="hljs-keyword">await</span> _context.Books.ToListAsync();

                <span class="hljs-comment">// Return the details of all books</span>
                <span class="hljs-keyword">return</span> books.Select(book =&gt; <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                });
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error retrieving books: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }
<span class="hljs-comment">//...</span>
</code></pre>
<p>Here, we are querying the database for all books, mapping each book entity to an <code>BookResponse</code> object, and returning a list of <code>BookResponse</code> objects. We are also logging information and errors using the <code>ILogger</code> dependency. If an exception occurs during the process, we log the error message and rethrow the exception.</p>
<p>Now that we have implemented the <code>GetBooksAsync</code> method, let's implement the <code>UpdateBookAsync</code> method.</p>
<h3 id="heading-how-to-implement-the-updatebookasync-method">How to Implement the <code>UpdateBookAsync</code> Method</h3>
<p>The <code>UpdateBookAsync</code> method is used to update an existing book in the database. In this method, we will query the database for the book with the specified ID, update the book entity with the data from the <code>UpdateBookRequest</code> object, save the updated book entity to the database, and return the updated book entity as a <code>BookResponse</code> object.</p>
<p>Update the <code>UpdateBookAsync</code> method in the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Services/BookService.cs</span>
 <span class="hljs-comment">//...</span>
 <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Update an existing book</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book to be updated<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="book"&gt;</span>Updated book model<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the updated book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt; <span class="hljs-title">UpdateBookAsync</span>(<span class="hljs-params">Guid id, UpdateBookRequest book</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the existing book by its ID</span>
                <span class="hljs-keyword">var</span> existingBook = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (existingBook == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
                }

                <span class="hljs-comment">// Update the book details</span>
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                <span class="hljs-comment">// Save the changes to the database</span>
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">"Book updated successfully."</span>);

                <span class="hljs-comment">// Return the details of the updated book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = existingBook.Id,
                    Title = existingBook.Title,
                    Author = existingBook.Author,
                    Description = existingBook.Description,
                    Category = existingBook.Category,
                    Language = existingBook.Language,
                    TotalPages = existingBook.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error updating book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }
<span class="hljs-comment">//...</span>
</code></pre>
<p>Here, we are querying the database for the book with the specified ID, updating the book entity with the data from the <code>UpdateBookRequest</code> object, saving the updated book entity to the database, and returning the updated book entity as a <code>BookResponse</code> object. We are also logging information and errors using the <code>ILogger</code> dependency.</p>
<p>If the book with the specified ID is not found, we log a warning message and return null. If an exception occurs during the process, we log the error message and rethrow the exception.</p>
<p>Now that we have implemented the <code>UpdateBookAsync</code> method, let's implement the <code>DeleteBookAsync</code> method.</p>
<h3 id="heading-how-to-implement-the-deletebookasync-method">How to Implement the <code>DeleteBookAsync</code> Method</h3>
<p>The <code>DeleteBookAsync</code> method is used to delete an existing book from the database. In this method, we will query the database for the book with the specified ID, remove the book entity from the database, and return a boolean value indicating whether the book was successfully deleted.</p>
<p>Update the <code>DeleteBookAsync</code> method in the <code>BookService</code> class as follows:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Services/BookService.cs</span>

 <span class="hljs-comment">//...</span>


<span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Delete a book by its ID</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book to be deleted<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>True if the book was deleted, false otherwise<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">bool</span>&gt; <span class="hljs-title">DeleteBookAsync</span>(<span class="hljs-params">Guid id</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the book by its ID</span>
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
                }

                <span class="hljs-comment">// Remove the book from the database</span>
                _context.Books.Remove(book);
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> deleted successfully."</span>);
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error deleting book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }
<span class="hljs-comment">//...</span>
</code></pre>
<p>In this code, we are querying the database for the book with the specified ID, removing the book entity from the database, and returning a boolean value indicating whether the book was successfully deleted. We are also logging information and errors using the <code>ILogger</code> dependency.</p>
<p>If the book with the specified ID is not found, we log a warning message and return false. If an exception occurs during the process, we log the error message and rethrow the exception.</p>
<p>Now you have successfully implemented the business logic for the <code>AddBookAsync</code>, <code>GetBookByIdAsync</code>, <code>GetBooksAsync</code>, <code>UpdateBookAsync</code>, and <code>DeleteBookAsync</code> methods in the <code>BookService</code> class. These methods handle the CRUD operations for books, validate book data, and handle exceptions. By now, your <code>BookService</code> class should look like this:</p>
<pre><code class="lang-csharp">

<span class="hljs-keyword">using</span> bookapi_minimal.AppContext;
<span class="hljs-keyword">using</span> bookapi_minimal.Contracts;
<span class="hljs-keyword">using</span> bookapi_minimal.Interfaces;
<span class="hljs-keyword">using</span> bookapi_minimal.Models;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Services</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookService</span> : <span class="hljs-title">IBookService</span>
    {
          <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationContext _context; <span class="hljs-comment">// Database context</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;BookService&gt; _logger; <span class="hljs-comment">// Logger for logging information and error</span>
          <span class="hljs-comment">// Constructor to initialize the database context and logger</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookService</span>(<span class="hljs-params">ApplicationContext context, ILogger&lt;BookService&gt; logger</span>)</span>
        {
            _context = context;
            _logger = logger;
        }

           <span class="hljs-comment"><span class="hljs-doctag">///</span> Add a new book</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="createBookRequest"&gt;</span>Book request to be added<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the created book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt; <span class="hljs-title">AddBookAsync</span>(<span class="hljs-params">CreateBookRequest createBookRequest</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">new</span> BookModel
                {
                    Title = createBookRequest.Title,
                    Author = createBookRequest.Author,
                    Description = createBookRequest.Description,
                    Category = createBookRequest.Category,
                    Language = createBookRequest.Language,
                    TotalPages = createBookRequest.TotalPages
                };

                <span class="hljs-comment">// Add the book to the database</span>
                _context.Books.Add(book);
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">"Book added successfully."</span>);

                <span class="hljs-comment">// Return the details of the created book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error adding book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }

          <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Get a book by its ID</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt;  <span class="hljs-title">GetBookByIdAsync</span>(<span class="hljs-params">Guid id</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the book by its ID</span>
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
                }

                <span class="hljs-comment">// Return the details of the book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error retrieving book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }



  <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Get all books</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>List of all books<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;BookResponse&gt;&gt; GetBooksAsync()
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Get all books from the database</span>
                <span class="hljs-keyword">var</span> books = <span class="hljs-keyword">await</span> _context.Books.ToListAsync();

                <span class="hljs-comment">// Return the details of all books</span>
                <span class="hljs-keyword">return</span> books.Select(book =&gt; <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = book.Id,
                    Title = book.Title,
                    Author = book.Author,
                    Description = book.Description,
                    Category = book.Category,
                    Language = book.Language,
                    TotalPages = book.TotalPages
                });
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error retrieving books: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }


         <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Update an existing book</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book to be updated<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="book"&gt;</span>Updated book model<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>Details of the updated book<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;BookResponse&gt; <span class="hljs-title">UpdateBookAsync</span>(<span class="hljs-params">Guid id, UpdateBookRequest book</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the existing book by its ID</span>
                <span class="hljs-keyword">var</span> existingBook = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (existingBook == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
                }

                <span class="hljs-comment">// Update the book details</span>
                existingBook.Title = book.Title;
                existingBook.Author = book.Author;
                existingBook.Description = book.Description;
                existingBook.Category = book.Category;
                existingBook.Language = book.Language;
                existingBook.TotalPages = book.TotalPages;

                <span class="hljs-comment">// Save the changes to the database</span>
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">"Book updated successfully."</span>);

                <span class="hljs-comment">// Return the details of the updated book</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BookResponse
                {
                    Id = existingBook.Id,
                    Title = existingBook.Title,
                    Author = existingBook.Author,
                    Description = existingBook.Description,
                    Category = existingBook.Category,
                    Language = existingBook.Language,
                    TotalPages = existingBook.TotalPages
                };
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error updating book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }



        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> Delete a book by its ID</span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;/summary&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;param name="id"&gt;</span>ID of the book to be deleted<span class="hljs-doctag">&lt;/param&gt;</span></span>
        <span class="hljs-comment"><span class="hljs-doctag">///</span> <span class="hljs-doctag">&lt;returns&gt;</span>True if the book was deleted, false otherwise<span class="hljs-doctag">&lt;/returns&gt;</span></span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">bool</span>&gt; <span class="hljs-title">DeleteBookAsync</span>(<span class="hljs-params">Guid id</span>)</span>
        {
            <span class="hljs-keyword">try</span>
            {
                <span class="hljs-comment">// Find the book by its ID</span>
                <span class="hljs-keyword">var</span> book = <span class="hljs-keyword">await</span> _context.Books.FindAsync(id);
                <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                {
                    _logger.LogWarning(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> not found."</span>);
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
                }

                <span class="hljs-comment">// Remove the book from the database</span>
                _context.Books.Remove(book);
                <span class="hljs-keyword">await</span> _context.SaveChangesAsync();
                _logger.LogInformation(<span class="hljs-string">$"Book with ID <span class="hljs-subst">{id}</span> deleted successfully."</span>);
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
            }
            <span class="hljs-keyword">catch</span> (Exception ex)
            {
                _logger.LogError(<span class="hljs-string">$"Error deleting book: <span class="hljs-subst">{ex.Message}</span>"</span>);
                <span class="hljs-keyword">throw</span>;
            }
        }

    }
}
</code></pre>
<p>Congratulations! You have successfully implemented the business logic for the <code>AddBookAsync</code>, <code>GetBookByIdAsync</code>, <code>GetBooksAsync</code>, <code>UpdateBookAsync</code>, and <code>DeleteBookAsync</code> methods in the <code>BookService</code> class.</p>
<p>There's one thing we need to do: we need to register the service in our extension method. Let's go ahead and do that.</p>
<p>In your <code>ServiceExtensions.cs</code> file, add the following code:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Extensions/ServiceExtensions.cs</span>

<span class="hljs-comment">//..</span>

 builder.Services.AddScoped&lt;IBookService, BookService&gt;();
<span class="hljs-comment">//...</span>
</code></pre>
<p>This will register the <code>BookService</code> class as a scoped service. This means that the service will be created once per request and disposed of after the request is complete.</p>
<p>Now that we have the service working, let's go ahead and create the exception classes.</p>
<h2 id="heading-how-to-create-exceptions">How to Create Exceptions</h2>
<p>Properly handling exceptions is crucial for ensuring the stability and reliability of an application. In the context of ASP.NET Core, there are two main types of exceptions:</p>
<ul>
<li><p><strong>System Exceptions</strong>: These are exceptions thrown by the .NET runtime or the underlying system.</p>
</li>
<li><p><strong>Application Exceptions</strong>: These are exceptions thrown by the application code to handle specific errors or conditions.</p>
</li>
</ul>
<p>In ASP.NET Core with .NET 8, a new feature called global exception handling was introduced. This feature allows you to handle exceptions globally in your application, making it easier to manage errors and provide a consistent user experience.</p>
<p>In our application, we will create custom exception classes to handle specific errors and conditions. We’ll also leverage the global exception handling feature to manage exceptions globally, ensuring a uniform approach to error handling across the entire application.</p>
<p>We are going to create the following exception classes:</p>
<ul>
<li><p><code>NoBookFoundException</code>: Thrown when a book with the specified ID is not found.</p>
</li>
<li><p><code>BookDoesNotExistException</code>: Thrown when a book with the specified ID does not exist.</p>
</li>
<li><p><code>GlobalExceptionHandler</code>: Handles exceptions globally in the application.</p>
</li>
</ul>
<p>In the <code>Exceptions</code> folder, create a new file named <code>NoBookFoundException.cs</code> and add the following code:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Exceptions/NoBookFoundException.cs</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Exceptions</span>
{

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">NoBookFoundException</span> : <span class="hljs-title">Exception</span>
    {

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">NoBookFoundException</span>(<span class="hljs-params"></span>) : <span class="hljs-title">base</span>(<span class="hljs-params"><span class="hljs-string">"No books found"</span></span>)</span>
        {}
    }
}
</code></pre>
<p>In this code, we are creating a custom exception class named <code>NoBookFoundException</code> that inherits from the <code>Exception</code> class. The <code>NoBookFoundException</code> class is used to handle the scenario where no books are found in the database. We are also providing a custom error message for the exception.</p>
<p>In the <code>Exceptions</code> folder, create a new file named <code>BookDoesNotExistException.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Exceptions</span>
{
     <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookDoesNotExistException</span> : <span class="hljs-title">Exception</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookDoesNotExistException</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> id</span>) : <span class="hljs-title">base</span>(<span class="hljs-params"><span class="hljs-string">$"Book with id <span class="hljs-subst">{id}</span> does not exist"</span></span>)</span>
        {
            <span class="hljs-keyword">this</span>.id = id;
        } 

    }
}
</code></pre>
<p>In this code, we are creating a custom exception class named <code>BookDoesNotExistException</code> that inherits from the <code>Exception</code> class. The <code>BookDoesNotExistException</code> class is used to handle the scenario where a book with the specified ID does not exist in the database. We are also providing a custom error message for the exception.</p>
<p>In the <code>Exceptions</code> folder, create a new file named <code>GlobalExceptionHandler.cs</code> and add the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Exceptions/GlobalExceptionHandler.cs</span>

<span class="hljs-keyword">using</span> System.Net;
<span class="hljs-keyword">using</span> bookapi_minimal.Contracts;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Diagnostics;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Exceptions</span>
{

   <span class="hljs-comment">// Global exception handler class implementing IExceptionHandler</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">GlobalExceptionHandler</span> : <span class="hljs-title">IExceptionHandler</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;GlobalExceptionHandler&gt; _logger;

        <span class="hljs-comment">// Constructor to initialize the logger</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">GlobalExceptionHandler</span>(<span class="hljs-params">ILogger&lt;GlobalExceptionHandler&gt; logger</span>)</span>
        {
            _logger = logger;
        }

        <span class="hljs-comment">// Method to handle exceptions asynchronously</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> ValueTask&lt;<span class="hljs-keyword">bool</span>&gt; <span class="hljs-title">TryHandleAsync</span>(<span class="hljs-params">
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken</span>)</span>
        {
            <span class="hljs-comment">// Log the exception details</span>
            _logger.LogError(exception, <span class="hljs-string">"An error occurred while processing your request"</span>);

            <span class="hljs-keyword">var</span> errorResponse = <span class="hljs-keyword">new</span> ErrorResponse
            {
                Message = exception.Message,
                Title = exception.GetType().Name
            };

            <span class="hljs-comment">// Determine the status code based on the type of exception</span>
            <span class="hljs-keyword">switch</span> (exception)
            {
                <span class="hljs-keyword">case</span> BadHttpRequestException:
                    errorResponse.StatusCode = (<span class="hljs-keyword">int</span>)HttpStatusCode.BadRequest;
                    <span class="hljs-keyword">break</span>;

                <span class="hljs-keyword">case</span> NoBookFoundException:
                <span class="hljs-keyword">case</span> BookDoesNotExistException:
                    errorResponse.StatusCode = (<span class="hljs-keyword">int</span>)HttpStatusCode.NotFound;
                    <span class="hljs-keyword">break</span>;

                <span class="hljs-keyword">default</span>:
                    errorResponse.StatusCode = (<span class="hljs-keyword">int</span>)HttpStatusCode.InternalServerError;
                    <span class="hljs-keyword">break</span>;
            }

            <span class="hljs-comment">// Set the response status code</span>
            httpContext.Response.StatusCode = errorResponse.StatusCode;

            <span class="hljs-comment">// Write the error response as JSON</span>
            <span class="hljs-keyword">await</span> httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);

            <span class="hljs-comment">// Return true to indicate that the exception was handled</span>
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    }
}
</code></pre>
<p>Let's break down the code above:</p>
<ul>
<li><p>We define a class named <code>GlobalExceptionHandler</code> that implements the <code>IExceptionHandler</code> interface. The <code>IExceptionHandler</code> interface is used to handle exceptions globally in the application.</p>
</li>
<li><p>The <code>GlobalExceptionHandler</code> class contains a constructor that initializes the <code>ILogger&lt;GlobalExceptionHandler&gt;</code> dependency. The <code>ILogger</code> is used for logging information and errors.</p>
</li>
<li><p>The <code>TryHandleAsync</code> method is used to handle exceptions asynchronously. This method accepts the <code>HttpContext</code>, <code>Exception</code>, and <code>CancellationToken</code> as parameters.</p>
</li>
<li><p>We log the exception details using the <code>ILogger</code> dependency.</p>
</li>
<li><p>We create an <code>ErrorResponse</code> object to represent the error response returned by the API. The <code>ErrorResponse</code> object contains the error message, title, and status code.</p>
</li>
<li><p>We determine the status code based on the type of exception. If the exception is a <code>BadHttpRequestException</code>, we set the status code to <code>BadRequest</code>. If the exception is a <code>NoBookFoundException</code> or <code>BookDoesNotExistException</code>, we set the status code to <code>NotFound</code>. Otherwise, we set the status code to <code>InternalServerError</code>.</p>
</li>
<li><p>We set the response status code using the <code>httpContext.Response.StatusCode</code> property.</p>
</li>
<li><p>We write the error response as JSON using the <code>httpContext.Response.WriteAsJsonAsync</code> method.</p>
</li>
<li><p>We return <code>true</code> to indicate that the exception was handled successfully.</p>
</li>
</ul>
<p>Now that we have created the exception classes, let's register the <code>GlobalExceptionHandler</code> in the dependency injection container. Since we created an Extension method for registering services in the dependency injection container, we will add the <code>GlobalExceptionHandler</code> to the <code>ServiceExtensions</code> class.</p>
<p>Update the <code>ServiceExtensions</code> class in the <code>Extensions</code> folder as follows:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Extensions/ServiceExtensions.cs</span>
<span class="hljs-comment">//...</span>
builder.Services.AddExceptionHandler&lt;GlobalExceptionHandler&gt;();

builder.Services.AddProblemDetails();

<span class="hljs-comment">//...</span>
</code></pre>
<p>The <code>AddExceptionHandler</code> method registers the <code>GlobalExceptionHandler</code> in the dependency injection container. The <code>AddProblemDetails</code> method registers the <code>ProblemDetails</code> class in the dependency injection container.</p>
<p>Now that we have registered the <code>GlobalExceptionHandler</code> in the dependency injection container, we can use it to handle exceptions globally in our application. In the next section, we will create the API endpoints to interact with the book data.</p>
<h2 id="heading-how-to-create-the-api-endpoints">How to Create the API Endpoints</h2>
<p>In the context of minimal APIs in ASP.NET Core, there are many ways to set up your endpoints.</p>
<p>You can define them directly in your <code>Program.cs</code> file. But as your project grows and you need to add more endpoints or functionality, it’s helpful to organize your code better. One way to achieve this is by creating a separate class to handle all the endpoints.</p>
<p>As we’ve discussed above, minimal APIs don’t use controllers or views like traditional ASP.NET Core applications. Instead, they use methods such as <code>MapGet</code>, <code>MapPost</code>, <code>MapPut</code>, and <code>MapDelete</code> to define HTTP methods and routes for API endpoints.</p>
<p>To get started, navigate to the <code>Endpoints</code> folder and create a new file named <code>BookEndpoints.cs</code>. Add the following code to the file:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>



<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Endpoints</span>
{
     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookEndPoint</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IEndpointRouteBuilder <span class="hljs-title">MapBookEndPoint</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IEndpointRouteBuilder app</span>)</span>
        {


            <span class="hljs-keyword">return</span> app;
        }
    }
}
</code></pre>
<p>The <code>BookEndpoints</code> class contains a <code>MapBookEndPoint</code> method that returns an <code>IEndpointRouteBuilder</code> object. The <code>IEndpointRouteBuilder</code> object is used to define the HTTP methods and routes for the API endpoints. In the next sections, we will define the API endpoints for <code>creating</code>, <code>reading</code>, <code>updating</code>, and <code>deleting</code> books.</p>
<h3 id="heading-how-to-create-the-addbookasync-books-endpoint">How to Create the <code>AddBookAsync</code> Books Endpoint</h3>
<p>In this section, we will create the <code>AddBookAsync</code> endpoint. This endpoint will accept a <code>Book</code> object as a JSON payload and add it to the database. We will use the <code>MapPost</code> method to define the HTTP method and route for this endpoint.</p>
<p>Add the following code to the <code>BookEndpoints</code> class:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>


<span class="hljs-comment">//...</span>
   <span class="hljs-comment">// Endpoint to add a new book</span>
      app.MapPost(<span class="hljs-string">"/books"</span>, <span class="hljs-keyword">async</span> (CreateBookRequest createBookRequest, IBookService bookService) =&gt;
        {
        <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.AddBookAsync(createBookRequest);
        <span class="hljs-keyword">return</span> Results.Created(<span class="hljs-string">$"/books/<span class="hljs-subst">{result.Id}</span>"</span>, result); 
        });


<span class="hljs-comment">//...</span>
</code></pre>
<ul>
<li><p><strong>Route Definition</strong>: The MapPost method defines the route for the endpoint as <code>/books</code>.</p>
</li>
<li><p><strong>Request Model</strong>: The endpoint accepts an <code>CreateBookRequest</code> object as a JSON payload. The <code>CreateBookRequest</code> object contains the data required to create a new book.</p>
</li>
<li><p><strong>Response Model</strong>: The endpoint returns a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data for the newly created book.</p>
</li>
<li><p><strong>Return Value</strong>: The endpoint returns a <code>Created</code> result. The <code>Created</code> result contains the location of the newly created book and the <code>Book</code> object.</p>
</li>
</ul>
<h3 id="heading-how-to-create-the-getbookasync-book-endpoint">How to Create the <code>GetBookAsync</code> Book Endpoint</h3>
<p>In this section, we will create the <code>GetBookAsync</code> endpoint. This endpoint will accept a book ID as a query parameter and return the book with the specified ID. We will use the <code>MapGet</code> method to define the HTTP method and route for this endpoint.</p>
<p>Add the following code to the <code>BookEndpoints</code> class:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>

<span class="hljs-comment">// ...</span>
    <span class="hljs-comment">// Endpoint to get all books</span>
    app.MapGet(<span class="hljs-string">"/books"</span>, <span class="hljs-keyword">async</span> (IBookService bookService) =&gt;
     {
    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.GetBooksAsync();
    <span class="hljs-keyword">return</span> Results.Ok(result);
});


<span class="hljs-comment">//...</span>
</code></pre>
<ul>
<li><p><strong>Route Definition</strong>: The MapGet method defines the route for the endpoint as <code>/books</code>.</p>
</li>
<li><p><strong>Request Model</strong>: The endpoint accepts a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data required to create a new book.</p>
</li>
<li><p><strong>Response Model</strong>: The endpoint returns a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data for the newly created book.</p>
</li>
<li><p><strong>Return Value</strong>: The endpoint returns an <code>Ok</code> result. The <code>Ok</code> result contains the <code>Book</code> object.</p>
</li>
</ul>
<h3 id="heading-how-to-create-the-getbookbyidasync-book-endpoint">How to Create the <code>GetBookByIdAsync</code> Book Endpoint</h3>
<p>In this section, we will create the <code>GetBookByIdAsync</code> endpoint. This endpoint will accept a book ID as a route parameter and return the book with the specified ID. We will use the <code>MapGet</code> method to define the HTTP method and route for this endpoint.</p>
<p>Add the following code to the <code>BookEndpoints</code> class:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>
<span class="hljs-comment">//...</span>
<span class="hljs-comment">// Endpoint to get a book by ID</span>

  app.MapGet(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, IBookService bookService) =&gt;
  {
    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.GetBookByIdAsync(id);
    <span class="hljs-keyword">return</span> result != <span class="hljs-literal">null</span> ? Results.Ok(result) : Results.NotFound();
});

<span class="hljs-comment">//...</span>
</code></pre>
<ul>
<li><p><strong>Route Definition</strong>: The MapGet method defines the route for the endpoint as <code>/books/{id:guid}</code>. The <code>{id:guid}</code> parameter specifies that the <code>id</code> parameter should be a GUID.</p>
</li>
<li><p><strong>Request Model</strong>: The endpoint accepts a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data required to create a new book.</p>
</li>
<li><p><strong>Response Model</strong>: The endpoint returns a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data for the newly created book.</p>
</li>
<li><p><strong>Return Value</strong>: The endpoint returns an <code>Ok</code> result if the book is found. The <code>NotFound</code> result is returned if the book is not found.</p>
</li>
</ul>
<h3 id="heading-how-to-create-the-updatebookasync-book-endpoint">How to Create the <code>UpdateBookAsync</code> Book Endpoint</h3>
<p>In this section, we will create the <code>UpdateBookAsync</code> endpoint. This endpoint will accept a book ID as a route parameter and an <code>Book</code> object as a JSON payload and update the book with the specified ID. We will use the <code>MapPut</code> method to define the HTTP method and route for this endpoint.</p>
<p>Add the following code to the <code>BookEndpoints</code> class:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>

<span class="hljs-comment">//...</span>
   <span class="hljs-comment">// Endpoint to update a book by ID</span>
    app.MapPut(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =&gt;
 {
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.UpdateBookAsync(id, updateBookRequest);
<span class="hljs-keyword">return</span> result != <span class="hljs-literal">null</span> ? Results.Ok(result) : Results.NotFound();
});

<span class="hljs-comment">//...</span>
</code></pre>
<ul>
<li><p><strong>Route Definition</strong>: The MapPut method defines the route for the endpoint as <code>/books/{id:guid}</code>. The <code>{id:guid}</code> parameter specifies that the <code>id</code> parameter should be a GUID.</p>
</li>
<li><p><strong>Request Model</strong>: The endpoint accepts a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data required to create a new book.</p>
</li>
<li><p><strong>Response Model</strong>: The endpoint returns a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data for the newly created book.</p>
</li>
<li><p><strong>Return Value</strong>: The endpoint returns an <code>Ok</code> result if the book is found. The <code>NotFound</code> result is returned if the book is not found.</p>
</li>
</ul>
<h3 id="heading-how-to-create-the-deletebookasync-book-endpoint">How to Create the <code>DeleteBookAsync</code> Book Endpoint</h3>
<p>In this section, we will create the <code>DeleteBookAsync</code> endpoint. This endpoint will accept a book ID as a route parameter and delete the book with the specified ID. We will use the <code>MapDelete</code> method to define the HTTP method and route for this endpoint.</p>
<p>Add the following code to the <code>BookEndpoints</code> class:</p>
<pre><code class="lang-csharp">
<span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>

<span class="hljs-comment">//...</span>
   <span class="hljs-comment">// Endpoint to delete a book by ID</span>
 app.MapDelete(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, IBookService bookService) =&gt;
{
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.DeleteBookAsync(id);
   <span class="hljs-keyword">return</span> result ? Results.NoContent() : Results.NotFound();
});


<span class="hljs-comment">//...</span>
</code></pre>
<ul>
<li><p><strong>Route Definition</strong>: The MapDelete method defines the route for the endpoint as <code>/books/{id:guid}</code>. The <code>{id:guid}</code> parameter specifies that the <code>id</code> parameter should be a GUID.</p>
</li>
<li><p><strong>Request Model</strong>: The endpoint accepts a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data required to create a new book.</p>
</li>
<li><p><strong>Response Model</strong>: The endpoint returns a <code>Book</code> object as a JSON payload. The <code>Book</code> object contains the data for the newly created book.</p>
</li>
<li><p><strong>Return Value</strong>: The endpoint returns a <code>NoContent</code> result if the book is deleted successfully. The <code>NotFound</code> result is returned if the book is not found.</p>
</li>
</ul>
<p>Now we have defined all the methods for the book endpoints. So your endpoint class should look like this:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Endpoints/BookEndpoints.cs</span>
<span class="hljs-keyword">using</span> bookapi_minimal.Contracts;
<span class="hljs-keyword">using</span> bookapi_minimal.Interfaces;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Endpoints</span>
{
     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookEndPoint</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IEndpointRouteBuilder <span class="hljs-title">MapBookEndPoint</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IEndpointRouteBuilder app</span>)</span>
        {
            <span class="hljs-comment">// Define the endpoints</span>

            <span class="hljs-comment">// Endpoint to add a new book</span>
            app.MapPost(<span class="hljs-string">"/books"</span>, <span class="hljs-keyword">async</span> (CreateBookRequest createBookRequest, IBookService bookService) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.AddBookAsync(createBookRequest);
                <span class="hljs-keyword">return</span> Results.Created(<span class="hljs-string">$"/books/<span class="hljs-subst">{result.Id}</span>"</span>, result); 
            });


               <span class="hljs-comment">// Endpoint to get all books</span>
            app.MapGet(<span class="hljs-string">"/books"</span>, <span class="hljs-keyword">async</span> (IBookService bookService) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.GetBooksAsync();
                <span class="hljs-keyword">return</span> Results.Ok(result);
            });

            <span class="hljs-comment">// Endpoint to get a book by ID</span>
            app.MapGet(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, IBookService bookService) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.GetBookByIdAsync(id);
                <span class="hljs-keyword">return</span> result != <span class="hljs-literal">null</span> ? Results.Ok(result) : Results.NotFound();
            });


            <span class="hljs-comment">// Endpoint to update a book by ID</span>
            app.MapPut(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.UpdateBookAsync(id, updateBookRequest);
                <span class="hljs-keyword">return</span> result != <span class="hljs-literal">null</span> ? Results.Ok(result) : Results.NotFound();
            });

            <span class="hljs-comment">// Endpoint to delete a book by ID</span>
            app.MapDelete(<span class="hljs-string">"/books/{id:guid}"</span>, <span class="hljs-keyword">async</span> (Guid id, IBookService bookService) =&gt;
            {
                <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> bookService.DeleteBookAsync(id);
                <span class="hljs-keyword">return</span> result ? Results.NoContent() : Results.NotFound();
            });

            <span class="hljs-keyword">return</span> app;
        }
    }
}
</code></pre>
<p>Congratulations! You have created all the endpoints for the book API. The endpoints handle the CRUD operations for books and return the appropriate responses based on the request and data.</p>
<h3 id="heading-how-to-register-the-endpoints">How to Register the Endpoints</h3>
<p>After defining the API endpoints for the book API, the next step is to register these endpoints in the <code>Program.cs</code> file. We will use the <code>MapBookEndpoints</code> method to register the book endpoints.</p>
<p>We should also clean up our <code>Program.cs</code> class to ensure it remains organized and maintainable.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs</span>

<span class="hljs-keyword">using</span> System.Reflection;
<span class="hljs-keyword">using</span> bookapi_minimal.Endpoints;
<span class="hljs-keyword">using</span> bookapi_minimal.Services;
<span class="hljs-keyword">using</span> Microsoft.OpenApi.Models;

<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);


builder.AddApplicationServices();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c=&gt;
{
    c.SwaggerDoc(<span class="hljs-string">"v1"</span>, <span class="hljs-keyword">new</span> OpenApiInfo { Title = <span class="hljs-string">"Mimal API"</span>, Version = <span class="hljs-string">"v1"</span>, Description = <span class="hljs-string">"Showing how you can build minimal "</span> +
        <span class="hljs-string">"api with .net"</span> });


    <span class="hljs-comment">// Set the comments path for the Swagger JSON and UI.</span>
    <span class="hljs-keyword">var</span> xmlFile = <span class="hljs-string">$"<span class="hljs-subst">{Assembly.GetExecutingAssembly().GetName().Name}</span>.xml"</span>;
    <span class="hljs-keyword">var</span> xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

});
<span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// Configure the HTTP request pipeline.</span>
<span class="hljs-keyword">if</span> (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseExceptionHandler();


app.MapGroup(<span class="hljs-string">"/api/v1/"</span>)
   .WithTags(<span class="hljs-string">" Book endpoints"</span>)
   .MapBookEndPoint();

app.Run();
</code></pre>
<p>Let's break down the key components of the <code>Program.cs</code> file:</p>
<ul>
<li><p><strong>AddApplicationServices</strong>: This method registers the necessary services for the API. It is an extension method we created earlier to add services to the dependency injection container.</p>
</li>
<li><p><strong>AddSwaggerGen</strong>: This method registers the Swagger generator, which is used to create the Swagger documentation for the API. We specify the title, version, and description of the API in the Swagger document.</p>
</li>
<li><p><strong>MapGroup</strong>: This method groups the endpoints. It takes a path as a parameter and returns an <code>IEndpointRouteBuilder</code> object. We use the <code>WithTags</code> method to add tags to the endpoints and the <code>MapBookEndpoints</code> method to register the book endpoints.</p>
</li>
<li><p><strong>Run</strong>: This method starts the application.</p>
</li>
</ul>
<p>To enable Swagger documentation, you need to add the <code>GenerateDocumentationFile</code> property to your <code>.csproj</code> file. In this example, the file is named <code>bookapi-minimal.csproj</code>, but the name may vary based on your project.</p>
<p>Add the following line to your <code>.csproj</code> file:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">GenerateDocumentationFile</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">GenerateDocumentationFile</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
</code></pre>
<p>By the end, bookapi-minimal.csproj should look like this:</p>
<pre><code class="lang-xml">
<span class="hljs-tag">&lt;<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk.Web"</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">TargetFramework</span>&gt;</span>net8.0<span class="hljs-tag">&lt;/<span class="hljs-name">TargetFramework</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Nullable</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">Nullable</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ImplicitUsings</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">ImplicitUsings</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">GenerateDocumentationFile</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">GenerateDocumentationFile</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">RootNamespace</span>&gt;</span>bookapi_minimal<span class="hljs-tag">&lt;/<span class="hljs-name">RootNamespace</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"FluentValidation.DependencyInjectionExtensions"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"11.9.2"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.AspNetCore.OpenApi"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.6"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Design"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IncludeAssets</span>&gt;</span>runtime; build; native; contentfiles; analyzers; buildtransitive<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeAssets</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PrivateAssets</span>&gt;</span>all<span class="hljs-tag">&lt;/<span class="hljs-name">PrivateAssets</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.SqlServer"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Microsoft.EntityFrameworkCore.Tools"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"8.0.8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IncludeAssets</span>&gt;</span>runtime; build; native; contentfiles; analyzers; buildtransitive<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeAssets</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PrivateAssets</span>&gt;</span>all<span class="hljs-tag">&lt;/<span class="hljs-name">PrivateAssets</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Swashbuckle.AspNetCore"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"6.4.0"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">Project</span>&gt;</span>
</code></pre>
<p>Now that we have registered the book endpoints in the <code>Program.cs</code> file, we can run the application and test the API endpoints using Swagger.</p>
<p>When you run the application, you should see the Swagger documentation at the following URL: <a target="_blank" href="https://localhost:5001/swagger/index.html"><code>https://localhost:5001/swagger/index.html</code></a>. The Swagger documentation provides information about the API endpoints, request and response models, and allows you to test the endpoints directly from the browser. You should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732624213627/e1e3b3d1-2ecb-486a-b95b-28b958f52462.png" alt="Book API Endpoints Swagger UI " width="2474" height="1242" loading="lazy"></p>
<p>Congratulations! You have implemented the business logic for the book service, created custom exceptions, defined API endpoints, and registered the endpoints in the <code>Program.cs</code> file. You have also enabled Swagger documentation to test the API endpoints.</p>
<h2 id="heading-how-to-add-seed-data-to-the-database">How to Add Seed Data to the Database</h2>
<p>One more important step is to seed the database with initial data when the application starts. This seed data will populate the database, allowing you to test your API endpoints without manually adding data.</p>
<p>Let's add some seed data before performing migrations and testing our API endpoints.</p>
<p>To achieve this, we will create a new class in our Configuration folder called <code>BookTypeConfigurations</code> and add the following code:</p>
<pre><code class="lang-csharp">

<span class="hljs-keyword">using</span> bookapi_minimal.Models;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore.Metadata.Builders;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">bookapi_minimal.Configurations</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookTypeConfigurations</span> : <span class="hljs-title">IEntityTypeConfiguration</span>&lt;<span class="hljs-title">BookModel</span>&gt;
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">EntityTypeBuilder&lt;BookModel&gt; builder</span>)</span>
        {
            <span class="hljs-comment">// Configure the table name</span>
            builder.ToTable(<span class="hljs-string">"Books"</span>);

            <span class="hljs-comment">// Configure the primary key</span>
            builder.HasKey(x =&gt; x.Id);

            <span class="hljs-comment">// Configure properties</span>
            builder.Property(x =&gt; x.Id).ValueGeneratedOnAdd();
            builder.Property(x =&gt; x.Title).IsRequired().HasMaxLength(<span class="hljs-number">100</span>);
            builder.Property(x =&gt; x.Author).IsRequired().HasMaxLength(<span class="hljs-number">100</span>);
            builder.Property(x =&gt; x.Description).IsRequired().HasMaxLength(<span class="hljs-number">500</span>);
            builder.Property(x =&gt; x.Category).IsRequired().HasMaxLength(<span class="hljs-number">100</span>);
            builder.Property(x =&gt; x.Language).IsRequired().HasMaxLength(<span class="hljs-number">50</span>);
            builder.Property(x =&gt; x.TotalPages).IsRequired();

            <span class="hljs-comment">// Seed data</span>
            builder.HasData(
                <span class="hljs-keyword">new</span> BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = <span class="hljs-string">"The Alchemist"</span>,
                    Author = <span class="hljs-string">"Paulo Coelho"</span>,
                    Description = <span class="hljs-string">"The Alchemist follows the journey of an Andalusian shepherd"</span>,
                    Category = <span class="hljs-string">"Fiction"</span>,
                    Language = <span class="hljs-string">"English"</span>,
                    TotalPages = <span class="hljs-number">208</span>
                },
                <span class="hljs-keyword">new</span> BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = <span class="hljs-string">"To Kill a Mockingbird"</span>,
                    Author = <span class="hljs-string">"Harper Lee"</span>,
                    Description = <span class="hljs-string">"A novel about the serious issues of rape and racial inequality."</span>,
                    Category = <span class="hljs-string">"Fiction"</span>,
                    Language = <span class="hljs-string">"English"</span>,
                    TotalPages = <span class="hljs-number">281</span>
                },
                <span class="hljs-keyword">new</span> BookModel
                {
                    Id = Guid.NewGuid(),
                    Title = <span class="hljs-string">"1984"</span>,
                    Author = <span class="hljs-string">"George Orwell"</span>,
                    Description = <span class="hljs-string">"A dystopian social science fiction novel and cautionary tale about the dangers of totalitarianism. "</span>,
                  Category = <span class="hljs-string">"Fiction"</span>,
                  Language = <span class="hljs-string">"English"</span>,
                  TotalPages = <span class="hljs-number">328</span>
                } 
            );
        }
    }
}
</code></pre>
<p>Let's break down the code above:</p>
<p>In Entity Framework Core, you can use the <code>IEntityTypeConfiguration</code> interface to configure the entity type and seed data for the database. The <code>BookTypeConfigurations</code> class implements the <code>IEntityTypeConfiguration&lt;BookModel&gt;</code> interface and provides the configuration for the <code>BookModel</code> entity.</p>
<ul>
<li><p><strong>Configure Method</strong>: This method is used to configure the <code>BookModel</code> entity type. It defines the table name, primary key, and properties for the <code>BookModel</code> entity.</p>
<ul>
<li><p><strong>Table Name</strong>: The <code>ToTable</code> method specifies the name of the table to be created in the database. In this case, the table name is set to "Books".</p>
</li>
<li><p><strong>Primary Key</strong>: The <code>HasKey</code> method specifies the primary key for the <code>BookModel</code> entity. The primary key is set to the <code>Id</code> property.</p>
</li>
<li><p><strong>Properties</strong>: The <code>Property</code> method configures the properties of the <code>BookModel</code> entity. It specifies the data type, length, and constraints for each property.</p>
</li>
</ul>
</li>
<li><p><strong>Seed Data</strong>: The <code>HasData</code> method seeds the database with initial data. It creates three <code>BookModel</code> objects with sample data for testing the API endpoints.</p>
</li>
</ul>
<p>Now that we have created the <code>BookTypeConfigurations</code> class, we need to register this configuration in the <code>ApplicationContext</code> class. This ensures that the configuration is applied when the database is created or migrated.</p>
<p>We’re finally almost ready to test our API. But before we do that, we need to perform migrations to create the database and apply the seed data.</p>
<p>Remember that we added our database connection string in the <code>appsettings.json</code> file? Now let's perform a migration and later update our database for the migration to take effect.</p>
<h2 id="heading-how-to-perform-a-migration">How to Perform a Migration</h2>
<p>Migrations allow you to update the database schema based on changes made to your model classes. In Entity Framework Core, you can use the <code>dotnet ef migrations add</code> command to create a new migration reflecting these changes.</p>
<p>To perform a migration, run the following command in the terminal:</p>
<pre><code class="lang-bash">dotnet ef migrations add InitialCreate
</code></pre>
<p>If the command is successful, you should see an output similar to this:</p>
<pre><code class="lang-bash">Build started...
Build succeeded.
Done. To undo this action, use <span class="hljs-string">'ef migrations remove'</span>
</code></pre>
<p>You will now see a new folder called <code>Migrations</code> in your project. This folder contains the migration files that were created based on the changes made to your model classes. These migration files include the SQL commands required to update the database schema.</p>
<h3 id="heading-how-to-update-the-database">How to Update the Database</h3>
<p>After creating the migration, you need to apply the migration to update the database schema. You can use the <code>dotnet ef database update</code> command to apply the migration and update the database. Make sure the SQL Server is running.</p>
<p>Run the following command in the terminal:</p>
<pre><code class="lang-bash">
dotnet ef database update
</code></pre>
<p>This will update the database schema based on the changes made to your model classes. Make sure there are no errors on your database connection string.</p>
<h2 id="heading-how-to-test-the-api-endpoints">How to Test the API Endpoints</h2>
<p>Now we can test our endpoints using Swagger. To do this, run the application by executing the following command in the terminal:</p>
<pre><code class="lang-bash">
dotnet run
</code></pre>
<p>This will run our application. You can open your browser and navigate to <a target="_blank" href="https://localhost:5001/swagger/index.html"><code>https://localhost:5001/swagger/index.html</code></a> to access the Swagger documentation. You should see a list of API endpoints, request and response models, and the ability to test the endpoints directly from the browser.</p>
<p>If your port number is different from <code>5001</code>, don't worry – it will still work. The port might change depending on the type of machine you're using, but it will still achieve the same result.</p>
<h3 id="heading-how-to-test-the-get-all-books-endpoint">How to Test the <code>Get All Books</code> Endpoint</h3>
<p>To test the <code>Get All Books</code> endpoint, follow these steps:</p>
<ol>
<li><p>In the Swagger documentation, click on the <code>GET /api/v1/books</code> endpoint.</p>
</li>
<li><p>Click the <code>Try it out</code> button.</p>
</li>
<li><p>Click the <code>Execute</code> button.</p>
</li>
</ol>
<p>This will send a request to the API to retrieve all the books in the database.</p>
<p>You should see the response from the API, which will include the list of books that were seeded in the database.</p>
<p>The image below shows the response from the API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732624950148/b497bc8e-727a-43c9-910f-755b3b6f208b.png" alt="Get All Books Endpoint Swagger UI " width="2391" height="1277" loading="lazy"></p>
<h3 id="heading-how-to-test-the-get-book-by-id-endpoint">How to Test the <code>Get Book by ID</code> Endpoint</h3>
<p>To test the <code>Get Book by ID</code> endpoint, follow these steps:</p>
<ol>
<li><p>In the Swagger documentation, click on the <code>GET /api/v1/books/{id}</code> endpoint.</p>
</li>
<li><p>Enter the ID of a book in the <code>id</code> field. You can use one of the book IDs that was seeded in the database.</p>
</li>
<li><p>Click the <code>Try it out</code> button.</p>
</li>
</ol>
<p>This will send a request to the API to retrieve the book with the specified ID. You should see the response from the API, which will include the book with the specified ID.</p>
<p>The image below shows the response from the API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732625042363/fe356453-afa6-4a78-b963-d0befff7bd63.png" alt="Get Book By ID Endpoint Swagger UI " width="2342" height="1282" loading="lazy"></p>
<h3 id="heading-how-to-test-the-add-book-endpoint">How to Test the <code>Add Book</code> Endpoint</h3>
<p>To test the <code>Add Book</code> endpoint, follow these steps:</p>
<ol>
<li><p>In the Swagger documentation, click on the <code>POST /api/v1/books</code> endpoint.</p>
</li>
<li><p>Click the <code>Try it out</code> button.</p>
</li>
<li><p>Enter the book details in the request body.</p>
</li>
<li><p>Click the <code>Execute</code> button.</p>
</li>
</ol>
<p>This will send a request to the API to add a new book to the database.</p>
<p>You should see the response from the API, which will include the newly created book.</p>
<p>The image below shows the response from the API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732625138350/faa54e57-e560-49ac-976a-b074e8eebb13.png" alt="Add Book Endpoint Swagger UI " width="2322" height="1292" loading="lazy"></p>
<h3 id="heading-how-to-test-the-update-book-endpoint">How to Test the <code>Update Book</code> Endpoint</h3>
<p>To test the <code>Update Book</code> endpoint, follow these steps:</p>
<ol>
<li><p>In the Swagger documentation, click on the <code>PUT /api/v1/books/{id}</code> endpoint.</p>
</li>
<li><p>Enter the ID of a book in the <code>id</code> field. You can use the id of one of the books that we just added.</p>
</li>
<li><p>Click the <code>Try it out</code> button.</p>
</li>
</ol>
<p>This will send a request to the API to update the book with the specified ID.</p>
<p>You should see the response from the API, which will include the updated book.</p>
<p>The image below shows the response from the API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732625300781/3de90d6c-92ca-40cb-a54e-2236ec921d86.png" alt="Update Book Endpoint Swagger UI " width="2326" height="1311" loading="lazy"></p>
<h3 id="heading-how-to-test-the-delete-book-endpoint">How to Test the <code>Delete Book</code> Endpoint</h3>
<p>To test the <code>Delete Book</code> endpoint, follow these steps:</p>
<ol>
<li><p>In the Swagger documentation, click on the <code>DELETE /api/v1/books/{id}</code> endpoint.</p>
</li>
<li><p>Enter the ID of a book in the <code>id</code> field. You can use any of the ids from the books that we just added or the seeded data.</p>
</li>
<li><p>Click the <code>Try it out</code> button.</p>
</li>
</ol>
<p>This will send a request to the API to delete the book with the specified ID.</p>
<p>The image below shows the response from the API:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732625225432/3b066f4c-2bf2-4f0c-a104-a94dbbad1706.png" alt="Delete Book Endpoint Swagger UI " width="2327" height="1270" loading="lazy"></p>
<p>Congratulations! You have implemented all the CRUD operations for books and tested the API endpoints using Swagger, verifying that they work as expected. You can now build on this foundation to add more features and functionality to your API.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This handbook explored how to create a minimal API in ASP.NET Core with .NET 8. We built a comprehensive book API that supports CRUD operations, implemented custom exceptions, defined and registered API endpoints, and enabled Swagger documentation for easy testing.</p>
<p>Following this tutorial, you have gained a solid foundation for building minimal APIs with ASP.NET Core. You can now apply this knowledge and create robust APIs for various domains and industries.</p>
<p>I hope you found this tutorial both helpful and informative. Thank you for reading!</p>
<p>Feel free to connect with me on social media:</p>
<ul>
<li><p><a target="_blank" href="https://x.com/Clifftech_Dev">Twitter</a></p>
</li>
<li><p><a target="_blank" href="https://www.linkedin.com/in/isaiah-clifford-opoku/">LinkedIn</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/Clifftech123">GitHub</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Benchmark Your Code in C# ]]>
                </title>
                <description>
                    <![CDATA[ Knowing how your code performs is a crucial part of development. We strive to write the most optimal and performant code whilst keeping readability. In this article, I will show you how to test the performance of your code, benchmark your code, and i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-benchmark-your-code-in-csharp/</link>
                <guid isPermaLink="false">673bdcd6088133abf555d52f</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 00:33:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731752379027/4ec760c3-4183-4852-9d3d-e3a5c75b4bcf.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Knowing how your code performs is a crucial part of development. We strive to write the most optimal and performant code whilst keeping readability.</p>
<p>In this article, I will show you how to test the performance of your code, benchmark your code, and identify areas of improvement within your code base.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-benchmarking">What is Benchmarking?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-using-a-stopwatch-is-not-reliable">Why Using a Stopwatch is Not Reliable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-benchmarkdotnet">How to Use BenchMarkDotnet</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-else-can-you-test-with-benchmarkdotnet">What Else Can You Test with BenchmarkDotnet?</a></p>
</li>
</ul>
<h2 id="heading-what-is-benchmarking">What is Benchmarking?</h2>
<p>Benchmarking measures the performance of your code, application, system, or hardware under specific conditions.  </p>
<p>The goal is to gather precise data about how the system behaves for metrics like processing speed, memory usage, resource consumption, or throughput and to identify areas where performance can be optimized.</p>
<h2 id="heading-why-using-a-stopwatch-is-not-reliable">Why Using a Stopwatch is Not Reliable</h2>
<p>Using the <code>Stopwatch</code> class for benchmarking in C# comes with many problems. Although it provides a simple way to measure elapsed time on a method or process, it lacks the precision, control, and consistency needed for accurate benchmarking.  </p>
<p>Before I get into the negatives of this utility, let’s look at how you could use it for very simple tasks.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Diagnostics;

<span class="hljs-comment">// Create a new Stopwatch instance</span>
<span class="hljs-keyword">var</span> sw = <span class="hljs-keyword">new</span> Stopwatch();

<span class="hljs-comment">// Start the stop watch clock</span>
sw.Start();

<span class="hljs-comment">// run your code</span>
<span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++)
{
    sum += i * i;
    Console.WriteLine(<span class="hljs-string">$"<span class="hljs-subst">{sw.ElapsedMilliseconds}</span>"</span>);
}
<span class="hljs-comment">// Stop the clock !</span>
sw.Stop();

<span class="hljs-comment">// Output total time elapsed on the Stopwatch.</span>
Console.WriteLine(<span class="hljs-string">$"Elapsed time: <span class="hljs-subst">{sw.ElapsedMilliseconds}</span> ms"</span>);
</code></pre>
<p>This would print out how many milliseconds have elapsed on each iteration as well as the end elapsed milliseconds. As this is a short program, you can convert to nanoseconds by using <code>ticks</code> like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">long</span> ticks = stopwatch.ElapsedTicks;
<span class="hljs-keyword">double</span> nanoseconds = (ticks * <span class="hljs-number">1e9</span>) / Stopwatch.Frequency;
</code></pre>
<p>Using a stopwatch can be useful if you want to quickly compare two methods or identify obvious performance bottlenecks during development. It’s a lightweight way to get an initial sense of which sections of code might need optimization.</p>
<h3 id="heading-cons-of-stopwatch">Cons of Stopwatch</h3>
<ul>
<li><p>Lack of precision by default, being only accurate to around 100 nanoseconds, which may not be useful for smaller quick micro operations.</p>
</li>
<li><p>JIT (Just in Time) compilation - When code runs for the first time, a JIT compiler compiles the code before running, causing a delay and skewing the timing of completion. Subsequent runs of the code will be slightly faster, however, <code>Stopwatch</code> does not account for this. Keeping this in mind, it is worth running the code a few times to try and alleviate this problem.</p>
</li>
<li><p>Garbage Collection (GC) - If garbage collection happens during a <code>Stopwatch</code> measurement, the time recorded will include GC pause time, which does not reflect the actual execution time of your code.</p>
</li>
</ul>
<p>These are just some of the basic, and most common flaws of using a <code>Stopwatch</code> to test the performance of your code but there are others.  </p>
<p>So what is the best approach?</p>
<p><strong>BenchmarkDotNet</strong> is a popular and robust library for benchmarking in .NET, which can be installed using <code>nuget</code>.</p>
<p>It overcomes many of the above challenges, in the following ways:</p>
<ul>
<li><p>Code Warm Ups - Automatically warms up the code (by running the code a few times) to avoid JIT-related inaccuracies.</p>
</li>
<li><p>Multiple code iterations - Runs the code multiple times to analyze and calculates statistical summaries, around execution time, heap memory allocation and more. The number of times the code is ran can be configured.</p>
</li>
<li><p>Isolated Environments - Manages garbage collection and isolates the execution environment to reduce external interference.</p>
</li>
</ul>
<h2 id="heading-how-to-use-benchmarkdotnet">How to Use BenchMarkDotnet</h2>
<p>Firstly, we need to install the Nuget package. To do this, run the following command in your command line/terminal:</p>
<pre><code class="lang-plaintext">dotnet add package BenchmarkDotnet
</code></pre>
<p>We then need some methods to benchmark, so create a .Net 8 C# console app with the following two class files:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs</span>
<span class="hljs-keyword">using</span> BenchmarkDotNet.Running;

BenchmarkRunner.Run&lt;Benchmarks&gt;();
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-comment">//Benchmarks.cs</span>
<span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Benchmarks</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span>[] _numbers = Enumerable.Range(<span class="hljs-number">1</span>, <span class="hljs-number">1000</span>).ToArray();

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ForLoopSum</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">int</span> sum = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; _numbers.Length; i++)
        {
            sum += _numbers[i];
        }

        <span class="hljs-keyword">return</span> sum;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ForeachLoopSum</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">int</span> number <span class="hljs-keyword">in</span> _numbers)
        {
            sum += number;
        }

        <span class="hljs-keyword">return</span> sum;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">LinqSelect</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> _numbers.Sum();
    }
}
</code></pre>
<p>Above, we have 3 different methods to add up an array of integers, each doing so in a slightly different fashion. This is a perfect example to show how benchmarking can aid us to choose the best solution in our code base.</p>
<h3 id="heading-how-to-run-the-benchmarks">How to Run the Benchmarks</h3>
<p>To run the benchmarks, you can run the following commands in your terminal/command line.</p>
<pre><code class="lang-bash">dotnet build
<span class="hljs-comment"># then run </span>
dotnet run -c Release
</code></pre>
<p>BenchmarkDotnet will then run the methods marked with the <code>[Benchmark]</code> attribute multiple times, and will output the results in an easy to read table, like so:</p>
<pre><code class="lang-bash">| Method         | Mean     | Error   | StdDev  |
|--------------- |---------:|--------:|--------:|
| ForLoopSum     | 434.2 ns | 0.40 ns | 0.31 ns |
| ForeachLoopSum | 321.9 ns | 1.22 ns | 1.14 ns |
| LinqSelect     | 189.4 ns | 0.84 ns | 0.70 ns |
</code></pre>
<p>What does this mean?</p>
<p><strong>Method</strong> - Name of the method under test  </p>
<p><strong>Mean</strong> - Shows the mean (average) time it took in nanoseconds.  </p>
<p><strong>Error</strong> - Represents the margin of error, telling you how much the "Mean" result might vary due to random factors in the system. The lower the number the better, here you can see a very small margin of error meaning the results are stable, whilst large numbers would mean more uncertainty/ unreliable results.</p>
<p><strong>StdDev -</strong> (Standard Deviation) shows how consistent the benchmark results are. A low deviation score indicates the time taken was very similar across multiple runs, increasing reliability. If the standard deviation is high, it would mean the method’s execution time varied a lot between runs.</p>
<h3 id="heading-how-to-measure-memory-allocation">How to Measure Memory Allocation</h3>
<p>Knowing how fast your methods run is a great statistic to understand and know. However, your performance and optimization isn’t just about the time of execution, sometimes you should ensure that there are no memory leaks or large sums of memory utilized, especially with large execution process.</p>
<p>We can use the <code>[MemoryDiagnoser]</code> to the <code>Benchmarks</code> class, which informs the benchmarking library to include memory statistics to the methods under test.</p>
<p>When we run our benchmarks, we get the following output:</p>
<pre><code class="lang-bash">| Method         | Mean     | Error   | StdDev  | Allocated |
|--------------- |---------:|--------:|--------:|----------:|
| ForLoopSum     | 436.8 ns | 5.32 ns | 4.98 ns |         - |
| ForeachLoopSum | 324.6 ns | 2.20 ns | 2.06 ns |         - |
| LinqSelect     | 192.7 ns | 2.40 ns | 2.24 ns |         - |
</code></pre>
<p>But wait, the <strong>Allocated</strong> column has but a dash ? Where are the results?</p>
<p>Simple operations, like summing values in an array generally don’t allocate memory, as they often only use stack memory, which BenchmarkDotNet doesn’t track in the same way.</p>
<p>But using the following tests, we can see how memory allocation can be analyzed:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MemoryBenchmark</span>
{
    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringConcatenation</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">string</span> result = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++)
        {
            result += <span class="hljs-string">"text"</span>;
        }
        <span class="hljs-keyword">return</span> result;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringBuilderConcatenation</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> builder = <span class="hljs-keyword">new</span> System.Text.StringBuilder();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++)
        {
            builder.Append(<span class="hljs-string">"text"</span>);
        }
        <span class="hljs-keyword">return</span> builder.ToString();
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">| Method                     | Mean       | Error     | StdDev    | Gen0     | Allocated  |
|--------------------------- |-----------:|----------:|----------:|---------:|-----------:|
| StringConcatenation        | 218.930 us | 0.7230 us | 0.6409 us | 641.8457 | 3933.56 KB |
| StringBuilderConcatenation |   1.645 us | 0.0034 us | 0.0030 us |   2.6875 |   16.47 KB |
</code></pre>
<p>Here we have 2 new columns:<br><strong>Gen0 Column:</strong><br>The <strong>Gen0</strong> column indicates how many Gen 0 garbage collections occurred during each method’s execution.  </p>
<p>.Net uses a generational garbage collection system, where memory is divided into three "generations" (Gen0, Gen1, and Gen2).</p>
<ul>
<li><p><strong>Gen0 (Generation 0)</strong>: Holds short-lived objects, such as temporary variables and small, quickly discarded objects. Gen0 collections are the fastest type of GC but still introduce some overhead. Examples of Gen0 would be local variables in methods, temporary objects, or method call arguments that aren’t used later on.</p>
</li>
<li><p><strong>Gen1 and Gen2</strong>: This is for longer-lived objects that survive Gen0 collections, like static objects that are kept alive for the lifetime of the application (that is, singletons), caching objects or large collections used across many operations.</p>
</li>
</ul>
<p>Objects in <strong>Gen0</strong> are collected quickly but often, and objects in <strong>Gen2</strong> are collected infrequently but with more effort because they are larger or more persistent. A lot of <strong>Gen0</strong> collections can be an indicator of inefficient memory usage, while <strong>Gen2 or 3</strong> collections may indicate that your app is keeping too many long-lived objects in memory.</p>
<p><strong>Allocated Column:</strong><br>The <strong>Allocated</strong> column shows the total memory allocated by each method during its execution. This is typically reported in kilobytes (KB).</p>
<p>This information helps you see how memory-intensive each method is, which can impact performance, especially if the method is called frequently.</p>
<p>For example, <code>StringBuilderConcatenation</code> is much more memory-efficient than <code>StringConcatenation</code>, which makes it preferable in cases where memory usage is a concern or where this operation is performed frequently.</p>
<h2 id="heading-what-else-can-you-test-with-benchmarkdotnet">What Else Can You Test with BenchmarkDotnet?</h2>
<h3 id="heading-throughput">Throughput</h3>
<ul>
<li><p>Analyzes how many iterations of a method can be executed per second.</p>
</li>
<li><p>Indicates the efficiency and scalability of the code.</p>
</li>
</ul>
<h3 id="heading-jit-just-in-time-optimization-impact">JIT (Just-In-Time) Optimization Impact</h3>
<ul>
<li><p>Evaluates the effects of JIT optimizations on performance.</p>
</li>
<li><p>Can test cold starts (first-run performance) versus steady-state performance (subsequent runs).</p>
</li>
</ul>
<h3 id="heading-platform-and-framework-differences">Platform and Framework Differences</h3>
<p>You could run benchmarks of the same code across different .NET runtimes (for example, .NET 6, .NET 8, .NET Framework) to compare whether it’s worth upgrading your application to newer systems or not.</p>
<p>Simply update the TargetFramework node in the <code>.csproj</code> file of your application to target the frameworks you wish to test.</p>
<p>Add the following attributes to your benchmark class (based on the target runtime).</p>
<pre><code class="lang-bash">[SimpleJob(runtimeMoniker: RuntimeMoniker.Net60)]
[SimpleJob(runtimeMoniker: RuntimeMoniker.Net80)]
</code></pre>
<p>when you run your application you will get an output as below highlighting the differenes in methods across both .net 6 and .net 8</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Job</td><td>Runtime</td><td>Mean</td><td>Error</td><td>StdDev</td></tr>
</thead>
<tbody>
<tr>
<td>StringConcatenation</td><td>.NET 6.0</td><td>.NET 6.0</td><td>286.503 us</td><td>3.5004 us</td><td>3.1030 us</td></tr>
<tr>
<td>StringBuilderConcatenation</td><td>.NET 6.0</td><td>.NET 6.0</td><td>4.595 us</td><td>0.0620 us</td><td>0.0580 us</td></tr>
<tr>
<td>StringConcatenation</td><td>.NET 8.0</td><td>.NET 8.0</td><td>222.270 us</td><td>1.7561 us</td><td>1.4664 us</td></tr>
<tr>
<td>StringBuilderConcatenation</td><td>.NET 8.0</td><td>.NET 8.0</td><td>1.650 us</td><td>0.0139 us</td><td>0.0116 us</td></tr>
</tbody>
</table>
</div><h3 id="heading-impact-of-input-parameters">Impact of Input Parameters</h3>
<ul>
<li><p>Supports parameterized benchmarks to test how different inputs affect performance.</p>
</li>
<li><p>Helps identify optimal input ranges or problematic edge cases.</p>
</li>
</ul>
<p>You can do something like this</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;
<span class="hljs-keyword">using</span> BenchmarkDotNet.Running;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">SortBenchmark</span>
{
    [<span class="hljs-meta">Params(10, 100, 1000)</span>]  <span class="hljs-comment">// Size of the array</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> N;

    [<span class="hljs-meta">Params(10, 100, 1000)</span>]  <span class="hljs-comment">// Maximum value of the array elements</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> MaxValue;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span>[] data;

    <span class="hljs-comment">// Setup method to create an array before each benchmark</span>
    [<span class="hljs-meta">GlobalSetup</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Setup</span>(<span class="hljs-params"></span>)</span>
    {
        data = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[N];
        <span class="hljs-keyword">var</span> rand = <span class="hljs-keyword">new</span> Random();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; N; i++)
        {
            data[i] = rand.Next(MaxValue);
        }
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SortArray</span>(<span class="hljs-params"></span>)</span>
    {
        Array.Sort(data);  <span class="hljs-comment">// Sort the array</span>
    }
}

<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
    {
        <span class="hljs-comment">// Run the benchmark</span>
        BenchmarkRunner.Run&lt;SortBenchmark&gt;();
    }
}
</code></pre>
<p>Giving the output of:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>N</td><td>MaxValue</td><td>Mean</td><td>Error</td><td>StdDev</td><td>Allocated</td></tr>
</thead>
<tbody>
<tr>
<td>SortArray</td><td>10</td><td>10</td><td>3.5 ns</td><td>0.1 ns</td><td>0.05 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>10</td><td>1000</td><td>4.0 ns</td><td>0.2 ns</td><td>0.1 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>100</td><td>10</td><td>20.1 ns</td><td>0.5 ns</td><td>0.3 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>100</td><td>1000</td><td>25.2 ns</td><td>0.8 ns</td><td>0.4 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>1000</td><td>10</td><td>300.3 ns</td><td>5.6 ns</td><td>2.7 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>1000</td><td>1000</td><td>320.1 ns</td><td>6.3 ns</td><td>3.1 ns</td><td>0 B</td></tr>
</tbody>
</table>
</div><h3 id="heading-third-party-library-performance">Third-Party Library Performance</h3>
<p>Using the techniques mentioned above, you can compare the performance of different third-party libraries for the same task to make informed decisions on library usage.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There you have it, how to benchmark your C# application. Using a combination of these methods, tools and techniques, the possibilities of benchmarking are incredible.</p>
<p>You can use benchmarking to improve your application’s code base, help make decisions on upgrade paths, and method choices.</p>
<p>I hope you find this article helpful, and as always, if you wish to discuss it you can follow me on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Semantic Version for .NET Core Apps and Libraries ]]>
                </title>
                <description>
                    <![CDATA[ Semantic Versioning (or SemVer for short) is a software versioning scheme that stipulates three-part version numbers of the form <major>.<minor>.<patch>, such as 1.0.2, with an optional prerelease suffix of the form -<prerelease>, as in 1.0.2-beta. S... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/set-semantic-versioning-for-net/</link>
                <guid isPermaLink="false">672df563eedb451674f63814</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Aspnetcore ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naveed Ausaf ]]>
                </dc:creator>
                <pubDate>Fri, 08 Nov 2024 11:26:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731065367635/f8ce5091-d526-4d09-8282-2ffe63cead40.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://semver.org/">Semantic Versioning</a> (or SemVer for short) is a software versioning scheme that stipulates three-part version numbers of the form <code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code>, such as <code>1.0.2</code>, with an optional prerelease suffix of the form <code>-&lt;prerelease&gt;</code>, as in <code>1.0.2-beta</code>.</p>
<p>SemVer is perhaps the the most widely used versioning scheme today. For example, both <a target="_blank" href="https://learn.microsoft.com/en-us/nuget/concepts/package-versioning?tabs=semver20sort#pre-release-versions">Nuget</a> and <a target="_blank" href="https://docs.npmjs.com/about-semantic-versioning">npm</a> recommend and support it, and VS Code <a target="_blank" href="https://github.com/microsoft/vscode/releases">uses it</a> as well.</p>
<p>In most GitHub repos that use the GitHub Releases feature to publish releases, you would see a SemVer version number in the latest release badge on the home page, as can be seen in the screenshot below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730988665455/34706cc9-7cf3-401c-9407-2f15933fef49.png" alt="Latest release badge in Next.js GitHub repository showing the three-part Semantic Versioning version number 15.0.3" class="image--center mx-auto" width="411" height="206" loading="lazy"></p>
<p>I frequently need to set a SemVer version number when building ASP.NET Core API projects, and then read or report this at runtime.</p>
<p>For example, if I build a minimal API with its version set to <code>1.0.2-beta</code>, this would be reported by a <code>/version</code> endpoint exposed by the API, as shown in the screenshot below from <a target="_blank" href="https://hoppscotch.io/">Hoppscotch</a> (this is a <a target="_blank" href="https://www.postman.com/">Postman</a>-like tool with the convenience that it runs the browser):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730746046707/eb8968ef-41c7-4919-a0ed-7aeb25e0a03d.png" alt="Screenshot of Hoppscotch shows that a was call made to the /version endpoint of an API running locally (on localhost). The result was a JSON document containing a &quot;version&quot; property whose value is the API's SemVer version number of &quot;1.0.2-beta&quot;." class="image--center mx-auto" width="623" height="488" loading="lazy"></p>
<p>Checking that the version reported from deployed services, such as web apps and APIs, is correct is a crucial part of my CD pipeline and is one of the smoke tests I use to determine if a deployment succeeded.</p>
<p>One slight complication when setting a SemVer version number on .NET assemblies is that .NET originally used four part version numbers like <code>1.0.3.212</code> and assemblies still have these (assembly is the .NET term for <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/standard/assembly/">units of code compiled to .NET bytecode</a>, the most typical of these being dll’s and exe’s).</p>
<p>The other is that .NET has not one but many, slightly different, version numbers that are present in the same assembly.</p>
<p>In this article, I’ll show you how to sidestep these quirks and stamp a SemVer version number on a .NET assembly during build, then read it back at runtime.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-structure-of-a-semver-version-number">Structure of a SemVer Version Number</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-many-version-numbers-of-a-net-assembly">The Many Version Numbers of a .NET Assembly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-a-semver-version-number">How to Set a SemVer Version Number</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-an-assemblys-semver-version-at-runtime">How to Read an Assembly’s SemVer Version at Runtime</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-structure-of-a-semver-version-number">Structure of a SemVer Version Number</h2>
<p>Consider a SemVer version number like <code>1.0.2</code> or <code>1.0.2-beta</code>. It has the form <code>&lt;major&gt;</code>.<code>&lt;minor&gt;</code>.<code>&lt;patch&gt;</code>-<code>&lt;prerelease&gt;</code></p>
<p>This is what the various components mean:</p>
<p>The <code>&lt;major&gt;</code> component of the version number would be incremented only if the new release would break an existing (most recent) release.</p>
<p>In case of a UI app, clients may be taken to mean <em>human clients</em>. So if the new release would break users’ existing assets such as workflow definitions, this would call for incrementing the major version number. In this event, if the previous release was <code>1.0.2</code>, the new release should be <code>2.0.0</code> (all lower components of the version number would reset).</p>
<p>In case of a library, such as a library package on Nuget or NPM, the clients would be other code. So if the new release would break existing client code, i.e. it would not be backwards compatible with its own previous version, then again it is the <code>&lt;major&gt;</code> component would be incremented.</p>
<p><code>&lt;minor&gt;</code> is incremented if new features have been added but the new version is still backwards compatible. So from <code>1.0.2</code> you would go to <code>1.1.0</code>.</p>
<p><code>&lt;patch&gt;</code> is incremented when a new release needs to be made even though there is no breaking change and no new functionality has been added. This could happen, for example, if there was a bugfix that had to be released.</p>
<p><code>-&lt;prerelease&gt;</code> suffix is optional. It is typically suffixed to a three part version number when software needs to be made available during prerelease testing phases such as alpha and beta. For example, before generally releasing version <code>1.0.2</code> of your software, you can make it available to your beta testers as <code>1.0.2-beta</code>.</p>
<p>The <code>&lt;prerelease&gt;</code> component can pretty much be any string of your choosing and the only requirement is that it is either an <em>alphanumeric identifier</em> such as <code>beta</code> or <code>12</code> or <code>alpha2</code> (no characters other than numbers or letters of the alphabet) or multiple alphanumeric identifiers separated by a dot(<code>.</code>) e.g. <code>development.version</code>.</p>
<h2 id="heading-the-many-version-numbers-of-a-net-assembly">The Many Version Numbers of a .NET Assembly</h2>
<p>As Andrew Lock’s <a target="_blank" href="https://andrewlock.net/version-vs-versionsuffix-vs-packageversion-what-do-they-all-mean/#how-to-set-the-version-number-when-you-build-your-app-library">article on .NET versioning</a> explains, a .NET assembly has not one but several different version numbers:</p>
<ul>
<li><p><strong>AssemblyVersion:</strong> This is a four part version number, for example, <code>1.0.2.0</code>. It is used by the runtime when loading linked assemblies.</p>
</li>
<li><p><strong>FileVersion:</strong> This is the version number reported for a <strong>.dll</strong> file in Windows File Explorer when you right click the assembly and select Properties.</p>
</li>
<li><p><strong>InformationalVersion:</strong> Yet another version number and, like FileVersion, can be seen in Properties dialog if you right-click the assembly in Windows and select Properties. This can contain strings and not only integers and dots that AssemblyVersion and FileVersion are constrained to.</p>
</li>
<li><p><strong>PackageVersion:</strong> If the project is a Nuget package, this would be the version number of the package that the assembly is part of.</p>
</li>
</ul>
<p>All of these version numbers are emitted into the assembly during compilation as metadata. You can see them if you inspect the assembly with <a target="_blank" href="https://www.jetbrains.com/decompiler/">JetBrains dotPeek</a> (free) or <a target="_blank" href="https://www.red-gate.com/products/reflector/">Red gate Reflector</a> (not free) or similar.</p>
<p>FileVersion and InformationalVersion can also be seen in the Details tab of the Properties dialog that appears when you right-click the assembly file in Windows File Explorer and select Properties:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730755185100/d444a84b-5148-47e6-ab75-951d9f0f73ac.png" alt="Properties Dialog for a compiled .NET DLL in Windows File Explorer. It shows the DLL's &quot;File Version&quot; attribute with value &quot;1.0.2.0&quot; and its &quot;Product version&quot; attribute with value &quot;1.0.2-beta&quot;" class="image--center mx-auto" width="480" height="621" loading="lazy"></p>
<p>In the screenshot above, “Product version” is the caption for InformationalVersion whereas “File version” is the caption for FIleVersion.</p>
<p>Of the four types of version numbers described above, only the first three apply to any assembly (i.e. whether or not the assembly is part of a Nuget package).</p>
<p>Of those three, AssemblyVersion alsways adds a <code>0</code> in the fourth place if you try to set a SemVer version which only has three numbers (plus an optional <em>prerelease</em> suffix). For example if you try to set a SemVer version of <code>1.0.2-beta</code> during build and then read the AssemblyVersion value at run time in the assembly, it would be <code>1.0.2.0</code>.</p>
<p>FileVersion does the same, as shown in the screenshot above.</p>
<p>InformationalVersion is the only version number which would get set exactly to the server version you set during build, as the screenshot above shows.</p>
<p>Therefore, InformationalVersion is what we need to set with a SemVer version during build, and read back at run time.</p>
<h2 id="heading-how-to-set-a-semver-version-number">How to Set a SemVer Version Number</h2>
<p>There are two things you need to do to set a SemVer version number on an assembly during build.</p>
<p><strong>First,</strong> in a <code>&lt;PropertyGroup&gt;</code> element in the project’s <code>csproj</code> file, add element <code>&lt;IncludeSourceRevisionInInformationalVersion&gt;false&lt;/IncludeSourceRevisionInInformationalVersion&gt;</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
 ...
 <span class="hljs-tag">&lt;<span class="hljs-name">IncludeSourceRevisionInInformationalVersion</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeSourceRevisionInInformationalVersion</span>&gt;</span> 
<span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
</code></pre>
<p>As described in <a target="_blank" href="https://developercommunity.visualstudio.com/t/Build-adds-string-to-assembly-Informatio/10515014?sort=newest">this issue</a>, this ensures that InformationalVersion is set exactly to the SemVer version number we specified and does not get a <code>+&lt;hash code&gt;</code> tacked on at the end.</p>
<p><strong>Second,</strong> pass the version number as value of <code>Version</code> property passed to <code>dotnet build</code> command e.g.:</p>
<pre><code class="lang-yaml"><span class="hljs-string">dotnet</span> <span class="hljs-string">build</span> <span class="hljs-string">--configuration</span> <span class="hljs-string">Release</span> <span class="hljs-string">-p</span> <span class="hljs-string">Version=1.0.2-beta</span>
</code></pre>
<p>This would set InformationalVersion in the compiled assembly (.exe or .dll file) to <code>1.0.2-beta</code>.</p>
<p>Incidentally, it would also set AssemblyVersion and FileVersion (an extra <code>0</code> would be added to the end of <code>1.0.2</code>) but we are not interested in those.</p>
<p>Note that instead pf passing <code>Version</code> argument on the command line, you can set MS Build property <code>&lt;Version&gt;1.0.2-beta&lt;/Version&gt;</code> in a <code>&lt;PropertyGroup&gt;</code> element in the csproj file. However passing a value of <code>Version</code> parameter to <code>dotnet build</code> is simpler because the csproj file does not need to be modified everytime the version number is incremented. This is helpful in CD pipelines. If you do set <code>&lt;Version&gt;</code> in csproj file, this will be overridden by whatever you provide on the <code>dotnet build</code> command line as the value of the <code>Version</code> property.</p>
<h2 id="heading-how-to-read-an-assemblys-semver-version-at-runtime">How to Read an Assembly’s SemVer Version at Runtime</h2>
<p>Code that reads InfromationalVersion at run time is as follows:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span>? version = Assembly.GetEntryAssembly()?.
  GetCustomAttribute&lt;AssemblyInformationalVersionAttribute&gt;()?.
  InformationalVersion;
</code></pre>
<p>In my minimal APIs, to add a <code>/version</code> endpoint as I showed in the Introduction section above, I place the above snippet in <code>Program.cs</code>, then add the following snippet immediately after. Note that the whole thing should appear <strong>before</strong> <code>builder.Build()</code> <strong>is called</strong>:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">//this object of an anonymous type will </span>
<span class="hljs-comment">//be serialised as JSON in response body</span>
<span class="hljs-comment">//when returned by a handler</span>
<span class="hljs-keyword">var</span> objVersion = <span class="hljs-keyword">new</span> { Version = version ?? <span class="hljs-string">""</span> };

<span class="hljs-comment">//OTHER CODE</span>
<span class="hljs-comment">//var app = builder.Build()</span>
</code></pre>
<p>After <code>builder.Build()</code> is called, I create the handler for the <code>/version</code> endpoint:</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/version"</span>, () =&gt; objVersion);
</code></pre>
<p>Now when I run the API project and call the <code>/version</code> endpoint, I get the version number back in a JSON object in HTTP response body:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.2-beta"</span>
}
</code></pre>
<p>This is what the Hoppscotch screenshot in the Introduction showed.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article showed you how to set a SemVer version number in your .NET assemblies.</p>
<p>It also showed you how to read the version number at runtime.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add Local Storage to Your Blazor Apps with Blazored.LocalStorage ]]>
                </title>
                <description>
                    <![CDATA[ By FADAHUNSI SEYI SAMUEL One critical feature of modern web applications is their ability to store and retrieve data on the client side. This is where local storage comes into play.  In this article, we'll explore how to leverage the power of the Bla... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-local-storage-in-blazor-apps/</link>
                <guid isPermaLink="false">66d45edd230dff01669057fb</guid>
                
                    <category>
                        <![CDATA[ Blazor ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ localstorage ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 29 Jul 2024 13:55:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/pexels-pixabay-236698.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By FADAHUNSI SEYI SAMUEL</p>
<p>One critical feature of modern web applications is their ability to store and retrieve data on the <a target="_blank" href="https://www.cloudflare.com/learning/serverless/glossary/client-side-vs-server-side/">client side</a>. This is where <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">local storage</a> comes into play. </p>
<p>In this article, we'll explore how to leverage the power of the <a target="_blank" href="https://www.nuget.org/packages/Blazored.LocalStorage">Blazored LocalStorage NuGet package</a> to seamlessly integrate <code>local storage</code> capabilities into your Blazor applications.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-understanding-local-storage">Understanding Local Storage</a></li>
<li><a class="post-section-overview" href="#heading-introducing-blazoredlocalstorage">Introducing Blazored.LocalStorage</a></li>
<li><a class="post-section-overview" href="#heading-advantages-of-using-blazoredlocalstorage-in-blazor-applications">Advantages of Using Blazored.LocalStorage in Blazor Applications</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-blazoredlocalstorage">How to Install Blazored.LocalStorage</a></li>
<li><a class="post-section-overview" href="#heading-install-using-the-nuget-package-manager">Install Using Nuget Package Manager</a></li>
<li><a class="post-section-overview" href="#heading-install-using-the-net-cli">Install Using the .NET CLI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-blazoredlocalstorage">How to Use Blazored.LocalStorage</a></li>
<li><a class="post-section-overview" href="#heading-advanced-features-and-techniques">Advanced Features and Techniques</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Make sure you have the necessary tools installed on your computer before continuing with this guide:</p>
<ul>
<li>To build and update <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-8.0">Blazor</a> projects, you'll need <a target="_blank" href="https://visualstudio.microsoft.com/downloads/">Visual Studio</a>, a feature-rich Integrated Development Environment (IDE) which you can download from the official <a target="_blank" href="https://visualstudio.microsoft.com/downloads/">Microsoft website here</a>.</li>
<li>The <a target="_blank" href="https://dotnet.microsoft.com/en-us/download">.NET SDK</a> (Software Development Kit) has everything you need to create and execute <a target="_blank" href="https://dotnet.microsoft.com/en-us/learn/dotnet/what-is-dotnet">.NET</a> apps, and it's required for Blazor projects. Make sure your computer has the <code>.NET SDK</code> installed.</li>
<li>Basic knowledge of <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/">C#</a> and Blazor.</li>
</ul>
<p>With these installed, will be ready to follow along.</p>
<h2 id="heading-understanding-local-storage">Understanding Local Storage</h2>
<p>Local storage is a <code>key-value</code> pair storage mechanism supported by modern web browsers. It provides a simple way to store data persistently on the user's device. Unlike <code>session storage</code>, which is cleared when the browser session ends, <code>local storage</code> remains intact even after closing the browser window.</p>
<p><a target="_blank" href="https://www.nuget.org/packages/Blazored.LocalStorage">Blazored.LocalStorage</a> is a powerful library that simplifies working with the browser's local storage <code>API</code> (Application Programming Interface) within Blazor applications. It provides a convenient <code>API</code> for storing, retrieving, and managing data in local storage.</p>
<p>By abstracting away the complexities of directly interacting with the <code>localStorage</code> API, this package provides an intuitive and type-safe interface. It lets you focus on building feature-rich Blazor applications without worrying about the underlying storage mechanisms.</p>
<h2 id="heading-introducing-blazoredlocalstorage">Introducing Blazored.LocalStorage</h2>
<p><code>Blazored.LocalStorage</code> is an open-source library designed to provide easy and accessible local storage management in Blazor applications. This library is compatible with both <a target="_blank" href="https://www.pragimtech.com/blog/blazor-webAssembly/what-is-blazor-webassembly/">Blazor WebAssembly</a> and <a target="_blank" href="https://www.c-sharpcorner.com/article/understanding-server-side-blazor/">Blazor Server</a> projects. This makes it a versatile choice for Blazor developers looking to enhance their applications with persistent, client-side storage capabilities.</p>
<p>At its core, <code>Blazored.LocalStorage</code> facilitates the storage and retrieval of data in the browser's local storage, allowing data to persist across browser sessions. </p>
<p>This is particularly useful for storing user preferences, application state, and other data that needs to persist between page reloads without the need for a database or backend storage solution.</p>
<h3 id="heading-advantages-of-using-blazoredlocalstorage-in-blazor-applications">Advantages of Using Blazored.LocalStorage in Blazor Applications</h3>
<p>The inclusion of <code>Blazored.LocalStorage</code> in Blazor applications comes with a host of benefits, including:</p>
<ul>
<li>Simplified State Management: By leveraging local storage, applications can maintain state more effectively across user sessions, enhancing the user experience.</li>
<li>Performance Improvements: Storing data locally reduces the need for frequent server requests, leading to faster application performance and reduced server load.</li>
<li>Enhanced User Experience: Preferences and application states can be preserved, meaning users do not need to reconfigure settings or lose their place in the application upon returning.</li>
<li>Easy Integration: The <code>API</code> provided by <code>Blazored.LocalStorage</code> is designed to be intuitive and straightforward, ensuring that developers can integrate local storage capabilities into their applications with minimal effort.</li>
<li>Cross-Session Persistence: Unlike session storage or in-memory data storage, local storage ensures that data persists across browser sessions and tab closures, providing a more consistent user experience.</li>
</ul>
<h2 id="heading-how-to-install-blazoredlocalstorage">How to Install Blazored.LocalStorage</h2>
<p>Integrating <code>Blazored.LocalStorage</code> into your Blazor project is straightforward, with support for installation via the <code>NuGet Package Manager</code> or the <code>.NET CLI</code> (Command Line Interpreter).</p>
<h3 id="heading-install-using-the-nuget-package-manager">Install Using the NuGet Package Manager</h3>
<p>Using <code>Visual Studio</code>, you can easily add <code>Blazored.LocalStorage</code> by following these steps:</p>
<ul>
<li>Open your Blazor project in Visual Studio.</li>
<li>Navigate to the “Solution Explorer”, right-click on "Dependencies", and select “Manage NuGet Packages”.</li>
</ul>
<p><img src="https://hackmd.io/_uploads/S1ckU8ll0.png" alt="Annotation 2024-04-07 181852" width="600" height="400" loading="lazy"></p>
<ul>
<li>In the NuGet Package Manager, click on the “Browse” tab and search for “Blazored.LocalStorage”.</li>
</ul>
<p><img src="https://hackmd.io/_uploads/HkBjUUxxC.png" alt="2024-04-07_18-22-49" width="600" height="400" loading="lazy"></p>
<ul>
<li>Find the package in the list, select it, and click “Install”.</li>
</ul>
<p>Visual Studio will handle the rest, adding the package to your project along with any dependencies.</p>
<h3 id="heading-install-using-the-net-cli">Install Using the .NET CLI</h3>
<p>For those who prefer using the command line or are working within a development environment other than Visual Studio, the <code>.NET CLI</code> provides a simple method to add <code>Blazored.LocalStorage</code>:</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">add</span> package Blazored.LocalStorage
</code></pre>
<p>Run the command above in your <code>terminal</code> or <code>command prompt</code> from the root directory of your Blazor project. The CLI will download and install <code>Blazored.LocalStorage</code> along with any necessary dependencies.</p>
<h2 id="heading-how-to-use-blazoredlocalstorage">How to Use Blazored.LocalStorage</h2>
<p>Let's dive into some basic examples of using Blazored.LocalStorage in a Blazor application.</p>
<h3 id="heading-how-to-register-blazoredlocalstorage-in-your-blazor-application">How to Register Blazored.LocalStorage in your Blazor Application</h3>
<p>We will register<code>Blazored.LocalStorage</code> into the root of the application, so that it will be available to use everywhere in the application.</p>
<p>In the <code>program.cs</code> file, which is our root file, we will register the <code>Blazored.LocalStorage</code> service by doing the following:</p>
<pre><code class="lang-csharp">builder.Services.AddBlazoredLocalStorage();
</code></pre>
<p>The code snippet above registers the <code>Blazored.LocalStorage</code> into the application. For this to work, make sure you add the code below to the top of the <code>program.cs</code> file:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Blazored.LocalStorage;
</code></pre>
<p>The code snippet above makes sure that the <code>Blazored.LocalStorage</code> is being imported to be used in the file. If you've added everything correctly, your <code>program.cs</code> file should look like this:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BlazorApp9.Components;
<span class="hljs-keyword">using</span> Blazored.LocalStorage;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">BlazorApp9</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
    {
        <span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

        builder.Services.AddRazorComponents()
            .AddInteractiveServerComponents();

        builder.Services.AddBlazoredLocalStorage();

        <span class="hljs-keyword">var</span> app = builder.Build();

        <span class="hljs-keyword">if</span> (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler(<span class="hljs-string">"/Error"</span>);
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        app.UseStaticFiles();
        app.UseAntiforgery();

        app.MapRazorComponents&lt;App&gt;()
            .AddInteractiveServerRenderMode();

        app.Run();
    }
}
</code></pre>
<p>The above is the full code that should be in your <code>program.cs</code> file. With this, you can now use <code>Blazored.LocalStorage</code> anywhere in the application to store and receive data.</p>
<h3 id="heading-how-to-store-and-retrieve-data-in-blazoredlocalstorage">How to Store and Retrieve Data in Blazored.LocalStorage</h3>
<p>Let's consider a simple scenario where we want to store and retrieve a piece of data using local storage. We'll create a Blazor page with two buttons: one to store data and another to retrieve it.</p>
<pre><code class="lang-csharp">@page <span class="hljs-string">"/"</span>

@inject Blazored.LocalStorage.ILocalStorageService localStorage
@rendermode RenderMode.InteractiveServer

&lt;h3&gt;Local Storage Example&lt;/h3&gt;

&lt;input @bind-<span class="hljs-keyword">value</span>=<span class="hljs-string">"@inputData"</span> /&gt;

&lt;button @onclick=<span class="hljs-string">"StoreData"</span>&gt;Store Data&lt;/button&gt;
&lt;button @onclick=<span class="hljs-string">"RetrieveData"</span>&gt;Retrieve Data&lt;/button&gt;

&lt;p&gt;The retrieved data <span class="hljs-keyword">from</span> the LocalStorage: @storedData &lt;/p&gt;

@code {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> dataKey = <span class="hljs-string">"localStorageKey"</span>;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span>? storedData;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span>? inputData;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">StoreData</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">if</span>(!<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(inputData))
        {
            <span class="hljs-keyword">await</span> localStorage.SetItemAsync(dataKey, inputData);
            inputData = <span class="hljs-string">""</span>;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">OnAfterRenderAsync</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> firstRender</span>)</span>
    {
        <span class="hljs-keyword">if</span> (firstRender)
        {
            <span class="hljs-keyword">await</span> RetrieveData();
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">RetrieveData</span>(<span class="hljs-params"></span>)</span>
    {
        storedData = <span class="hljs-keyword">await</span> localStorage.GetItemAsync&lt;<span class="hljs-keyword">string</span>&gt;(dataKey);
    }
}
</code></pre>
<p>In the code snippet above, the <code>@inject Blazored.LocalStorage.ILocalStorageService localStorage</code> injects the local storage service to interact with the browser's local storage. The <code>@rendermode RenderMode.InteractiveServer</code> specifies that the page should be rendered as an interactive server-side component. Without the interactive server, the page will not be interactive.</p>
<p>The input field binds to <code>inputData</code> using the <code>@bind-value</code> attribute, allowing users to enter data they wish to store. The <code>dataKey</code> is a constant variable used to store and retrieve data from local storage. The <code>storedData</code> and <code>inputData</code> variables are used to hold the data to be stored and retrieved.</p>
<p>The <code>StoreData</code> method checks to see if <code>inputData</code> is not empty. If not, it stores it in local storage using <code>dataKey</code>, and clears the input field. </p>
<p><code>OnAfterRenderAsync</code> is triggered after the component's first render. It retrieves data from local storage to ensure that data persists even after the page is reloaded. </p>
<p><code>RetrieveData</code> retrieves data from local storage and assigns it to <code>storedData</code> for display.</p>
<p><img src="https://hackmd.io/_uploads/H1DGRAL-0.gif" alt="2024-04-15_00-52-31 (1) (1) (1) (1)" width="600" height="400" loading="lazy"></p>
<p>The video above explains how to store and retrieve data stored in localstorage on the client-side.</p>
<h2 id="heading-advanced-features-and-techniques">Advanced Features and Techniques</h2>
<p>In this section, we'll talk about how you can set an expiration date on your data, and how the stored data can be encrypted and decrypted for security.</p>
<h3 id="heading-how-to-manage-the-expiration-of-stored-data">How to Manage the Expiration of Stored Data</h3>
<p>To manage the expiration of your stored data, you will create a helper class that stores data along with an expiration timestamp. Create a file called <code>StorageItem.cs</code> which will contain the code below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">StorageItem</span>&lt;<span class="hljs-title">T</span>&gt;
{
    <span class="hljs-keyword">public</span> required T Data { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> DateTime Expiry { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>The code snippet above is a <code>StorageItem&lt;T&gt;</code> class, which is a generic class that can hold data of any type <code>T</code> and an expiry date <code>DateTime</code>. The Data property is required to be set when an instance of <code>StorageItem</code> is created or initialized, ensuring that it always has a valid value. The <code>Expiry</code> property represents the expiration date of the stored data.</p>
<p>Next, you'll create a file which will be a class that contains methods to set and get from the LocalStorage, with an expiration time. Create a file called <code>LocalStorageHelper.cs</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Blazored.LocalStorage;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">BlazorApp9.Components.Helpers</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">LocalStorageHelper</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">SetItemAsyncWithExpiry</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">ILocalStorageService localStorageService, <span class="hljs-keyword">string</span> key, TimeSpan expiry, T data</span>)</span>
    {
        StorageItem&lt;T&gt; storageItem = <span class="hljs-keyword">new</span> StorageItem&lt;T&gt;
        {
            Data = data,
            Expiry = DateTime.UtcNow.Add(expiry)
        };

        <span class="hljs-keyword">await</span> localStorageService.SetItemAsync(key, storageItem);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> <span class="hljs-title">Task</span>&lt;<span class="hljs-title">T</span>?&gt; <span class="hljs-title">GetItemAsyncWithExpiry</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">ILocalStorageService localStorageService, <span class="hljs-keyword">string</span> key</span>)</span>
    {
         <span class="hljs-keyword">var</span> storageItem = <span class="hljs-keyword">await</span> localStorageService.GetItemAsync&lt;StorageItem&lt;T&gt;&gt;(key);

        <span class="hljs-keyword">if</span>(storageItem <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">default</span>;
        }

        <span class="hljs-keyword">if</span> (storageItem.Expiry &lt; DateTime.UtcNow)
        {
            <span class="hljs-keyword">await</span> localStorageService.RemoveItemAsync(key);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">default</span>;
        }
        <span class="hljs-keyword">return</span> storageItem.Data;
    }
}
</code></pre>
<p>In the code above, you can see the necessary <code>using</code> directive for the <code>Blazored.LocalStorage</code> library. It provides easy access to the browser's local storage from Blazor applications. </p>
<p>This is followed by the declaration of the namespace <code>BlazorApp9.Components.Helpers</code>. This organizes the code and indicates that this helper is part of a specific component's helpers within the Blazor application.</p>
<p>Next, the <code>LocalStorageHelper</code> class is defined as a <code>static</code> class. A static class is one that cannot be instantiated and can only contain static members (using non static methods or properties will not be accepted). </p>
<p>Within the <code>LocalStorageHelper</code> class, two <code>static</code> asynchronous methods are defined: <code>SetItemAsyncWithExpiry</code> and <code>GetItemAsyncWithExpiry</code>.</p>
<p>The <code>SetItemAsyncWithExpiry</code> method is responsible for storing an item in the local storage with an associated expiry time. It accepts an <code>ILocalStorageService</code> instance for interacting with local storage, a <code>key</code> <code>string</code> to identify the stored item, a <code>TimeSpan</code> value representing the expiry duration, and the actual data to be stored. </p>
<p>Inside the method, a <code>StorageItem&lt;T&gt;</code> object is created, where <code>T</code> is the type of data being stored. This object includes the data and the expiry time, which is calculated by adding the specified <code>TimeSpan</code> to the current <a target="_blank" href="https://www.space.com/what-is-utc.html">UTC time</a>. </p>
<p>This <code>StorageItem</code> object is then serialized and saved in local storage under the given key using the <code>SetItemAsync</code> method of <code>ILocalStorageService</code>.</p>
<p>The <code>GetItemAsyncWithExpiry</code> method is responsible for retrieving an item from local storage and checking if it has expired. It also accepts an <code>ILocalStorageService</code> instance and a <code>key</code> <code>string</code>. </p>
<p>This method attempts to retrieve the stored <code>StorageItem&lt;T&gt;</code> object using the <code>key</code>. If the retrieved item is <code>null</code>, it returns the default value for the type <code>T</code> (typically <code>null</code> for reference types and zero or equivalent for value types). </p>
<p>If the item is found but its expiry time is earlier than the current UTC time, it means the item has expired. In this case, the item is removed from the local storage using the <code>RemoveItemAsync</code> method of <code>ILocalStorageService</code>, and the method returns the default value for <code>T</code>. If the item is valid and has not expired, the method returns the stored data.</p>
<h3 id="heading-how-to-implement-encryption-and-decryption">How to Implement Encryption and Decryption</h3>
<p>In this section, we will explore a utility file that provides encryption and decryption functionalities for a Blazor application. </p>
<p>The <code>EncryptionHelper.cs</code> class includes methods for encrypting and decrypting strings, as well as methods for serializing objects to <a target="_blank" href="https://www.w3schools.com/whatis/whatis_json.asp">JSON (Javascript Object Notation)</a> and then encrypting them. This ensures that sensitive data can be securely stored and transmitted. </p>
<p>Let’s dive into the code to understand how these methods work and how you can use them. Add the following code for this file:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Security.Cryptography;
<span class="hljs-keyword">using</span> System.Text;
<span class="hljs-keyword">using</span> System.Text.Json;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">BlazorApp9.Components.Helpers</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">EncryptionHelper</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> EncryptionKey = <span class="hljs-string">"your-encryption-key"</span>;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">byte</span>[] <span class="hljs-title">GetKeyBytes</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> key</span>)</span>
    {

        <span class="hljs-keyword">byte</span>[] keyBytes = Encoding.UTF8.GetBytes(key);
        Array.Resize(<span class="hljs-keyword">ref</span> keyBytes, <span class="hljs-number">32</span>);
        <span class="hljs-keyword">return</span> keyBytes;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Encrypt</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> plainText</span>)</span>
    {
        <span class="hljs-keyword">byte</span>[] iv = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[<span class="hljs-number">16</span>];
        <span class="hljs-keyword">byte</span>[] array;

        <span class="hljs-keyword">using</span> (Aes aes = Aes.Create())
        {
            aes.Key = GetKeyBytes(EncryptionKey);
            aes.IV = iv;

            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            <span class="hljs-keyword">using</span> (MemoryStream memoryStream = <span class="hljs-keyword">new</span> MemoryStream())
            {
                <span class="hljs-keyword">using</span> (CryptoStream cryptoStream = <span class="hljs-keyword">new</span> CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    <span class="hljs-keyword">using</span> (StreamWriter streamWriter = <span class="hljs-keyword">new</span> StreamWriter((Stream)cryptoStream))
                    {
                        streamWriter.Write(plainText);
                    }

                    array = memoryStream.ToArray();
                }
            }
        }

        <span class="hljs-keyword">return</span> Convert.ToBase64String(array);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Decrypt</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> cipherText</span>)</span>
    {
        <span class="hljs-keyword">byte</span>[] iv = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[<span class="hljs-number">16</span>];
        <span class="hljs-keyword">byte</span>[] buffer = Convert.FromBase64String(cipherText);

        <span class="hljs-keyword">using</span> (Aes aes = Aes.Create())
        {
            aes.Key = GetKeyBytes(EncryptionKey);
            aes.IV = iv;

            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            <span class="hljs-keyword">using</span> (MemoryStream memoryStream = <span class="hljs-keyword">new</span> MemoryStream(buffer))
            {
                <span class="hljs-keyword">using</span> (CryptoStream cryptoStream = <span class="hljs-keyword">new</span> CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    <span class="hljs-keyword">using</span> (StreamReader streamReader = <span class="hljs-keyword">new</span> StreamReader((Stream)cryptoStream))
                    {
                        <span class="hljs-keyword">return</span> streamReader.ReadToEnd();
                    }
                }
            }
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">SerializeAndEncrypt</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T data</span>)</span>
    {
        <span class="hljs-keyword">var</span> jsonString = JsonSerializer.Serialize(data);
        <span class="hljs-keyword">return</span> Encrypt(jsonString);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> T <span class="hljs-title">DecryptAndDeserialize</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">string</span> cipherText</span>)</span>
    {
        <span class="hljs-keyword">var</span> json = Decrypt(cipherText);
        <span class="hljs-keyword">return</span> JsonSerializer.Deserialize&lt;T&gt;(json);
    }
}
</code></pre>
<p>The <code>EncryptionHelper</code> class is a static helper class designed for encrypting and decrypting data. This is particularly useful for securing sensitive information in a Blazor application. </p>
<p>The class above defines a <code>static</code> <code>readonly</code> field <code>EncryptionKey</code> which holds the encryption key. This key is crucial for both the encryption and decryption processes. It's important to use a strong and securely stored key.</p>
<p>The <code>GetKeyBytes</code> method converts the string key into a byte array and ensures its length is 32 bytes. This is because <a target="_blank" href="https://www.techtarget.com/searchsecurity/definition/Advanced-Encryption-Standard">the AES encryption</a> algorithm requires a 256-bit key, which is 32 bytes long.</p>
<p>The <code>Encrypt</code> method encrypts a <code>plaintext</code> string using <code>AES</code> encryption. It first creates an initialization vector (IV) of 16 bytes, which is required by the <code>AES</code> algorithm. The method then sets up an <code>AES</code> object with the encryption key and IV, and uses a <code>CryptoStream</code> to write the encrypted data to a memory stream. This encrypted data is then converted to a <code>base64</code> string for easy storage and transmission.</p>
<p>The <code>Decrypt</code> method performs the reverse operation. It converts a <code>base64</code> string back to a byte array, sets up the <code>AES</code> object with the same key and IV, and uses a <code>CryptoStream</code> to read the decrypted data from the memory stream. The result is the original plaintext string.</p>
<p>The <code>EncryptionHelper</code> class provides two methods for handling complex data structures: <code>SerializeAndEncrypt</code> and <code>DecryptAndDeserialize</code>. The <code>SerializeAndEncrypt</code> method first serializes an object to a <code>JSON</code> string using <code>JsonSerializer.Serialize</code>, and then encrypts this <code>JSON</code> string using the <code>Encrypt</code> method. This allows complex objects to be securely stored in an encrypted format.</p>
<p>The <code>DecryptAndDeserialize</code> method decrypts an encrypted <code>JSON</code> string back into its original form and then deserializes it into an object of type T using <code>JsonSerializer.Deserialize</code>. This combination of decryption and deserialization ensures that complex data can be securely retrieved and used within the application.</p>
<h3 id="heading-how-to-connect-the-expiration-and-encryption-to-the-user-interface">How to Connect the Expiration and Encryption to the User Interface</h3>
<p>Now we'll walk through a Blazor component (<code>Home.razor</code>) that allows users to store and retrieve encrypted data in the browser's local storage. This ensures that sensitive information is protected and automatically expires when no longer needed. </p>
<p>This approach combines the ease of local storage with the security of encryption, providing a robust solution for managing user data in web applications. Let's dive into the code to see how it works.</p>
<pre><code class="lang-csharp"> @page <span class="hljs-string">"/"</span>
@using BlazorApp9.Components.Helpers

@inject Blazored.LocalStorage.ILocalStorageService localStorage
@rendermode RenderMode.InteractiveServer

&lt;h3&gt;Local Storage Example&lt;/h3&gt;

&lt;input @bind-<span class="hljs-keyword">value</span>=<span class="hljs-string">"@inputData"</span> /&gt;

&lt;button @onclick=<span class="hljs-string">"StoreData"</span>&gt;Store Data&lt;/button&gt;
&lt;button @onclick=<span class="hljs-string">"RetrieveData"</span>&gt;Retrieve Data&lt;/button&gt;

&lt;p&gt;The retrieved data <span class="hljs-keyword">from</span> the LocalStorage: @storedData &lt;/p&gt;

@code {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> dataKey = <span class="hljs-string">"localStorageKey"</span>;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span>? storedData;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span>? inputData;

    <span class="hljs-keyword">bool</span> isDataLoaded = <span class="hljs-literal">false</span>;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">StoreData</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(inputData))
        {
            <span class="hljs-keyword">string</span> encryptData = EncryptionHelper.SerializeAndEncrypt(inputData);
            <span class="hljs-keyword">await</span> LocalStorageHelper.SetItemAsyncWithExpiry(localStorage, dataKey, TimeSpan.FromMinutes(<span class="hljs-number">30</span>), encryptData);
            inputData = <span class="hljs-string">""</span>;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">OnAfterRenderAsync</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> firstRender</span>)</span>
    {
        <span class="hljs-keyword">if</span> (firstRender &amp;&amp; !isDataLoaded)
        {
            <span class="hljs-keyword">await</span> RetrieveData();
            isDataLoaded = <span class="hljs-literal">true</span>;
            StateHasChanged();
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">RetrieveData</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">string</span> encryptData = <span class="hljs-keyword">await</span> LocalStorageHelper.GetItemAsyncWithExpiry&lt;<span class="hljs-keyword">string</span>&gt;(localStorage, dataKey);
        storedData = encryptData != <span class="hljs-literal">null</span> ? EncryptionHelper.DecryptAndDeserialize&lt;<span class="hljs-keyword">string</span>&gt;(encryptData) : <span class="hljs-string">"Data not found or expired."</span>;
    }
}
</code></pre>
<p>In the code above, the <code>StoreData</code> method checks if <code>inputData</code> is valid, encrypts it using <code>EncryptionHelper.SerializeAndEncrypt</code>, and stores it in local storage with a thirty-minute expiry using <code>LocalStorageHelper.SetItemAsyncWithExpiry</code>. The input field is then cleared.</p>
<p>The <code>OnAfterRenderAsync</code> method retrieves data from local storage after the component's initial render. This ensures previously stored data is loaded when the page first displays. It runs once, setting <code>isDataLoaded</code> to true and calling <code>StateHasChanged</code> to update the user interface (UI).</p>
<p>The <code>RetrieveData</code> method fetches data from local storage using <code>LocalStorageHelper.GetItemAsyncWithExpiry</code>. If the data is found and valid, it decrypts and deserializes it using <code>EncryptionHelper.DecryptAndDeserialize</code>. If not, it sets <code>storedData</code> to "<code>Data not found or expired.</code>"</p>
<p><img src="https://hackmd.io/_uploads/BJRDqJUNR.gif" alt="2024-05-30_11-18-37 (1) (2) (1)" width="600" height="400" loading="lazy"></p>
<p>The video above demonstrates how you can implement the concepts discussed in this guide in a web application.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>Blazored.LocalStorage</code> offers a powerful and easy-to-use solution for managing user information in Blazor applications. Its integration brings numerous benefits, including enhanced state management, improved performance, and a better user experience.</p>
<p>After reading through this article and trying out the code for yourself, you should be able to incorporate local storage capabilities into any Blazor project. This will help you unlock the full potential of client-side storage in your web applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How To Use LINQ in C# – With Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ .Net (pronounced as "dot net") has many internal libraries and tools, but one that wields great power is LINQ (Language Integrated Query). It can be used in two ways: the language-level query syntax, or the LINQ API. In this article, we'll explore: ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-linq/</link>
                <guid isPermaLink="false">66bb8896deef71ff683a6d1e</guid>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 15 Jul 2024 20:59:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/How-to-use-linq.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>.Net (pronounced as "dot net") has many internal libraries and tools, but one that wields great power is LINQ (Language Integrated Query). It can be used in two ways: the language-level query syntax, or the LINQ API.</p>
<p>In this article, we'll explore:</p>
<ul>
<li>What LINQ is.</li>
<li>How to use it.</li>
<li>Examples of some common LINQ methods.</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-language-level-query-syntax">Language Level Query Syntax</a></li>
<li><a class="post-section-overview" href="#heading-method-syntax">Method Syntax</a></li>
<li><a class="post-section-overview" href="#heading-common-linq-api-methods">Common LINQ API Methods</a><ul>
<li><a class="post-section-overview" href="#heading-orderby-method">OrderBy Method</a></li>
<li><a class="post-section-overview" href="#heading-first-method">First Method</a></li>
<li><a class="post-section-overview" href="#heading-single-and-singleordefault-method">Single/SingleOrDefault Method</a></li>
<li><a class="post-section-overview" href="#heading-select-method">Select Method</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-combine-methods">How to Combine Methods</a></li>
<li><a class="post-section-overview" href="#heading-deferred-execution">Deferred Execution</a></li>
<li><a class="post-section-overview" href="#heading-how-to-chain-iqueryable-api-methods">How to Chain IQueryable API Methods</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<p>We'll be utilizing an <code>Animal</code> class in this article:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Age {<span class="hljs-keyword">get</span>;<span class="hljs-keyword">set</span>;}
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Sound { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<h2 id="heading-language-level-query-syntax">Language Level Query Syntax</h2>
<p>You may see something that resembles a SQL query in some code snippets. For example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animals = <span class="hljs-keyword">new</span> List&lt;Animal&gt;
{
    <span class="hljs-keyword">new</span> Animal { Name = <span class="hljs-string">"Dog"</span>, Age = <span class="hljs-number">2</span>, Sound = <span class="hljs-string">"Bark"</span> },
    <span class="hljs-keyword">new</span> Animal { Name = <span class="hljs-string">"Cat"</span>, Age = <span class="hljs-number">2</span>, Sound = <span class="hljs-string">"Meow"</span> },
    <span class="hljs-keyword">new</span> Animal { Name = <span class="hljs-string">"Fox"</span>, Age = <span class="hljs-number">5</span>, Sound = <span class="hljs-string">"Bark"</span> }
};


<span class="hljs-keyword">var</span> barkingAnimals =
    <span class="hljs-keyword">from</span> animal <span class="hljs-keyword">in</span> animals
    <span class="hljs-keyword">where</span> animal.Sound == <span class="hljs-string">"Bark"</span>
    <span class="hljs-keyword">select</span> animal;
</code></pre>
<p>You can build queries similar to SQL for complex tasks, but this can be excessive. You can simplify these queries using the LINQ API methods syntax.</p>
<h2 id="heading-method-syntax">Method Syntax</h2>
<p>The LINQ API methods utilizes a predicate (in the form of a lambda extension) to determine the criteria.</p>
<p>We can write the above query using the method syntax like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> barkingAnimals = animals.Where(x=&gt; x.Sound == <span class="hljs-string">"Bark"</span>).ToList();
</code></pre>
<p>It's a lot easier to write and much more concise for such a simple query.</p>
<p>It reads a lot better too: barking Animals equals all the animals where the sound property equals <code>Bark</code>.</p>
<p>LINQ querying methods all return an <code>IQueryable</code> object. This informs the compiler that the variable is not the result of the query, but the definition of the query. [see deferred execution later in this article.]</p>
<p>In order to use the results of the query we can either:</p>
<ul>
<li>Iterate over the "queryable" object (for example: using a ForEach loop)</li>
<li>Convert to an IEnunerable type. For example: a List/Array.</li>
</ul>
<h2 id="heading-common-linq-api-methods">Common LINQ API Methods</h2>
<h3 id="heading-ia"> </h3>
<p>OrderBy Method</p>
<p><code>OrderBy</code> is a useful LINQ API method that lets you order any IEnumerable object.</p>
<p>We can use it like so:</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">var</span> orderedByAge = people.OrderBy(x=&gt;x.Age);

<span class="hljs-comment">// or</span>

<span class="hljs-keyword">var</span> orderedByAgeDescending = people.OrderByDescending(x=&gt;x.Age);
</code></pre>
<p>The above shows examples of ordering by Age in both ascending and descending order.</p>
<h3 id="heading-first-method">First Method</h3>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> first = animals.First(x=&gt; x.Sound == <span class="hljs-string">"Bark"</span>);
</code></pre>
<p>This will return the first object from the list that matches the criteria.</p>
<h3 id="heading-single-and-singleordefault-method">Single and SingleOrDefault Method</h3>
<p>This is used when you know/expect that there will be only one object that matches your criteria.</p>
<p>Example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> cat = animals.Single(x=&gt;x.Name == <span class="hljs-string">"Cat"</span>);
</code></pre>
<p>Data can change over time, leading to unexpected results. Writing defensive code is important. If multiple objects named "Cat" are found, an uncaught error will occur. To prevent this, use the <code>SingleOrDefault</code> method, which returns a default value (null for strings) on error. Then check if the cat variable is not null.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> cat = animals.SingleOrDefault(x=&gt; x.Name==<span class="hljs-string">"Cat"</span>);

<span class="hljs-keyword">if</span>(cat != <span class="hljs-literal">null</span>){
  Console.WriteLine(<span class="hljs-string">"A single cat was found"</span>);
}
</code></pre>
<h3 id="heading-select-method">Select Method</h3>
<p>Let's say that you wish to return only the types of animal, from the <code>Animal</code> object. This can be accomplished with the <code>Select</code> method. This will create a new object for each element in the list/array.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> typesOfAnimal = animals.Select(x=&gt;x.Name).ToList();
</code></pre>
<p>But what if you want to return their name and sound? That's just as easy with the <code>Select</code> method. However, you'll have to create an anonymous object instead of just returning the property.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animals = animals.Select(x=&gt; { Name = x.Name, 
Noise = x.Sound}).ToList();
</code></pre>
<p>This should now return a list of anonymous objects, with a <code>Name</code> and a <code>Sound</code> property.</p>
<h2 id="heading-how-to-combine-methods">How to Combine Methods</h2>
<p>Using the <code>People</code> class:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Address { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<pre><code><span class="hljs-keyword">var</span> people = <span class="hljs-keyword">new</span> List&lt;Person&gt;()
{
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Harry Potter"</span>, Address = <span class="hljs-string">"123 Privet Drive, Hogwarts, United Kingdom"</span> },
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Alex the Kidd"</span>, Address = <span class="hljs-string">"Rock Paper Scissors Avenue, United Kingdom"</span> },
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Donkey Kong"</span>, Address = <span class="hljs-string">" The Monkey Temple, Jungle"</span> }
};
</code></pre><p>You can combine LINQ API methods to carry out multiple actions. Take the following scenario as an example:</p>
<p>We want to find all the people in a list whose address contains "United Kingdom".</p>
<p>To accomplish this, you can utilize a combination of  <code>Where()</code>and <code>Contains()</code>, passing the <code>Contains()</code> function as part of the predicate.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> ukResidents = peopleList.Where(p =&gt; p.Address.Contains(<span class="hljs-string">"United Kingdom"</span>)).ToList();
</code></pre>
<h2 id="heading-deferred-execution">Deferred Execution</h2>
<p>LINQ queries use what's called deferred execution. This means that the query will not be executed immediately when it is defined.</p>
<p>Instead, it is executed when the query results are iterated or when certain operators trigger the execution explicitly. This deferred execution enables optimizations and improves performance by avoiding unnecessary computations.</p>
<p>This goes back to what I discussed earlier with the conversion of <code>IQueryable</code> to another object. For example, a list using <code>.ToList()</code>. It's the <code>ToList()</code>that converts the <code>IQueryable</code> object to a list and actually executes the query.</p>
<p>Lets take a look at an example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> people = <span class="hljs-keyword">new</span> List&lt;Person&gt;()
{
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Harry Potter"</span>, Address = <span class="hljs-string">"123 Privet Drive, Hogwarts, United Kingdom"</span> },
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Alex the Kidd"</span>, Address = <span class="hljs-string">"Rock Paper Scissors Avenue, United Kingdom"</span> },
    <span class="hljs-keyword">new</span>() { Name = <span class="hljs-string">"Donkey Kong"</span>, Address = <span class="hljs-string">" The Monkey Temple, Jungle"</span> }
};

<span class="hljs-comment">// then we'll define the query</span>
<span class="hljs-keyword">var</span> peopleCalledHarryPotter = people.Where(x =&gt; x.Name == <span class="hljs-string">"Harry Potter"</span>);

<span class="hljs-comment">// this will create the query object, You could write other code here.</span>

<span class="hljs-comment">// now convert the IQueryable query object to a List, thus invoking the actual query.</span>

<span class="hljs-keyword">var</span> list = peopleCalledHarryPotter.ToList();
</code></pre>
<p>Though a basic example, this demonstrates that you can create a queryable object and execute additional code before actually running the query using the <code>.ToList()</code> extension method.</p>
<h2 id="heading-how-to-chain-iqueryable-api-methods">How to Chain IQueryable API Methods</h2>
<p>Similar to combining <code>Where</code> and <code>Contains</code>, you can further enhance your queries by chaining LINQ API methods. </p>
<p>For example, using <code>Where()</code> and <code>GroupBy()</code> together allows you to filter and then group data by a property. </p>
<p>Let's apply what we've learned and chain these methods. Instead of using <code>.ToList()</code> to create a new variable with the results, we can utilize the query object within a <code>ForEach</code> loop to execute the query and iterate over the results simultaneously.</p>
<p>Update the <code>Person</code> class to have a <code>Age</code> property:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Address { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }    
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Age { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Then look at the following code, which will first filter the list of people, and then group them based on <code>Age</code>.</p>
<pre><code>using System;
using System.Collections.Generic;
using System.Linq;

public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
</span>{
    public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> Main()
    {
        List&lt;Person&gt; people = <span class="hljs-keyword">new</span> List&lt;Person&gt;
        {
            <span class="hljs-keyword">new</span> Person { Name = <span class="hljs-string">"John"</span>, Age = <span class="hljs-number">30</span> },
            <span class="hljs-keyword">new</span> Person { Name = <span class="hljs-string">"Alice"</span>, Age = <span class="hljs-number">25</span> },
            <span class="hljs-keyword">new</span> Person { Name = <span class="hljs-string">"Bob"</span>, Age = <span class="hljs-number">30</span> },
            <span class="hljs-keyword">new</span> Person { Name = <span class="hljs-string">"Charlie"</span>, Age = <span class="hljs-number">25</span> },
            <span class="hljs-keyword">new</span> Person { Name = <span class="hljs-string">"Eve"</span>, Age = <span class="hljs-number">35</span> }
        };

        <span class="hljs-keyword">var</span> groupsOfPeople = people
            .Where(<span class="hljs-function"><span class="hljs-params">p</span> =&gt;</span> p.Age &gt;= <span class="hljs-number">30</span>)  <span class="hljs-comment">// Filter people with age &gt;= 30</span>
            .GroupBy(<span class="hljs-function"><span class="hljs-params">p</span> =&gt;</span> p.Age);    <span class="hljs-comment">// Group people by their age</span>

        foreach (<span class="hljs-keyword">var</span> group <span class="hljs-keyword">in</span> groupsOfPeople)
        {
            Console.WriteLine($<span class="hljs-string">"Age Group: {group.Key}"</span>); <span class="hljs-comment">// output the Age</span>
            foreach (<span class="hljs-keyword">var</span> person <span class="hljs-keyword">in</span> group)
            {
                Console.WriteLine($<span class="hljs-string">"Name: {person.Name}, Age: {person.Age}"</span>);
            }
        }
    }
}
</code></pre><p>This combination of LINQ methods allow us to define more complex quieries without using the Language Level Query Syntax.</p>
<p>Sometimes it can based on personal preference, but I believe the method syntax is much more readable.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we've explored the power of LINQ in .NET, comparing its language-level query syntax and method syntax. We've demonstrated how to simplify complex queries using LINQ API methods and discussed common methods like <code>OrderBy</code>, <code>First()</code>, <code>Single()</code>, <code>SingleOrDefault()</code>, and <code>Select</code>.</p>
<p>We highlighted the importance of writing defensive code and the concept of deferred execution, which optimizes performance. By combining and chaining LINQ methods, you can create complex, readable queries efficiently.</p>
<p>LINQ is a versatile tool that enhances your ability to handle data in .NET applications. Whether using query syntax or method syntax, LINQ provides a powerful way to write efficient and maintainable code.</p>
<p>You can find some useful examples on the Microsoft website <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/working-with-linq">here</a></p>
<p>As always I'd welcome comments, or discussion on the topic. You can follow me on <a target="_blank" href="https://twitter.com/grantdotdev">Twitter</a> </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Mocking? Mocking in .NET Explained With Examples ]]>
                </title>
                <description>
                    <![CDATA[ Let's go on a journey to demystify the concept of mocking in .NET software development. Let's delve into how straightforward and accessible mocking truly is. Join me as we navigate through this subject, as I cover: Understanding mocking: Why it's ne... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/explore-mocking-in-net/</link>
                <guid isPermaLink="false">66bb888a6b3bd8d6bf25ae34</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Fri, 12 Apr 2024 19:37:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Mocking-in-Dotnet.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let's go on a journey to demystify the concept of mocking in .NET software development. Let's delve into how straightforward and accessible mocking truly is. Join me as we navigate through this subject, as I cover:</p>
<ul>
<li>Understanding mocking: Why it's necessary for building a robust testing strategy.</li>
<li>Exploring some of the most common mocking libraries: Such as Moq, NSubstitute, FakeItEasy, and Rhino Mocks.</li>
<li>Mastering mocking techniques: Using each of these libraries, equipping you with the knowledge to choose the best mocking tool for your needs.</li>
</ul>
<p>The aim of this tutorial is to give you the knowledge to make up your own mind on which mocking library you prefer, so you can go forward and write some powerful tests in your application. </p>
<h2 id="heading-prerequisites-for-this-tutorial">Prerequisites for this tutorial</h2>
<ol>
<li>Understanding of C# programming</li>
<li>Understanding of C# Unit Testing</li>
<li>An IDE (Rider, Visual Studio, and so on), or Code Editor (VS Code, Sublime Text, Atom, and so on)</li>
</ol>
<h2 id="heading-getting-startedsetup">Getting Started/Setup</h2>
<p>To speed up the process, I've made a public GitHub repo available for this tutorial,. You can clone and use it on your local machine to follow along.</p>
<p>Simply navigate to <a target="_blank" href="https://github.com/grant-dot-dev/mocking-tutorial">this link</a> and clone the repo to your local machine.  </p>
<p>Quick refresher if you've forgotten to do that: go to the link above, and in the top right corner click "Code", and then copy the URL provided. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Find your local <strong>git</strong> folder, (if you don't have one, create one in your user root folder). Then in your preferred terminal navigate to your <strong>git</strong> older, and execute the following command.</p>
<pre><code class="lang-terminal">// (replacing &lt;url&gt; with the url)
git clone &lt;url&gt;
</code></pre>
<h2 id="heading-a-brief-overview-of-the-application">A Brief Overview of the Application</h2>
<p>The solution you've just cloned is a basic Web API project which references a class library with some Todo domain objects and services which will alter a list of todo items.   </p>
<p>For the purpose of this tutorial, these elements are stored in an in-memory list, rather than connecting to a database. But you can use a database or other forms of data persistence methods.</p>
<p>However, for the purpose of this tutorial, we're less concerned with persisting the data, and more so around mocking this service. </p>
<h2 id="heading-what-is-mocking">What Is Mocking?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-112.png" alt="Two arrows with text Real and Fake" width="600" height="400" loading="lazy">
<em>With mocking you won't tell the difference</em></p>
<p>Mocking in software development is the concept of simulating a behavior or returned object of a class, method or service that another part of the system under test depends on. </p>
<p>When testing a particular component or unit of code, it's often necessary to isolate it from its dependencies, such as databases, web services, or other classes, in order to ensure that the test focuses solely on the functionality of the unit being tested rather than concerning itself with external aspects of the code.</p>
<p>Mocking allow developers to create mock objects or dummy implementations of these dependencies that behave in a predetermined way, mimicking the behavior of the real objects.</p>
<p>By using mocked objects or methods, we can control the input and output of the dependencies. This makes it a lot easier to test different scenarios, where business logic is dependent on the outcome of a dependency. </p>
<h3 id="heading-example">Example</h3>
<p>Let's say we have a API endpoint that calls a repository which connects to a database. Our API response type depends on the returned value from the repository: we either return a 400 or a 200 response from our API.</p>
<p>We can simply mock the repository return value, and test that our API returns the correct response in both scenarios without having to actually touch our database at all. </p>
<p>In essence, mocking helps developers write more reliable and efficient tests by isolating the code under test and providing predictable behaviour for its dependencies, thereby improving the overall quality of the software.</p>
<h2 id="heading-what-are-the-benefits-of-mocking">What Are the Benefits of Mocking?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-113.png" alt="Image showing jigsaw pieces, with one highlighted saying Benefits" width="600" height="400" loading="lazy">
<em>Benefits</em></p>
<h3 id="heading-code-isolation">Code Isolation</h3>
<p>As I've already explained, mocking dependencies allows for isolation of code for testing. By mocking the dependencies, you can assert the points of failure in the code under test, and not the dependencies themselves (unless you mock them incorrectly – which hopefully this tutorial will help you avoid). </p>
<h3 id="heading-faster-testing">Faster Testing</h3>
<p>By replacing real dependencies with mocked implementations, testing can be performed faster. You're not battling with inconsistencies, unreliable or unpredictable outcomes from those dependencies. It eliminates the need for setting up and tearing down external sources, which can sometimes be complex and time consuming. </p>
<h3 id="heading-deterministic-testing">Deterministic Testing</h3>
<p>Mock objects offer a controlled environment, allowing developers to specify the exact behavior and responses of dependencies. This approach means that the tests are consistent, making it easier to find bugs and debug issues, especially those adopting a TDD (Test Driven Development) approach. </p>
<h3 id="heading-parallel-testing">Parallel Testing</h3>
<p>Mocking enables parallel testing (multiple tests being ran at the same time) by removing dependencies on external resources that may be limited or inaccessible during testing. </p>
<p>For example if you weren't mocking your database connection layer, trying to run tests in parallel may cause inconsistent pass/fail metrics, as another test could be affecting the database table you're utilizing in another test. Mocking this layer means your tests are now agnostic to this layer, and can be ran at the same time. </p>
<h3 id="heading-reduced-test-maintenance">Reduced Test Maintenance</h3>
<p>Since mock objects encapsulate the behavior of dependencies, changes to those dependencies do not necessarily require updates to the tests themselves. This reduces the maintenance overhead associated with evolving codebases and dependencies.</p>
<h3 id="heading-enhanced-test-coverage">Enhanced Test Coverage</h3>
<p>Mocking allows developers to simulate a wide range of scenarios and edge cases. By controlling the behavior of dependencies, developers can ensure that their tests exercise all relevant paths through the code, removing any real life or physical limitations.</p>
<h2 id="heading-things-to-be-aware-of-with-mocking">Things To Be Aware of With Mocking</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-20.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Complexity:</strong> Sometimes when mocking/testing complex areas of the application, mocking can also become complex. However, if the system is too difficult to mock, you may need to reassess your architecture. </p>
<p><strong>Learning Curve:</strong> It requires an understanding of the syntax and concepts of the mocking library, which can be challenging for developers who are new to unit testing or the specific framework.</p>
<p><strong>Over-specification</strong>: Mocking can lead to over-specifying the behavior of the code under test. This means that tests may become tightly coupled to the implementation details, making them brittle and prone to breaking when the implementation changes. It's essential to strike a balance between verifying behavior and focusing on the desired outcomes.</p>
<p><strong>Be mindful of false-positive tests:</strong> While mocking allows you to isolate units of code, it may also create a false sense of security. Mocks simulate dependencies, but they may not fully replicate the behaviour of the real dependencies. <a target="_blank" href="https://www.browserstack.com/guide/integration-testing">Integration</a> tests or <a target="_blank" href="https://www.browserstack.com/guide/end-to-end-testing">end-to-end tests</a> are still necessary to verify the system's behavior as a whole.  </p>
<h2 id="heading-popular-net-mocking-libraries">Popular .NET Mocking Libraries</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-25.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here are some popular .NET testing libraries:</p>
<ul>
<li>FakeItEasy</li>
<li>NSubstitute</li>
<li>Moq</li>
<li>Rhino Mocks</li>
</ul>
<p>These are just a list of the most commonly used .NET mocking libraries available online, but there are many more. I'd highly recommend using one of these as they have a larger community, more reputable code base, and good documentation (vital when starting out).   </p>
<p>Each of these mocking libraries have their own syntax for creating mocks of objects however they all follow the same principles. </p>
<ol>
<li>Declare the object/type/service you'd like to mock</li>
<li>How you want that object/type/service to run (the implementation)</li>
<li>What is the returned value (where necessary). </li>
</ol>
<h2 id="heading-looking-at-the-tests">Looking at the Tests</h2>
<p>If you open the solution, and navigate to the "Test" project, you can see that we have four files with each of the different mocking library tests in there. </p>
<ol>
<li>FakeItEasyApiTests.cs</li>
<li>MoqApiTests.cs</li>
<li>NSubstituteApiTests.cs</li>
<li>RhinoMocksApiTests.cs</li>
</ol>
<p>Within these files, you will see that we have four very basic XUnit tests. I've kept them brief and simple for the purpose of this tutorial. </p>
<h2 id="heading-deep-dive-into-the-test-structure">Deep Dive Into the Test Structure</h2>
<p>Each test file uses a constructor to initialize a private version of their respectable services, and you can see how these differ from one library to the next, yet still follow the same concepts. </p>
<pre><code class="lang-csharp"><span class="hljs-comment">// FakeItEasy</span>
 _fakeTodoService = A.Fake&lt;ITodoService&gt;();

<span class="hljs-comment">// NSubstitute</span>
  _substituteTodoService = Substitute.For&lt;ITodoService&gt;();

<span class="hljs-comment">// Moq</span>
 _mockTodoService = <span class="hljs-keyword">new</span> Mock&lt;ITodoService&gt;();

<span class="hljs-comment">// Rhino Mocks</span>
 _mockTodoService = MockRepository.GenerateMock&lt;ITodoService&gt;();
</code></pre>
<p>Choosing the "right" mocking library all comes down to personal preference, and what you feel is easier to write, work with and read/understand.  </p>
<p>Some may find the use of words like <code>Fake</code>, or <code>Substitute.For</code> easier to comprehend or read. Whereas others may find the <code>A.Fake</code> unintuitive and prefer <code>new Mock&lt;type&gt;</code> more obvious.</p>
<h2 id="heading-arrange-act-and-assert">Arrange, Act and Assert</h2>
<p>Following the testing principle of AAA (Arrange, Act and Assert) we can carefully structure our tests, making it obvious what we're doing and where. </p>
<p>The AAA approach to testing involves three steps:</p>
<ol>
<li><strong>Arrange</strong>: Set up the test environment, mocked services/external dependencies.</li>
<li><strong>Act</strong>: Perform the action being tested.</li>
<li><strong>Assert</strong>: Verify the expected outcome.</li>
</ol>
<h2 id="heading-using-mocks-to-simulate-returned-items">Using Mocks to Simulate Returned Items</h2>
<p>Let's test the <code>getAll</code> (GetAllTodoItems) endpoint by mocking the <code>TodoService.GetAllTodos</code> method to return a stubbed list of tasks. </p>
<p>This approach eliminates the need to set up and seed a database for each test scenario, ensuring focused testing of API endpoint returning values against specific criteria. </p>
<p>Mocks provide an ideal solution, allowing us to simulate the desired behavior without interference from other tests.   </p>
<p>We can do this like so (remember the full code is available in the repo):</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// FakeItEasy</span>
A.CallTo(() =&gt; _fakeTodoService.GetAllTodos()).Returns(expectedTodos);

<span class="hljs-comment">// NSubstitute</span>
 _substituteTodoService.GetAllTodos().Returns(expectedTodos);

 <span class="hljs-comment">// Moq</span>
 _moqTodoService.Setup(s =&gt; s.GetAllTodos()).Returns(expectedTodos);

 <span class="hljs-comment">// Rhino Mocks</span>
 _mockTodoService.Stub(s =&gt; s.GetAllTodos()).Return(expectedTodos);
</code></pre>
<h3 id="heading-what-are-these-methods-doing">What are these methods doing?</h3>
<p>A common feature you will see across the majority of libraries is the usage of lambda functions to dictate which method is being mocked.</p>
<p>The lambda function provided in the setup method is essentially a configuration step that defines what action should be taken when the mocked method is called. This configuration is stored and applied when the mocked method is invoked during the test.  </p>
<p>Let's break it down, what we're doing:</p>
<ol>
<li><p>The lambda specifies which method on the mocked service we wish to configure/setup.  </p>
</li>
<li><p>The lambda we pass in isn't ran straight away by the setup method. We're not telling the test to run the method immediately; we're saying, "Remember this instruction/setup for when the actual method is called during the test."  </p>
</li>
<li><p>When the method we're mocking gets called during the test then it checks if the call signature matches a setup configuration we've provided.  If it matches, the test follows the instructions given during setup. </p>
</li>
</ol>
<h3 id="heading-important-notes">Important Notes:</h3>
<p>NSubstitute, on the other hand, allows the developer to mock the method directly on the fake object. This means that you can access the fake <code>GetAllTodos</code> method and simply set the return value to your expected value.   </p>
<p>Although Moq uses a Setup method, it differs ever so slightly from the other methods. Moq internally creates a proxy object that represents the mocked object, and exposes a <code>.Object</code> property to access it. We'll see how this works in the next part. </p>
<h2 id="heading-how-to-call-the-system-under-test-sut">How to Call The System Under Test (SUT)</h2>
<pre><code class="lang-csharp"><span class="hljs-comment">// FakeItEasy</span>
<span class="hljs-keyword">var</span> sut = <span class="hljs-keyword">new</span> TodoController(_fakeTodoService);

<span class="hljs-comment">// NSubstitute</span>
<span class="hljs-keyword">var</span> sut = <span class="hljs-keyword">new</span> TodoController(_substituteTodoService);

<span class="hljs-comment">// Moq -- slightly different to the others</span>
 <span class="hljs-keyword">var</span> sut = <span class="hljs-keyword">new</span> TodoController(_moqTodoService.Object);

<span class="hljs-comment">// Rhino Mocks</span>
<span class="hljs-keyword">var</span> sut = <span class="hljs-keyword">new</span> TodoController(_mockTodoService);
</code></pre>
<p>In three of the four libraries, you can pass the mocked object directly. However, Moq requires accessing the <code>.Object</code> property on the mock to use it. Thus, we passed <code>moqTodoService.Object</code> as an argument to the controller.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-26.png" alt="Image" width="600" height="400" loading="lazy">
<em>an image showing that all the tests passed</em></p>
<p>Running the tests, you can see they all pass. Should you change any of the code in the repository, it would not make any difference as the repo is mocked in these tests. Have a go, try changing the repo functionality and re-running the tests, they will continue to pass.   </p>
<p>We are concentrating on the functionality of the endpoint, not what the mocked repo is doing, and this is the beauty of mocking.   </p>
<h2 id="heading-the-options-with-mocking-are-endless">The Options with Mocking Are Endless</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/image-5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Mocking doesn't just allow us to set what is returned from a mocked object, it can also allow us to mock the implementation, including the ability to throw specific errors so we can test our API error handling and unhappy paths too.</p>
<p>This is illustrated in the <code>Delete_Returns500_AndErrorMessageThrown_WhenExceptionThrown</code> test within each library test file. </p>
<pre><code class="lang-csharp"><span class="hljs-comment">// FakeItEasy</span>
A.CallTo(() =&gt; _fakeTodoService.Delete(<span class="hljs-number">1</span>)).Throws(<span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// NSubstitute</span>
_substituteTodoService.When(x =&gt; x.Delete(<span class="hljs-number">1</span>)).Do(x =&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// Moq</span>
_moqTodoService.Setup(s =&gt; s.Delete(<span class="hljs-number">1</span>)).Throws(<span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// Rhino Mocks</span>
 _mockTodoService.Stub(s =&gt; s.Delete(<span class="hljs-number">1</span>)).Throw(<span class="hljs-keyword">new</span> Exception(errorMessage));
</code></pre>
<p>Using the different libraries, we can make the <code>Delete</code> method on the service throw whatever exception we'd like to. This is ideal for when you want to return different status codes, or handle errors in different ways depending on the type of exception thrown. </p>
<p>As an example we could change <code>Throws(new Exception(errorMessage)</code> to <code>Throws(new UnauthorizedAccessException()</code> and test that if a 401 status code is returned when thrown.</p>
<h2 id="heading-global-setup">Global Setup</h2>
<p>You can assign multiple configurations to the same method. This is great in situations where you want to set up all your configurations of the mocked object in one place. For example, in the test class constructor. </p>
<p>In some test frameworks (like NUnit) you can use a <code>[OneTimeSetUp]</code> attribute above your method, which is ran before your test cases, or simply use your test class constructor.  </p>
<p>In this scenario you could do something (in Moq) like:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MoqApiTests</span>(<span class="hljs-params"></span>)</span>
{
        _mockTodoService = <span class="hljs-keyword">new</span> Mock&lt;ITodoService&gt;();
        _mockTodoService.Setup(x =&gt; x.Delete(<span class="hljs-number">1</span>)).Throws(<span class="hljs-keyword">new</span> Exception(<span class="hljs-string">"This is a generic exception"</span>));
        _mockTodoService.Setup(x =&gt; x.Delete(<span class="hljs-number">2</span>)).Throws(<span class="hljs-keyword">new</span> UnauthorizedAccessException(<span class="hljs-string">"You cannot perform this action on this item"</span>));
}
</code></pre>
<p>In this example, we demonstrate setting up mocked service calls to the same method with various arguments, each causing it to throw different exceptions. </p>
<p>This approach is beneficial for testing different outcomes when different exceptions occur in separate tests, without cluttering our test code with repetitive setup. </p>
<p>For example:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Test 1</span>
<span class="hljs-keyword">var</span> result = TodoController.Delete(<span class="hljs-number">1</span>);
<span class="hljs-comment">// Assert handles general exception</span>

<span class="hljs-comment">// Test 2</span>
<span class="hljs-keyword">var</span> result = TodoController.Delete(<span class="hljs-number">2</span>); 
<span class="hljs-comment">// Assert handles UnauthorizedAccessException</span>
</code></pre>
<p>I prefer to set up mocks within each individual tests to ensure there are no external factors influencing the mock. </p>
<p>This way, I can easily identify what is being mocked within the test without searching for the mocked object and setup elsewhere.</p>
<h2 id="heading-what-if-i-dont-care-what-im-passing-in">What If I Don't Care What I'm Passing In?</h2>
<p>In our deletion examples, we consistently passed an ID to the mock implementation. Consequently, if we were to call the method with a different ID like <code>101</code> through the <code>TodoController.DeleteTodoItem</code> call, we wouldn't receive the same result. </p>
<p>This is because we explicitly instructed the mocked object to throw an error when the stubbed method is called with an ID of 1.</p>
<p>To address this issue, we can be less specific. Each library has its own syntax for this, enabling us to specify that if any integer is passed to the method, it will throw a particular exception.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// FakeItEasy</span>
A.CallTo(() =&gt; _fakeTodoService.Delete(A&lt;<span class="hljs-keyword">int</span>&gt;._)).Throws( <span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// NSubstitute</span>
_substituteTodoService.When(x =&gt; x.Delete(Arg.Any&lt;<span class="hljs-keyword">int</span>&gt;())).Do(x =&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// Moq</span>
_mockTodoService.Setup(s =&gt; s.Delete(It.IsAny&lt;<span class="hljs-keyword">int</span>&gt;())).Throws(<span class="hljs-keyword">new</span> Exception(errorMessage));

<span class="hljs-comment">// Rhino Mocks</span>
_mockTodoService.Stub(s =&gt; s.Delete(Arg&lt;<span class="hljs-keyword">int</span>&gt;.Is.Anything)).Throw(<span class="hljs-keyword">new</span> Exception(errorMessage));
</code></pre>
<p>This code indicates that when any argument of type <code>int</code> is passed, it should throw this exception.</p>
<p>NSubstitute differs slightly in syntax: it requires explicit instruction to throw the specified error when encountering this scenario, unlike when were were informing it to return an object. This difference stems from the internal mechanisms of the library.</p>
<h2 id="heading-asserting-mocks-are-called">Asserting Mocks Are Called</h2>
<p>In some cases, you might want to verify that a mocked service is called with the correct arguments. This is particularly useful when dealing with a "fire-and-forget" service. </p>
<p>In this scenario, your API endpoint is called, and while it always returns true, it also triggers a service to perform some action independently, which doesn't affect the API's return type (perhaps an asynchronous notification service).</p>
<p>This is one of the few instances where you may want to perform a quick sanity check to ensure that your "fire-and-forget" service is invoked (although ideally, you'd conduct an integration test with that service).</p>
<p>If you take a look at the <code>DeleteTodoItem</code> endpoint, and the <code>DeleteAPI_CallsNotificationService_WithTaskId_AndUserId</code> test within each test file you can see full examples of how this can be done.   </p>
<p>We are verifying that when we call the <code>DeleteTodoItem</code>, on our happy path, the <code>NotificationService.NotifyUserTaskCompleted</code> is called with the ID of the item for deletion, and the hard-coded user ID.  </p>
<p>As an exercise, you could create a user service which returns an ID of logged in user, and this can be used to pass the ID to the service. This can also be mocked in the test. </p>
<pre><code class="lang-csharp"> <span class="hljs-comment">// FakeItEasy</span>
  A.CallTo(() =&gt; _fakeNotificationService.NotifyUserTaskCompleted(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>)).MustHaveHappened(<span class="hljs-number">1</span>, Times.Exactly);

<span class="hljs-comment">// NSubstitute _notificationService.Received().NotifyUserTaskCompleted(1,1);</span>

<span class="hljs-comment">// Moq </span>
 _moqNotificationService.Verify(x =&gt; x.NotifyUserTaskCompleted(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>)); <span class="hljs-comment">// Defaults to Times.AtLeastOnce</span>

<span class="hljs-comment">// Rhino Mock</span>
_mockNotificationService.AssertWasCalled(x=&gt;x.NotifyUserTaskCompleted(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>));
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, the versatility of mocked objects offers a myriad of applications, proving indispensable in the testing of individual units of code. </p>
<p>While I've covered several functionalities and validations achievable with mocks, there are more, such as method call order and negative validation.  </p>
<p>In my view, the choice of a mocking library for a project is subjective, with no definitive right or wrong option. While some libraries may offer more convenient extensions or clearer syntax, the decision ultimately boils down to personal preference.   </p>
<p>My hope is that this tutorial has provided you with a glimpse into the world of mocking and shed light on the syntax variances across different libraries.</p>
<p>As always, don't hesitate to reach out (links in bio). </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn .NET MAUI for cross-platform apps ]]>
                </title>
                <description>
                    <![CDATA[ Are you looking to develop cross-platform desktop and mobile applications? If so, we have just the course for you! We just published a full course on the freeCodeCamp.org YouTube channel that will teach you the essential skills you need to build your... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-net-maui-for-cross-platform-apps/</link>
                <guid isPermaLink="false">66b204cdeea9870582e16c95</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 04 May 2023 15:09:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/netmaui.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you looking to develop cross-platform desktop and mobile applications? If so, we have just the course for you!</p>
<p>We just published a full course on the freeCodeCamp.org YouTube channel that will teach you the essential skills you need to build your own mobile applications using .NET MAUI.</p>
<p>Frank Liu created this course. Frank is a full stack developer with about 20 years of development experience.</p>
<p>Throughout the course, you will learn how to create a Contacts app from scratch, using .NET MAUI. You will start with an introduction to .NET MAUI and learn how it compares to Xamarin Forms. Next, you will set up your development environment and create your first project.</p>
<p>From there, the course will guide you through the project structure, the three elements of stateful .NET MAUI, and page, layout, and view namespaces. You will also learn about URL-based navigation and the basics of ListView and data binding.</p>
<p>The course will then dive deeper into ListView and cover events handling, parameters in URL-based navigation, and using static repositories. You will also learn about stack layout for the Edit Contact page and view contact details, and update contact information.</p>
<p>In addition, the course covers more advanced topics such as Observable Collection, field validation with .NET MAUI CommunityToolkit, and creating reusable controls. You will also learn about grid layout and context actions, as well as how to use MenuItems in ListView and add a SearchBar in .NET MAUI.</p>
<p>By the end of the course, you will have gained the skills and knowledge necessary to build your own mobile applications with .NET MAUI. This course is perfect for anyone looking to develop cross-platform desktop and mobile applications using .NET MAUI.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/n3tA3Ku65_8">the freeCodeCamp.org YouTube channel</a> (3-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/n3tA3Ku65_8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build Minimal APIs in .NET 7 ]]>
                </title>
                <description>
                    <![CDATA[ Minimal APIs offer a streamlined approach to constructing HTTP APIs with minimal dependencies, making them perfect for microservices and applications that aim to incorporate only the essential files, features, and dependencies within ASP.NET Core. We... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-minimal-apis-in-net-7/</link>
                <guid isPermaLink="false">66b2010aa8b92c932923640f</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 04 Apr 2023 18:41:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/minimalapi.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Minimal APIs offer a streamlined approach to constructing HTTP APIs with minimal dependencies, making them perfect for microservices and applications that aim to incorporate only the essential files, features, and dependencies within ASP.NET Core.</p>
<p>We just published a coruse on the freeCodeCamp.org YouTube channel that will teach you the power of Minimal APIs in .NET 7 and build well-constructed endpoints with C#, .NET 7, and Swagger.</p>
<p>You'll gain a deep understanding of API basics, routing, dependency injection, and more, culminating in the ability to create well-constructed Minimal API endpoints.</p>
<h3 id="heading-topics-covered">Topics Covered</h3>
<p>This comprehensive course will cover the following topics:</p>
<ol>
<li>Why Minimal API? - Understand the advantages of using Minimal APIs in your projects and how they differ from traditional APIs.</li>
<li>Create Project - Set up a new project with .NET 7 and explore the differences between Minimal and Standard APIs in terms of project structure and files.</li>
<li>Program File Changes - Learn about the changes you'll need to make to your Program.cs file to accommodate Minimal APIs.</li>
<li>API Basics - Dive into the fundamentals of APIs, including requests, responses, and HTTP verbs.</li>
<li>Create First Endpoint - Build your first Minimal API endpoint using C# and .NET 7.</li>
<li>Route Parameters - Learn how to use route parameters in your Minimal API endpoints for more dynamic functionality.</li>
<li>Create Coupon Model and Coupon Store - Set up a data model and storage solution for a coupon system, which will be used in subsequent endpoints.</li>
<li>Get All Endpoint - Implement an endpoint to retrieve all coupons from the coupon store.</li>
<li>Get Individual Coupon - Develop an endpoint to fetch a specific coupon based on its unique identifier.</li>
<li>Create Coupon - Create an endpoint to add new coupons to the store.</li>
<li>Name Endpoints - Understand the importance of naming your endpoints and how to do so effectively.</li>
<li>Products and Accepts in Minimal API - Learn how to use the <code>Produces</code> and <code>Accepts</code> attributes to define supported media types in your Minimal API endpoints.</li>
<li>Dependency Injection in Minimal API - Explore the process of implementing dependency injection in your Minimal APIs to improve testability and maintainability.</li>
<li>Add DTOs - Implement Data Transfer Objects (DTOs) to separate your API's data representation from its internal data structures.</li>
<li>AutoMapper and Dependency Injection - Learn how to use AutoMapper to map data between your DTOs and data models, integrating it with dependency injection.</li>
<li>Fluent Validators - Implement validation logic for your DTOs using Fluent Validation.</li>
<li>Async Endpoints - Explore the benefits of asynchronous programming and how to create asynchronous endpoints in your Minimal API.</li>
<li>API Response - Learn how to create custom API responses and handle errors gracefully in your Minimal API.</li>
<li>Assignment - Put and Delete - Apply your newfound knowledge by implementing update (PUT) and delete (DELETE) endpoints in your coupon system.</li>
<li>Assignment Solution - Put and Delete Endpoints - Review the solutions for the PUT and DELETE endpoints assignment, comparing your implementation with the recommended approach.</li>
</ol>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Upon completing this course, you'll have a solid understanding of how to implement Minimal APIs in .NET 7, and build robust and efficient API endpoints. </p>
<p>Watch the course on <a target="_blank" href="https://youtu.be/lFo3Yy8Ro7w">the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/lFo3Yy8Ro7w" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Create a React App With a .NET Core Backend ]]>
                </title>
                <description>
                    <![CDATA[ ASP.NET Core can work well as a backend for your React apps. We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to to create a basic React application that leverages a .NET Web API Component written in C#. In o... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-react-app-with-a-dot-net-backend/</link>
                <guid isPermaLink="false">66b201a3a8b92c932923642d</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 03 Jan 2023 19:57:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/webapi.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>ASP.NET Core can work well as a backend for your React apps.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to to create a basic React application that leverages a .NET Web API Component written in C#.</p>
<p>In order to integrate React with a .NET Web API component this course utilizes the 'ASP.NET Core with React.js' project template from within the free version of Visual Studio 2022 (the community edition). The App that you will build enables users to rank items by dragging and dropping the items (displayed in a list) to a cell position on the ranking grid (the grid is positioned above the list of relevant items).</p>
<p>Each cell position on the grid denotes a ranking value. For example, the top left cell is position number 1 denoting the top ranked item and the cell positioned to the immediate right of this cell denotes the 2nd ranked item. This continues all the way to the bottom right cell which is the worst possible ranking position.</p>
<p>Coding this project will give you an insight into how to build a simple app using React on the front-end that leverages a .NET Web API component on the backend.</p>
<p>Gavin Lon created this course. He is an experienced software developer and teacher.</p>
<p>Here are all the sections covered in this course:</p>
<ul>
<li>Project Creation</li>
<li>Create the Server-side .NET Web API Code in C#</li>
<li>Create 'RankItems' React Component</li>
<li>Create Navigation Functionality</li>
<li>Write Code to Import Movie Images</li>
<li>Write Code to Display Movie Images</li>
<li>Write Code for 'RankingGrid' React Component</li>
<li>Write Code for Drag and Drop Functionality</li>
<li>Write Code for Ranking Albums</li>
<li>Write Code for Initialising Ranking Items (Reload Button)</li>
</ul>
<p>Watch the full course <a target="_blank" href="https://youtu.be/4RKuyp_bOhY">on the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/4RKuyp_bOhY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Debug Dockerized .NET Core Apps in VS Code ]]>
                </title>
                <description>
                    <![CDATA[ By Haseeb Ahmed I recently switched to VS Code for development of a dockerized .NET core app. But I then realized that there weren't many up-to-date articles about debugging dockerized .NET core applications in VS Code. Source: GIPHY So, here I am, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-debug-dockerized-net-core-apps-in-vs-code/</link>
                <guid isPermaLink="false">66d461498812486a37369d66</guid>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Visual Studio Code ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 08 Sep 2022 15:34:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/debug_dockerized_dotnet_core_apps-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Haseeb Ahmed</p>
<p>I recently switched to VS Code for development of a dockerized .NET core app. But I then realized that there weren't many up-to-date articles about debugging dockerized .NET core applications in VS Code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-217.png" alt="Image" width="600" height="400" loading="lazy">
<em>Source: GIPHY</em></p>
<p>So, here I am, writing this article. I hope that it'll help others like myself in debugging their dockerized .NET core applications in VS Code. </p>
<p>In this article we will discuss the following:</p>
<ol>
<li>VS Code extensions for Docker</li>
<li>VS Code launch configurations</li>
<li>How to debug .NET core apps on your local Docker</li>
</ol>
<h1 id="heading-vs-code-extensions-for-docker">VS Code Extensions for Docker</h1>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-218.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Although no extension is really required (for you hardcore minimalists) to debug containerzied .NET core applications, I'd still recommend that you install the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker">Docker extension</a> by Microsoft. It will make it easy to create, manage, and debug containerized applications from right within VS Code. </p>
<p>Honestly, ever since I've started using the extension, I don't use the Docker Desktop anymore.</p>
<h1 id="heading-vs-code-launch-configurations">VS Code Launch Configurations</h1>
<p>Now, in order to debug dockerized .NET Core applications, we will need to create a VS code launch configuration. </p>
<p>In this section, I'll talk about two sorts of launch configurations: one for docker run and the other for docker compose. </p>
<p>Later on, we'll also look into the final launch configurations and understand them so that we know what we're doing.</p>
<h2 id="heading-docker-run-launch-configuration">Docker Run Launch Configuration</h2>
<p>If you installed the Docker extension by Microsoft, this should be easy.</p>
<p>First, press "Ctrl + P" in VS code and type in <code>&gt; Docker:</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-221.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, select "Docker: Initialize for Docker Debugging".</p>
<p>Then select ".NET: ASP.NET Core" as your application platform. Select "Linux" as your operating system.</p>
<p>And voilà! You should now have a "Docker .NET Core Launch" launch configuration.</p>
<p>Note that Docker extension will want to overwrite your existing dockerfile. If you don't let it, it won't create the launch configurations. I'd recommend that you backup your dockerfile, let the extension overwrite it, and then restore your original file.</p>
<h2 id="heading-docker-compose-launch-configurations">Docker Compose Launch Configurations</h2>
<p>Now we all know that real-life applications are never that simple! You would probably have a DB container, an app container, and a few other containers all connected together via a docker-compose configuration. </p>
<p>If that is the case, here is how you'll create your launch configurations.</p>
<p>First, open your workspace in VS Code. If you already have existing launch configurations, you can skip the next part.</p>
<p>Next, press "Ctrl + Shift + D" to switch to the "Run &amp; Debug Tab".</p>
<p>Click on "create a launch.json file" and choose ".NET 5+ and .NET Core" (This will create a basic launch configuration for running your application without Docker).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-222.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, while your launch.json file is open, click on Add Configuration button in the bottom right corner of the editor.</p>
<p>Select "Docker: .NET Core Attach (Preview)" from the opened dropdown.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-229.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>It should have added a new configuration called "Docker: .NET Core Attach (Preview)".</p>
<p>Voilà! Now you're ready to debug your dockerized .NET Core application in VS Code.</p>
<h2 id="heading-how-to-understand-launch-configurations">How to Understand Launch Configurations</h2>
<p>Before we start debugging our application, let's look into the launch configurations a bit closer to understand how they work. </p>
<p>There should be two files in the .vscode folder, in the root of your workspace. </p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2.0.0"</span>,
    <span class="hljs-attr">"tasks"</span>: [
        {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"docker-build"</span>,
            <span class="hljs-attr">"label"</span>: <span class="hljs-string">"docker-build: debug"</span>,
            <span class="hljs-attr">"dependsOn"</span>: [
                <span class="hljs-string">"build"</span>
            ],
            <span class="hljs-attr">"dockerBuild"</span>: {
                <span class="hljs-attr">"tag"</span>: <span class="hljs-string">"webapi:dev"</span>,
                <span class="hljs-attr">"target"</span>: <span class="hljs-string">"base"</span>,
                <span class="hljs-attr">"dockerfile"</span>: <span class="hljs-string">"${workspaceFolder}/src/Dockerfile"</span>,
                <span class="hljs-attr">"context"</span>: <span class="hljs-string">"${workspaceFolder}"</span>,
                <span class="hljs-attr">"pull"</span>: <span class="hljs-literal">true</span>
            },
            <span class="hljs-attr">"netCore"</span>: {
                <span class="hljs-attr">"appProject"</span>: <span class="hljs-string">"${workspaceFolder}/src/webapi.csproj"</span>
            }
        },
        {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"docker-run"</span>,
            <span class="hljs-attr">"label"</span>: <span class="hljs-string">"docker-run: debug"</span>,
            <span class="hljs-attr">"dependsOn"</span>: [
                <span class="hljs-string">"docker-build: debug"</span>
            ],
            <span class="hljs-attr">"dockerRun"</span>: {},
            <span class="hljs-attr">"netCore"</span>: {
                <span class="hljs-attr">"appProject"</span>: <span class="hljs-string">"${workspaceFolder}/src/webapi.csproj"</span>,
                <span class="hljs-attr">"enableDebugging"</span>: <span class="hljs-literal">true</span>
            }
        }
    ]
}
</code></pre>
<p>First, let's take a look at the tasks.json file. This file has list of tasks that might be required by some of the launch configurations to launch the application properly.</p>
<p>The task we want to look at is "docker-run: debug". This is the task called when you launch using the "Docker .NET Core Launch" configuration (as we'll see later).</p>
<p>This task has three properties that we need to understand:</p>
<ul>
<li>netCore.appProject: This property is .NET Core app-specific and just points to the project file of your app.</li>
<li>netCore.enableDebugging: This is another .NET Core app-specific property which tells VS code to launch the app with debugging capabilities.</li>
<li>dependsOn: This is a generic property that defines if a task depends on other tasks for its execution.</li>
</ul>
<p>Secondly, we need to understand what the "docker-build: debug" task does. </p>
<p>In addition to the netCore and dependsOn properties, it has a dockerBuild object that controls the <code>docker build</code> command which is run by VS Code to launch a docker run app. </p>
<p>Properties of the dockerBuild object are quite similar to the arguments that you pass to the <code>docker build</code> command.</p>
<p>You can read about all the dockerBuild object properties <a target="_blank" href="https://code.visualstudio.com/docs/containers/reference#_dockerbuild-object-properties">here</a> and tasks in general <a target="_blank" href="https://code.visualstudio.com/docs/editor/tasks">here</a>.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
  <span class="hljs-attr">"configurations"</span>: [
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Docker .NET Core Attach"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"docker"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"attach"</span>,
      <span class="hljs-attr">"platform"</span>: <span class="hljs-string">"netCore"</span>,
      <span class="hljs-attr">"sourceFileMap"</span>: {
        <span class="hljs-attr">"/src"</span>: <span class="hljs-string">"${workspaceFolder}/src"</span>
      }
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Docker .NET Core Launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"docker"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"preLaunchTask"</span>: <span class="hljs-string">"docker-run: debug"</span>,
      <span class="hljs-attr">"netCore"</span>: {
        <span class="hljs-attr">"appProject"</span>: <span class="hljs-string">"${workspaceFolder}/src/webapi.csproj"</span>
      }
    }
  ]
}
</code></pre>
<p>Now, let's take a look at the launch.json file where all the launch configurations live. </p>
<p>While most of these properties are standard, the one we care about is "sourceFileMap" in the "Docker .NET Core Attach" configuration. </p>
<p>In order to debug .NET Core applications that were built on a machine other than the VS Code machine (in this case a Docker), VS Code needs to understand how-to map your current workspace to the build machine hierarchy. </p>
<p>For example, if my project was built from the "/src" folder in Linux, this property will tell VS Code to replace "/src" with "${workspaceFolder}/src" in all the filepaths. In case this mapping is incorrect, VS Code will hit the breakpoint but will give an error that the file (being debugged) does not exist.</p>
<p>You can read more about launch.json properties in detail over <a target="_blank" href="https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes">here</a>.</p>
<h1 id="heading-how-to-debug-net-core-apps-on-your-local-docker">How to Debug .NET Core Apps on Your Local Docker</h1>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-230.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-docker-run">Docker Run</h2>
<p>Now that we have the launch configurations figured out, this should be easy! Just follow the steps below.</p>
<p>First, press "Ctrl + Shift + D" to switch to the "Run &amp; Debug Tab".</p>
<p>Then choose "Docker .NET Core Launch" and press the green play icon to debug.</p>
<h2 id="heading-docker-compose">Docker Compose</h2>
<p>Debugging a Docker Compose container is slightly different. You'll have to make sure that your Docker Compose containers are already running before you attach the VS Code debugger. </p>
<p>Once they are up, follow these steps:</p>
<p>First, press "Ctrl + Shift + D" to switch to the "Run &amp; Debug Tab".</p>
<p>Then choose "Docker: .NET Core Attach (Preview)" and press the green play icon to debug.</p>
<p>That is, it! I hope you found this helpful. If you have any questions or are confused about anything, you can reach out to me or you can check out the sample project on <a target="_blank" href="https://github.com/thehaseebahmed/vscode-dotnet-docker-debug">GitHub</a>.</p>
<p>Happy Coding!</p>
<p>This article is a part of my <a target="_blank" href="https://blog.thehaseebahmed.com/series/coding">coding series</a>. You may find other useful articles for your daily development there as well.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Create an Industry Level REST API Using .NET 6 ]]>
                </title>
                <description>
                    <![CDATA[ There are a lot of REST API tutorials out there but many of them do not teach you how to create production-ready code using industry best practices. We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to create ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-an-industry-level-rest-api-using-net-6/</link>
                <guid isPermaLink="false">66b201c5a2135cc2539a215f</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Mon, 01 Aug 2022 14:21:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/maxresdefault.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>There are a lot of REST API tutorials out there but many of them do not teach you how to create production-ready code using industry best practices.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to create a REST API using industry-level .NET code. You will learn how to write code similar to what they write at top tech companies.</p>
<p>Amichai Mantinband created this course. Amichai is a software engineer at Microsoft and before that he worked as a college computer science instructor. </p>
<p>This course is a concise, start-to-finish video that developers can use as a template to onboard/structure small to medium sized industry-level CRUD applications.</p>
<p>Here are the sections covered in this course:</p>
<ul>
<li>Backend server architecture</li>
<li>Implementing logic of API model</li>
<li>Refactoring routes</li>
<li>Create model for request data</li>
<li>Create service interface</li>
<li>Implement additional methods</li>
<li>Handling errors</li>
<li>Refactoring controller and services</li>
<li>Refactoring error handling</li>
<li>Testing API requests</li>
</ul>
<p>Watch the full course below or <a target="_blank" href="https://youtu.be/PmDJIooZjBE">on the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/PmDJIooZjBE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
