<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Engincan VESKE</title>
    <description>The latest articles on Forem by Engincan VESKE (@engincanv).</description>
    <link>https://forem.com/engincanv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F634587%2F2aa94c41-2f0e-4c58-8716-1ea489c83d13.png</url>
      <title>Forem: Engincan VESKE</title>
      <link>https://forem.com/engincanv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/engincanv"/>
    <language>en</language>
    <item>
      <title>Most Popular .NET Domain-Driven Design (DDD) Resources: Top 5 GitHub Repositories, Frameworks &amp; Templates in 2025</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Wed, 04 Jun 2025 06:20:04 +0000</pubDate>
      <link>https://forem.com/engincanv/most-popular-net-domain-driven-design-ddd-resources-top-5-github-repositories-frameworks--n9o</link>
      <guid>https://forem.com/engincanv/most-popular-net-domain-driven-design-ddd-resources-top-5-github-repositories-frameworks--n9o</guid>
      <description>&lt;p&gt;Domain-Driven Design (DDD) is a software development approach that focuses on creating software that closely models complex business domains. Introduced by Eric Evans, DDD emphasizes collaboration between technical and domain experts to create a shared understanding of the problem space through ubiquitous language, bounded contexts, and rich domain models.&lt;/p&gt;

&lt;p&gt;The best way to master Domain-Driven Design in .NET is by exploring real-world implementations through popular GitHub repositories, established frameworks, ready-to-use templates, and comprehensive sample projects. These resources provide practical insights into how DDD principles are applied in production-grade applications, making complex concepts tangible and actionable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top 10 Most Popular .NET DDD Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 🏆 ABP Framework - The Complete DDD Ecosystem
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⭐ GitHub Stars: 13,500+ | 🍴 Forks: 3,600+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ABP Framework stands out as the most comprehensive Domain-Driven Design resource in the .NET ecosystem, offering everything developers need in one unified platform: a robust framework, extensive sample projects, detailed documentation, ready-to-use templates, and a complete infrastructure for DDD implementation.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What Makes ABP Unique:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete DDD Infrastructure&lt;/strong&gt;: Pre-built implementation of all DDD patterns including aggregates, repositories, domain services, and domain events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich Sample Repository&lt;/strong&gt;: &lt;a href="https://github.com/abpframework/abp-samples" rel="noopener noreferrer"&gt;abp-samples&lt;/a&gt; contains dozens of real-world examples demonstrating DDD principles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Templates&lt;/strong&gt;: Startup templates for various architectures (modular monolith, microservices, layered architecture)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production-Ready Framework&lt;/strong&gt;: Used by thousands of enterprise applications worldwide&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Resources&lt;/strong&gt;: &lt;a href="https://abp.io/docs/latest/tutorials" rel="noopener noreferrer"&gt;Comprehensive tutorials&lt;/a&gt; (for modular monolith, microservices, and layered architecture), &lt;a href="https://abp.io/docs/latest/best-practices" rel="noopener noreferrer"&gt;best practices&lt;/a&gt;, and &lt;a href="https://abp.io/docs/latest/framework/architecture" rel="noopener noreferrer"&gt;architectural guidance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔗 Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main Repository: &lt;a href="https://github.com/abpframework/abp" rel="noopener noreferrer"&gt;github.com/abpframework/abp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sample Projects: &lt;a href="https://github.com/abpframework/abp-samples" rel="noopener noreferrer"&gt;github.com/abpframework/abp-samples&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Templates: Available via ABP CLI and &lt;a href="https://abp.io/studio" rel="noopener noreferrer"&gt;ABP Studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/abpframework/eShopOnAbp" rel="noopener noreferrer"&gt;eShopOnAbp&lt;/a&gt; - A reference DDD project for those who want to build microservice solutions with the ABP Framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. eShopOnContainers - Microsoft's DDD Reference Implementation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⭐ GitHub Stars: 24,500+ | 🍴 Forks: 10,300+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microsoft's official reference application demonstrating DDD principles in a microservices architecture. This comprehensive sample showcases how to build distributed applications using Domain-Driven Design patterns with .NET and containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main Repository: &lt;a href="https://github.com/dotnet-architecture/eShopOnContainers" rel="noopener noreferrer"&gt;github.com/dotnet-architecture/eShopOnContainers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns" rel="noopener noreferrer"&gt;docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Clean Architecture Solution Template for ASP.NET Core by Jason Taylor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⭐ GitHub Stars: 18,300+ | 🍴 Forks: 3,900+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A popular solution template implementing Clean Architecture principles with DDD patterns. Provides an excellent starting point for new projects with pre-configured layers, CQRS implementation using MediatR, and comprehensive testing strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://github.com/jasontaylordev/CleanArchitecture" rel="noopener noreferrer"&gt;github.com/jasontaylordev/CleanArchitecture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://jasontaylor.dev/clean-architecture" rel="noopener noreferrer"&gt;jasontaylor.dev/clean-architecture&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. eShopOnWeb - Simplified DDD Monolith
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⭐ GitHub Stars: 10,400+ | 🍴 Forks: 5,700+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microsoft's monolithic reference application demonstrating DDD implementation in a traditional web application architecture. Perfect for teams transitioning from traditional approaches to Domain-Driven Design principles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://github.com/dotnet-architecture/eShopOnWeb" rel="noopener noreferrer"&gt;github.com/dotnet-architecture/eShopOnWeb&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Modular Monolith with DDD
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⭐ GitHub Stars: 12,200+ | 🍴 Forks: 1,900+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Shows how to implement DDD in a modular monolithic architecture, bridging the gap between traditional monoliths and microservices while maintaining strong domain boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://github.com/kgrzybek/modular-monolith-with-ddd" rel="noopener noreferrer"&gt;modular-monolith-with-ddd&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started with .NET DDD
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Begin with ABP Framework&lt;/strong&gt; for comprehensive understanding and rapid development (also for ecosystem level of understanding)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Study Microsoft's eShopOnContainers&lt;/strong&gt; for microservices architecture insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Clean Architecture templates&lt;/strong&gt; for structural understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examine specialized samples&lt;/strong&gt; for specific patterns like event sourcing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join community discussions&lt;/strong&gt; on GitHub and Stack Overflow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The journey to mastering Domain-Driven Design in .NET becomes significantly easier when you have access to these high-quality resources. Whether you're building your first DDD application or architecting complex enterprise systems, these repositories provide the foundation for success.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>ddd</category>
      <category>csharp</category>
      <category>abp</category>
    </item>
    <item>
      <title>MySQL with EF Core 9 in ABP: Avoiding Translation Issues</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Fri, 31 Jan 2025 18:04:44 +0000</pubDate>
      <link>https://forem.com/engincanv/mysql-with-ef-core-9-in-abp-avoiding-translation-issues-1il1</link>
      <guid>https://forem.com/engincanv/mysql-with-ef-core-9-in-abp-avoiding-translation-issues-1il1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Entity Framework Core 9 introduces several improvements, but if you are using MySQL with EF Core 9 (and using the &lt;code&gt;Pomelo.EntityFrameworkCore.MySql&lt;/code&gt;), you need to be aware of a critical issue related to query translation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;ABP Framework's MySQL provider (&lt;a href="https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.MySQL" rel="noopener noreferrer"&gt;Volo.Abp.EntityFrameworkCore.MySQL&lt;/a&gt;) relies on &lt;code&gt;Pomelo.EntityFrameworkCore.MySql&lt;/code&gt; NuGet package. However, as of now, the stable &lt;code&gt;9.0.0&lt;/code&gt; version of this package has not been released. (You can follow the upgrade status from &lt;a href="https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1841" rel="noopener noreferrer"&gt;https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1841&lt;/a&gt;, if you want).&lt;/p&gt;

&lt;p&gt;When using EF Core 9 with MySQL in ABP-based projects, you may encounter SQL translation issues. One of the key problems is that certain queries involving parameterized collections fail to translate correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To workaround this issue, you must explicitly enable the &lt;code&gt;TranslateParameterizedCollectionsToConstants()&lt;/code&gt; option in your EF Core configuration. Without this setting, the provider may not correctly translate certain SQL commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Apply the Fix
&lt;/h3&gt;

&lt;p&gt;Open the module class of the &lt;code&gt;*.EntityFrameworkCore&lt;/code&gt; project and configure the &lt;code&gt;AbpDbContextOptions&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyProjectEntityFrameworkCoreModule&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AbpModule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceConfigurationContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;//...&lt;/span&gt;

        &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AbpDbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseMySQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;//add the following line 👇&lt;/span&gt;
                &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TranslateParameterizedCollectionsToConstants&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;TranslateParameterizedCollectionsToConstants&lt;/code&gt; option ensures that parameterized collections are translated into constants, preventing SQL translation errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Updates
&lt;/h2&gt;

&lt;p&gt;The good news is that once &lt;code&gt;Pomelo.EntityFrameworkCore.MySql 9.0.0&lt;/code&gt; is officially released, this setting will become the default. Actually, it's going to be the default starting from &lt;code&gt;v9.0.0-preview.3.efcore.9.0.0&lt;/code&gt; as &lt;a href="https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1960#issuecomment-2575821923" rel="noopener noreferrer"&gt;announced here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until then, applying this temporary solution should allow you to use MySQL with EF Core 9 smoothly.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/abpframework/abp/issues/21879#issuecomment-2586221036" rel="noopener noreferrer"&gt;https://github.com/abpframework/abp/issues/21879#issuecomment-2586221036&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1960#issuecomment-2575821923" rel="noopener noreferrer"&gt;https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1960#issuecomment-2575821923&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>abp</category>
      <category>abpframework</category>
      <category>mysql</category>
      <category>entityframework</category>
    </item>
    <item>
      <title>ABP Suite: Best CRUD Page Generation Tool for .NET</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Wed, 14 Feb 2024 09:49:27 +0000</pubDate>
      <link>https://forem.com/engincanv/abp-suite-best-crud-page-generation-tool-for-net-2p8a</link>
      <guid>https://forem.com/engincanv/abp-suite-best-crud-page-generation-tool-for-net-2p8a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;In this article, I will list some CRUD page generation/code generation tools for .NET and then we will explore the ABP Suite that empowers .NET developers to generate CRUD pages and is distinguished from other tools by its features and user-friendly UI. &lt;/p&gt;

&lt;h2&gt;
  
  
  Code/CRUD Page Generation Tools
&lt;/h2&gt;

&lt;p&gt;There are some CRUD page generation tools that you may already know as listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://commercial.abp.io/tools/suite" rel="noopener noreferrer"&gt;ABP Suite (supports plenty of UI options - MVC, Blazor WASM, Blazor Server, Angular, etc. - also supports mobile options)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.radzen.com/documentation/" rel="noopener noreferrer"&gt;Radzen (only supports Blazor &amp;amp; Angular)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codesmithtools.com/product/generator#overview" rel="noopener noreferrer"&gt;CodeSmith Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Also, there are a bunch of open-source code generation tools with limited options...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Among these code-generation tools, ABP Suite is in a special place in my opinion. It's not just related to working on it, but also its capabilities, and what it's providing is really noteworthy. Therefore, in the following section, I want to mention what ABP Suite is and some of its capabilities.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are looking for a code generation tool that's gonna speed up your business, I highly recommend you to check the &lt;a href="https://commercial.abp.io/" rel="noopener noreferrer"&gt;ABP Commercial&lt;/a&gt;, try its demo, and see all of its capabilities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ABP Suite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://commercial.abp.io/" rel="noopener noreferrer"&gt;ABP Commercial&lt;/a&gt; is a complete web development platform built on &lt;a href="https://abp.io/" rel="noopener noreferrer"&gt;open-source ABP Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://commercial.abp.io/tools/suite" rel="noopener noreferrer"&gt;ABP Suite&lt;/a&gt; is a complementary tool to &lt;a href="https://commercial.abp.io/" rel="noopener noreferrer"&gt;ABP Commercial&lt;/a&gt; and allows you to build web pages in a matter of minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ABP Suite&lt;/strong&gt; allows you to easily create CRUD pages. You just need to define your entity and its properties and let the rest go to ABP Suite for you! ABP Suite generates all the necessary code for your CRUD page in a few seconds. It supports Angular, MVC, and Blazor (both Blazor Server and Blazor WASM) UIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating A New Solution
&lt;/h3&gt;

&lt;p&gt;You can create a new solution from the ABP Suite UI, by selecting one of the &lt;a href="https://docs.abp.io/en/commercial/latest/startup-templates/index" rel="noopener noreferrer"&gt;startup templates provided by ABP Commercial&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;You can select a &lt;strong&gt;template type&lt;/strong&gt;, &lt;strong&gt;project name&lt;/strong&gt;, &lt;strong&gt;output folder&lt;/strong&gt; where the project will be created, &lt;strong&gt;UI Framework&lt;/strong&gt; as your front-end, &lt;strong&gt;mobile&lt;/strong&gt; option (React Native or MAUI), &lt;strong&gt;database provider&lt;/strong&gt;, &lt;strong&gt;database management system&lt;/strong&gt; (if the database provider is Entity Framework Core), &lt;strong&gt;connection string&lt;/strong&gt;, &lt;strong&gt;theme&lt;/strong&gt; and more...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/create-solution" rel="noopener noreferrer"&gt;See the documentation&lt;/a&gt; for all other options and their descriptions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Generating CRUD Pages
&lt;/h3&gt;

&lt;p&gt;When you add an existing project or create a new one, the project will be listed in the "Open Recent" section. Then, you can select the project, and start generating CRUD pages.&lt;/p&gt;

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

&lt;p&gt;ABP Suite provides the following features for code generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/creating-many-to-many-relationship" rel="noopener noreferrer"&gt;Creating One-To-Many &amp;amp; Many-To-Many Relationships&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/generating-entities-from-an-existing-database-table" rel="noopener noreferrer"&gt;Generating CRUD Pages From an Existing Database Table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/customizing-the-generated-code" rel="noopener noreferrer"&gt;Customizable Code Support (allows you to customize the generated code to your needs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/creating-master-detail-relationship" rel="noopener noreferrer"&gt;Creating Master-Detail (Master/Child) Relationship&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's deep-dive into these features and see them one by one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating One-to-Many &amp;amp; Many-to-Many Relationship
&lt;/h4&gt;

&lt;p&gt;ABP Suite allows you to create one-to-many and many-to-many relationships. You only need to click the &lt;strong&gt;Navigations&lt;/strong&gt; tab and click either the &lt;strong&gt;Add navigation connection (n-n)&lt;/strong&gt; button to establish a many-to-many relationship or the &lt;strong&gt;Add navigation property (1-n)&lt;/strong&gt; button to establish a one-to-many relationship.&lt;/p&gt;

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

&lt;p&gt;When you establish a many-to-many relationship, ABP Suite will do the rest for you and generate a join entity (also join table in the database) and all of the required codes by following DDD principles.&lt;/p&gt;

&lt;p&gt;For example, if you establish a many-to-many relationship between the &lt;strong&gt;Book&lt;/strong&gt; and &lt;strong&gt;Category&lt;/strong&gt; entities, then ABP Suite creates the related join table named &lt;strong&gt;"BookCategories"&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;Also, if you selected the &lt;strong&gt;Create User Interface&lt;/strong&gt; option, it creates the related web page for you as in the following figure:&lt;/p&gt;

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

&lt;p&gt;It's the same if you establish a one-to-many relationship. ABP Suite adds the related navigation property to the related entity and generates all related codes for you, including &lt;strong&gt;export to excel&lt;/strong&gt; support, &lt;strong&gt;customizable code support&lt;/strong&gt;, &lt;strong&gt;web pages&lt;/strong&gt;, &lt;strong&gt;unit tests&lt;/strong&gt;, &lt;strong&gt;advanced filters&lt;/strong&gt;, and more according to the enabled options...&lt;/p&gt;

&lt;h4&gt;
  
  
  Generating CRUD Pages From an Existing Database Table
&lt;/h4&gt;

&lt;p&gt;If you have an existing database table, you can generate the entities using ABP Suite and create a CRUD page based on those entities.&lt;/p&gt;

&lt;p&gt;You first need to create an application from ABP Suite or add an existing project. Then, after opening the project in &lt;a href="https://commercial.abp.io/tools/suite" rel="noopener noreferrer"&gt;ABP Suite&lt;/a&gt;, you can scroll down to the bottom and click the &lt;strong&gt;Load Entity From Database&lt;/strong&gt; button:&lt;/p&gt;

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

&lt;p&gt;A modal will be opened, and then you can specify the connection string for your database, test the connection, and click the &lt;strong&gt;Connect&lt;/strong&gt; button to allow ABP Suite to list all tables for you:&lt;/p&gt;

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

&lt;p&gt;Then, you can uncheck the related properties such as &lt;strong&gt;Id&lt;/strong&gt;, &lt;strong&gt;ConcurrencyStamp&lt;/strong&gt; and others, you can click the &lt;strong&gt;"Ok"&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;After that, ABP Suite will auto-fill all related entity metadata for you in the UI, and then you can revise them and by clicking the &lt;strong&gt;"Save and generate"&lt;/strong&gt; button, you can generate the related entity within seconds.&lt;/p&gt;

&lt;p&gt;Here is the video that summarizes the process:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RMphf3kozy8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Customizable Code Support
&lt;/h4&gt;

&lt;p&gt;ABP Suite allows you to customize the generated code blocks and preserve your custom code changes in the next CRUD Page Generation. It specifies hook points to allow adding custom code blocks. Then, the code written by you to these hook points will be respected and will not be overridden in the next CRUD Page Generation.&lt;/p&gt;

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

&lt;p&gt;To enable custom code support, you should check the Customizable code option in the CRUD Page Generation page (it's selected by default). When you enable the custom code support, you will see some hook points in your application on both the backend and UI side.&lt;/p&gt;

&lt;p&gt;On the C# side (backend), you'll be seeing some abstract classes and classes that derive from them (for entities, application services, interfaces, domain services, and so on ...) according to the template that you have created.&lt;/p&gt;

&lt;p&gt;You can write your custom code in those classes (with the &lt;code&gt;*.Extended.cs&lt;/code&gt; extension) and next time when you need to re-generate the entity, your custom code will not be overridden (only the base abstract classes will be re-generated and your changes on Suite will be respected).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtyvc71uroeowdmc6fqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtyvc71uroeowdmc6fqe.png" alt="ABP Suite - Backend - C# Side Customizations" width="343" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me share an example from the &lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/customizing-the-generated-code" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;IBookRepository.Extended.cs&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;EfCoreBookRepository.Extended.cs&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuknbh7thozk83l1hgsvr.png" width="594" height="230"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w1dgjkeckxs53sisjam.png" alt="Image description" width="778" height="468"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;defining a new method in the &lt;code&gt;IBookRepository&lt;/code&gt; interface&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;implementing the method in the &lt;code&gt;EfCoreBookRepository&lt;/code&gt; class&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As can be seen in the example above, if you write your custom code in the &lt;code&gt;*.Extended.cs&lt;/code&gt; files, then in the next generation, all of the code written by you, will be preserved and not overridden. Thus, you have an option to customize the generated code, by overriding the related methods and also you can extend the generated codes according to your needs.&lt;/p&gt;

&lt;p&gt;For the UI side, ABP Suite provides convenient comment placeholders within pages for MVC, Blazor, and Angular UIs. &lt;/p&gt;

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

&lt;p&gt;For example, in the screenshot above, you can see the example placeholders (hook-points) for MVC/Razor Pages, where you can write your custom code and make customizations on the page. &lt;/p&gt;

&lt;p&gt;In addition to these hook points, you can also add new custom hook points and change their places. You can check &lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/customizing-the-generated-code#adding-new-custom-hook-points-changing-their-places" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt; to see how it can be done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For all of the customization tips and directives, please refer to the documentation: &lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/customizing-the-generated-code" rel="noopener noreferrer"&gt;https://docs.abp.io/en/commercial/latest/abp-suite/customizing-the-generated-code&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Creating Master-Detail (Master/Child) Relationship
&lt;/h4&gt;

&lt;p&gt;ABP Suite allows you to create a master-detail relationship with a few clicks. It generates the necessary code for the master and detail tables, including the foreign key relationship between the two tables.&lt;/p&gt;

&lt;p&gt;To establish a master-detail relationship, you can apply the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating the master entity&lt;/li&gt;
&lt;li&gt;Creating the child entity and associating it with a master entity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, let's assume you want to establish a master/child relationship between the &lt;strong&gt;Order&lt;/strong&gt; and &lt;strong&gt;OrderDetail&lt;/strong&gt; entities, in that case, you first need to create the &lt;strong&gt;Order&lt;/strong&gt; entity and then you can create the &lt;strong&gt;OrderDetail&lt;/strong&gt; entity and specify its entity type as &lt;strong&gt;Child&lt;/strong&gt; like in the following screenshot:&lt;/p&gt;

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

&lt;p&gt;Then, after the code generation, if you run the application, you will see an output as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fsuoskzqy9vne4usp33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fsuoskzqy9vne4usp33.png" alt="ABP Suite - Generating Master/Child Relationship - Order/OrderDetail" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, you have a parent data grid and child data-grids for each order record. You can create your orders from this page and add order lines for each order. So, you can manage all of the orders and order lines from a central point and filter them, or export all of the orders to an Excel file and more...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want a quick overview of these features (customizable code support, establishing one-to-many and many-to-many relationships, creating master/detail relationships, and more), you can check the &lt;a href="https://www.youtube.com/watch?v=t0aWk5HMoDs" rel="noopener noreferrer"&gt;ABP Suite Webinar 2024 Demo video from YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are looking for complete instructions and descriptions for ABP Suite, you can check the &lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/index" rel="noopener noreferrer"&gt;ABP Suite docs&lt;/a&gt; and webinars organized by the core ABP Framework team: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=RFArBh60RSA" rel="noopener noreferrer"&gt;"Take a closer look at the code generation: ABP Suite"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=0CJxCfhAyiA" rel="noopener noreferrer"&gt;"Explore the potential of code generation: ABP Suite"&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Other Features
&lt;/h3&gt;

&lt;p&gt;ABP Suite's features are not limited to the features described in the previous sections. It provides so many features, that it makes it impossible to mention all of them, but here are some of the other features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/updating-packages" rel="noopener noreferrer"&gt;Updating NPM &amp;amp; NuGet Packages Automatically&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/editing-templates" rel="noopener noreferrer"&gt;Editing / Customizing Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io/en/commercial/latest/abp-suite/source-code" rel="noopener noreferrer"&gt;Accessing the Source Code Of Modules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;In conclusion, ABP Suite is a game-changer in the realm of application development. Its modular design (comes from &lt;a href="https://abp.io/" rel="noopener noreferrer"&gt;ABP Framework&lt;/a&gt;), powerful code generation, and support for modern frontend frameworks make it a go-to choice for developers looking to build scalable and feature-rich applications. Whether you are working on a small project or a large enterprise application, ABP Suite provides the tools you need to succeed in your development endeavors.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>codegeneration</category>
      <category>csharp</category>
      <category>abpsuite</category>
    </item>
    <item>
      <title>Usage of Consul in .NET Core - Configuration Management</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Sat, 30 Oct 2021 16:11:31 +0000</pubDate>
      <link>https://forem.com/engincanv/usage-of-consul-in-net-core-configuration-management-39h5</link>
      <guid>https://forem.com/engincanv/usage-of-consul-in-net-core-configuration-management-39h5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Hello everyone,&lt;/p&gt;

&lt;p&gt;In this article, I would like to talk about the &lt;strong&gt;Consul&lt;/strong&gt; tool, which enables us to change/manage our settings on the .NET Core side from a central point, via the UI. Firstly, let's start by talking about what &lt;strong&gt;Consul&lt;/strong&gt; is and what advantages does it provide for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Consul?
&lt;/h2&gt;

&lt;p&gt;Before talking about what &lt;strong&gt;Consul&lt;/strong&gt; is and the advantages/conveniences it provides for us, in an application; Let's review the resources where we can keep our application-based settings. &lt;/p&gt;

&lt;p&gt;Let's have a look at where we can store/keep application-based settings: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1-)&lt;/strong&gt; In application settings (appsettings.json, appsettings.development.json, environment variables etc.),&lt;br&gt;
&lt;strong&gt;2-)&lt;/strong&gt; In the database,&lt;br&gt;
&lt;strong&gt;3-)&lt;/strong&gt; In a key-value store (Consul comes into play here) &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keeping our relevant data in a file like &lt;strong&gt;appsettings.json&lt;/strong&gt; will not cause any performance disadvantage for us. By simply storing the relevant settings as key-values in the relevant file, we can then perform the operations on the basis of the relevant settings. However, if this is the case, we will not be able to simply manage these settings via the UI. In addition, each developer will have to commit this in the relevant setting change. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When we look at the database option, there is a loss of performance since the relevant setting will be fetched from the database each time and an action will be taken accordingly. Of course, at this point, this performance problem can be improved by using the &lt;strong&gt;cache mechanism&lt;/strong&gt;, but we can say that the effort we spend will increase because if a setting is changed, the &lt;strong&gt;cache invalidation&lt;/strong&gt; situation will come into play.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If we consider the option of keeping our settings in a &lt;strong&gt;key-value store&lt;/strong&gt;, which is another method, we can use the pub/sub pattern to load the relevant changes in the application runtime, and in this way, we can dynamically access the relevant settings for each change without having to deal with &lt;strong&gt;cache invalidation&lt;/strong&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we evaluate our options here, we can see that keeping our settings in the key-value store makes our work easier and allows us to provide a centralized settings management. At this point, we can use Consul as the key-value store option. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Consul&lt;/strong&gt; acts as a &lt;strong&gt;distributed key-value store&lt;/strong&gt; for us, a choice where we can keep our application settings in a central place and use them easily within the application. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Usage of Consul in .NET Core Application
&lt;/h2&gt;

&lt;p&gt;After talking about what Consul is and what advantages it provides for us, we can now create a simple .NET Core Web API project and move on to its use. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can find the source code of the application at &lt;a href="https://github.com/EngincanV/Asp.Net-Core-Consul-Demo" rel="noopener noreferrer"&gt;https://github.com/EngincanV/Asp.Net-Core-Consul-Demo&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Firstly, let's run our Consul application on Docker. We can use the following command for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8500:8500 &lt;span class="nt"&gt;-p&lt;/span&gt; 8600:8600/udp &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-consul consul agent &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-ui&lt;/span&gt; &lt;span class="nt"&gt;-node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;server-1 &lt;span class="nt"&gt;-bootstrap-expect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;-client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this command, if you navigate to &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt;, you should see an image similar to the photo below. &lt;/p&gt;

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

&lt;p&gt;Now that we are running our Consul application via Docker, we can create a simple Web API project. If you want to create it via CLI, you can create a Web API project with the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new webapi &lt;span class="nt"&gt;--name&lt;/span&gt; Consul.Demo &lt;span class="nt"&gt;--output&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating our application, we need to install the Consul package on our application via Nuget. You can add the relevant package to the Web API project with the following command via the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Consul &lt;span class="nt"&gt;--version&lt;/span&gt; 1.6.10.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding the relevant package, we now need to make the necessary configurations so that we can use the settings we defined on Consul (which we will define in the next step) in our application.&lt;/p&gt;

&lt;p&gt;Firstly, let's create a folder named &lt;strong&gt;Extensions&lt;/strong&gt; and add the following codes by creating a class named &lt;strong&gt;ServiceCollectionExtensions.cs&lt;/strong&gt; in this folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Extensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceCollectionExtensions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="nf"&gt;AddConsulConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;configKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConsulClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;consul&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConsulClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consulConfig&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;consulConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}));&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, &lt;strong&gt;Startup.cs&lt;/strong&gt; or &lt;strong&gt;Program.cs&lt;/strong&gt; (Since I created the project with .NET 6, there is no &lt;strong&gt;Startup.cs&lt;/strong&gt; class and all settings are provided through this class, if you have created a .NET 6 project, you can add the following codes to this class), let's open our file and provide the relevant configurations.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;consulHost&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:8500"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddConsulConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;consulHost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;After completing the relevant configuration, let's create a simple provider class and write a method that returns the type of value we want according to the key value we passed to the method. For this, let's create a folder named &lt;strong&gt;Helpers&lt;/strong&gt;, add a class called &lt;strong&gt;ConsulKeyValueProvider&lt;/strong&gt; and fill this class with the following codes.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Helpers&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsulKeyValueProvider&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetValueAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConsulClient&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;getPair&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getPair&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getPair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getPair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Here, thanks to the &lt;strong&gt;ConsulClient&lt;/strong&gt; class, we have created a simple method by communicating with the &lt;strong&gt;Consul&lt;/strong&gt; (working at &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt;) where we can get the value we want according to the key. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's navigate to &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt;, select the &lt;strong&gt;Key/Value&lt;/strong&gt; section, and then create a key-value from there and test our method. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here I created a key named &lt;strong&gt;ConsulDemoKey&lt;/strong&gt; and assigned several values (IsEnabled, ShowMessage and Message) as in the photo above. Now, in my .NET Core application, I need to be able to reach these values by giving the &lt;strong&gt;ConsulDemoKey&lt;/strong&gt; key to the helper method that we've created in the previous step. To test this in practice, let's open the &lt;strong&gt;WeatherForecastController&lt;/strong&gt; class under the &lt;strong&gt;Controllers&lt;/strong&gt; folder and update the &lt;strong&gt;Get&lt;/strong&gt; method as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Helpers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherForecastController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GetWeatherForecast"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;consulDemoKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ConsulKeyValueProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValueAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConsulDemoKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ConsulDemoKey"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consulDemoKey&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;consulDemoKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consulDemoKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ConsulDemoKey is null"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If we examine the code here, we can see that there is a class named &lt;strong&gt;ConsulDemoKey&lt;/strong&gt;. While assigning a value in terms of key-value on the Consul, we can create a class to match (map) the relevant values, since we define value as json. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can define the &lt;strong&gt;ConsulDemoKey&lt;/strong&gt; class under the &lt;strong&gt;Models&lt;/strong&gt; folder as follows.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Consul.Demo.Models&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsulDemoKey&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsEnabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ShowMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have completed everything we want to do, we can now run the application and see the result. &lt;/p&gt;

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

&lt;p&gt;When we run our application and send a request to the &lt;strong&gt;/WeatherForecast&lt;/strong&gt; route, the relevant values are read from &lt;strong&gt;Consul&lt;/strong&gt; and printed on the screen as we expected.&lt;/p&gt;

&lt;p&gt;Here, without closing our application, if we change a relevant value via &lt;strong&gt;Consul&lt;/strong&gt; (For example: We can change the Message to "Dynamic Configuration with Consul"), we can see that the relevant change is reflected on the screen dynamically. &lt;/p&gt;

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




&lt;p&gt;Thank you for reading my article. See you in my next post. &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>dotnet</category>
      <category>consul</category>
      <category>csharp</category>
      <category>configuration</category>
    </item>
    <item>
      <title>What are OAuth 2.0 and OIDC (OpenID Connect)? Step By Step Authorization Code Flow With Endpoints</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Sat, 11 Sep 2021 14:20:24 +0000</pubDate>
      <link>https://forem.com/engincanv/what-are-oauth-2-0-and-oidc-openid-connect-with-endpoints-25kd</link>
      <guid>https://forem.com/engincanv/what-are-oauth-2-0-and-oidc-openid-connect-with-endpoints-25kd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Hello everyone,&lt;/p&gt;

&lt;p&gt;In this article, I would like to talk about OAuth 2.0, which is used as a protocol (industry standard) for &lt;strong&gt;Authorization&lt;/strong&gt; and OIDC (OpenID Connect) which is a top layer of the OAuth 2.0 and used for &lt;strong&gt;Authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are OAuth 2.0 and OIDC? What are they used for?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;OAuth 2.0 and OIDC are industry standards used for &lt;strong&gt;Authorization&lt;/strong&gt; and &lt;strong&gt;Authentication&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every day we use mobile or web applications for our works. Defining username and password for each application, getting harder at some point. Some "password management" applications come into place etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  History
&lt;/h3&gt;

&lt;p&gt;In 2005 Brad Fitzpatrick developed an authentication protocol to remove these difficulties.&lt;/p&gt;

&lt;p&gt;The main purpose of this protocol was to enable users to define user credentials to a central system/application (for example, Google) and to enable other applications using this protocol to perform relevant transactions using only the necessary information of the users. And this was done through certificates.&lt;/p&gt;

&lt;p&gt;This developed protocol was not an open protocol, and at the same time, it was making the related transactions through certificates. &lt;strong&gt;OAuth Discussion Group&lt;/strong&gt; which was created in 2007, started to create an open authorization protocol. In December 2007, the OAuth protocol was openly made available as v1.0 and in October 2012 it was finalized as &lt;strong&gt;OAuth 2.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this way, OAuth 2.0 was filled the missing &lt;strong&gt;Authorization&lt;/strong&gt; part of the OpenID protocol with a &lt;strong&gt;token based&lt;/strong&gt; structure. Instead of certificates, identity-related transactions started to do with tokens.&lt;/p&gt;

&lt;p&gt;Then, in 2004 an identity layer called OIDC (OpenID Connect) developed on the OAuth 2.0 Framework was added and thus Authentication processes were defined within a standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth 2.0 Structure
&lt;/h2&gt;

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

&lt;p&gt;In the above picture, you can see the base structure of OAuth 2.0. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the most basic sense, when we as a user (&lt;strong&gt;Resource Owner&lt;/strong&gt;) want to access our own data, we enter the relevant website (&lt;strong&gt;Client&lt;/strong&gt;) and send a request to the relevant URL. The relevant website communicates with the &lt;strong&gt;Authorization Server&lt;/strong&gt; to query whether we have access to that resource. As a result, the relevant server indicates that we have authorization. We reach the information we want to access as a result that returned by the &lt;strong&gt;Resource Server&lt;/strong&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;Resource Owner&lt;/strong&gt;, &lt;strong&gt;Client&lt;/strong&gt;, &lt;strong&gt;Authorization Server&lt;/strong&gt;, and &lt;strong&gt;Resource Server&lt;/strong&gt; are defined as Roles in OAuth 2.0 protocol. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;OAuth 2.0 offers different types of &lt;strong&gt;Authorization Flows&lt;/strong&gt; according to different usage conditions and these flows are named as follows. 

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authorization Code Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implicit Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Owner Password Credential Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client Credential Flow&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Authorization Code Flow is used in &lt;strong&gt;Server Side&lt;/strong&gt; applications and Implicit Flow is used in &lt;strong&gt;Browser Based (SPA's)&lt;/strong&gt; applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In these flows, although the basic logic is the same (authorization, token exchange, etc.), the number of steps and methods applied are different. As an example, let's examine the frequently used &lt;strong&gt;Authorization Code Flow&lt;/strong&gt; together with the related endpoints. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example: OAuth 2.0 - Authorization Code Flow
&lt;/h3&gt;

&lt;p&gt;We can examine this flow in 4 steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1-) Authorization Request&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If we examine the related request, we can see that we passed a query parameter named &lt;strong&gt;"client_id"&lt;/strong&gt;. This "client_id" represents our application defined within the OAuth 2.0 protocol. In other word, it's a value that identifies the relevant application. The &lt;strong&gt;"scope"&lt;/strong&gt; parameter specifies the scope of the relevant authorization. In other words, the user is only allowed to see the relevant information in the &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;profile&lt;/code&gt; scope above. User can't access any other part of the application (e.g. delete contact) by using the generated token. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Redirect_uri" represents the URL where the application we are using will get and use the relevant token.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The most important parameter here, the &lt;strong&gt;"response_type"&lt;/strong&gt; parameter, shows which flow the request will be made with. (&lt;strong&gt;“code”&lt;/strong&gt; for Authorization Code Flow) &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2-) Authorization Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the &lt;strong&gt;Authorization Server&lt;/strong&gt; ensure that the relevant request is valid and in the correct format as well, it sends a GET request with the &lt;strong&gt;Authorization Code&lt;/strong&gt; to the relevant callback url (redirect_uri) specified in the request.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;3-) Token Request&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After obtaining the &lt;strong&gt;Authorization Code&lt;/strong&gt;, a token request is made by using the relevant Authorization Code to obtain a &lt;strong&gt;Access Token&lt;/strong&gt;. (Token exchange =&amp;gt; Authorization Code ↔ Access Token)&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;As can be seen in this request, &lt;strong&gt;Authorization Code&lt;/strong&gt; is specified as the &lt;code&gt;grant_type&lt;/code&gt; and if this request is successful Authorization Server redirects us to the route specified in the "redirect_uri" parameter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4-) Token Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the "Token Request" we made in the third step is successful, a similar response returns as below and can be used by the &lt;strong&gt;Client&lt;/strong&gt; (the application we want to use).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now, with this "access_token" the user can access its own data from &lt;strong&gt;Resource Server&lt;/strong&gt; through the &lt;strong&gt;Client&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here, if the "refresh_token" is also returned as a result of the request, when the "access_token" expires, a request can be made to renew the related "access_token" with this token. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OIDC (OpenID Connect) Structure
&lt;/h2&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Simple &lt;strong&gt;identity layer&lt;/strong&gt; on top of the OAuth 2.0 protocol.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OpenID Connect can be thought of as an &lt;strong&gt;identity layer&lt;/strong&gt; added on top of the OAuth 2.0 protocol to enable the OAuth 2.0 protocol to be used for &lt;strong&gt;Authentication&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;OpenID Connect contains a meta-data document (&lt;strong&gt;.well-known/openid-configuration&lt;/strong&gt;) that defines the information required to login through an application. (Which urls should be used, which scopes it contains, etc.) &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to view the relevant metadata document as an example, you can access the Microsoft's OIDC metadata document by navigating to &lt;a href="https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration" rel="noopener noreferrer"&gt;https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As in OAuth 2.0, transactions are performed using flows in OIDC. As an example, let's examine the endpoints of &lt;strong&gt;Authorization Code Flow&lt;/strong&gt; for OIDC as in OAuth 2.0. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example: OAuth 2.0 - Authorization Code Flow
&lt;/h3&gt;

&lt;p&gt;We can examine this flow in 6 steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1-) Authentication Request&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;If we examine the related endpoint, we can see that a value called &lt;strong&gt;openid&lt;/strong&gt; is passed in the scope section. We can actually think of this as the equivalent of the identity layer concept we used when defining OIDC. With this scope added to the OAuth 2.0 protocol, Authorization Server now handles the relevant request within the scope of OIDC. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2-) Authentication Response&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Here, as in OAuth 2.0, a &lt;strong&gt;GET&lt;/strong&gt; request is sent to the callback-url (redirect-uri) with the &lt;code&gt;Authorization Code&lt;/code&gt;. In this way, the Client becomes aware of the relevant authorization code. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3-) Token Request&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Then, the relevant Client requests a token from the Authorization Server with the "authorization_code" it has obtained. (Token exchange =&amp;gt; Authorization Code ↔ Access Token)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4-) Token Response&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;If we examine the response we can see the "id_token" section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID_Token&lt;/strong&gt;: It can be thought of as an identity card. Contains information about the end user. It is in JWT format. It can be thought of as the add-on that OIDC brings to OAuth 2.0. In this way, the Authentication process can happen. &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;5-) UserInfo Request - Obtaining End User's Information&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The UserInfo endpoint returns information about the logged in user (name, surname, etc.). When the client needs the information of the relevant user, he can obtain the necessary information by using this endpoint. (Note that the relevant user is now authenticated and a request is made to the endpoint using the Bearer Authorization.) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6-) UserInfo Response&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the request that we made in the previous step is successful, the user's information receives. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As another method, the user's relevant information can be accessed by decoding the previously generated "Id_Token" value. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading this article, I hope you've enjoyed it. See you in the next article...&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>oauth2</category>
      <category>authorization</category>
      <category>authentication</category>
      <category>identity</category>
    </item>
    <item>
      <title>Asp.Net Core - Deferred Option Pattern</title>
      <dc:creator>Engincan VESKE</dc:creator>
      <pubDate>Tue, 07 Sep 2021 16:57:18 +0000</pubDate>
      <link>https://forem.com/engincanv/asp-net-core-usage-of-options-pattern-as-defered-36f8</link>
      <guid>https://forem.com/engincanv/asp-net-core-usage-of-options-pattern-as-defered-36f8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Hello to everyone,&lt;br&gt;
In this article, I will try to explain how we can define the settings in our applications in a deferred way by using the &lt;code&gt;IConfigureOptions&amp;lt;TOptions&amp;gt;&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;In an application I am developing, I decided to add the &lt;strong&gt;sitemap.xml&lt;/strong&gt; file, thinking that adding the &lt;strong&gt;sitemap.xml&lt;/strong&gt; file would give good results so that the links of the relevant website would be better indexed and produce more efficient SEO scores. Later on, while I was browsing whether there were packages that I could easily configure the &lt;strong&gt;sitemap.xml&lt;/strong&gt; and &lt;strong&gt;robots.txt&lt;/strong&gt; files to add to the application, I came across a package named &lt;a href="https://github.com/wintoncode/Winton.AspNetCore.Seo" rel="noopener noreferrer"&gt;Winton.AspNetCore.Seo&lt;/a&gt;. Thanks to this package, I was able to add the &lt;strong&gt;sitemap.xml&lt;/strong&gt; file to my application simply by specifying the relevant urls. &lt;/p&gt;

&lt;p&gt;Then, by examining the documents of the relevant package, I could simply view the URLs of the relevant website statically on the &lt;strong&gt;/sitemap.xml&lt;/strong&gt; route. While performing this operation, &lt;/p&gt;

&lt;p&gt;I created a simple web application and then I started to make the necessary settings by installing the &lt;a href="https://github.com/wintoncode/Winton.AspNetCore.Seo" rel="noopener noreferrer"&gt;Winton.AspNetCore.Seo&lt;/a&gt; package on the relevant web project. First, I defined the &lt;strong&gt;appsettings.json&lt;/strong&gt; file with the links I wanted to appear on the /sitemap.xml route. Likewise, it is possible to make settings in the robots.txt file too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Seo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sitemap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Urls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"RelativeUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/article/what-is-new-in-csharp"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"RelativeUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/article/dotnet-5"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"RobotsTxt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AddSitemapUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then I updated the &lt;code&gt;ConfigureServices&lt;/code&gt; method in the &lt;strong&gt;Startup.cs&lt;/strong&gt; file as follows. In this way, I applied the process of defining the dependencies (dependency registration) required to be able to use the relevant package.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeoWithDefaultRobots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SitemapUrlOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SitemapUrlOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RelativeUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/article/what-is-new-in-csharp"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SitemapUrlOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RelativeUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/article/dotnet-5"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Later, when I ran the application and navigates to the &lt;strong&gt;/sitemap.xml&lt;/strong&gt; and &lt;strong&gt;/robots.txt&lt;/strong&gt; routes, I saw that the relevant links were listed as defined in &lt;strong&gt;appsettings.json&lt;/strong&gt;. &lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;After performing these operations with a simple configuration, I can address the scenario I want to realize.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What I want to achieve at this point is to get the relevant links by calling the service that returns the relevant links in my application, and to list the relevant links in the /sitemap.xml file thanks to the "Option Pattern" so that search engines can better index the relevant links. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The first thing that comes to my mind is: I need to call the relevant service in the &lt;code&gt;ConfigureServices&lt;/code&gt; method, access the &lt;code&gt;IServiceProvider&lt;/code&gt; somehow, and call the methods of that service by resolving the relevant service (to use the services), and finally (&lt;code&gt;services.AddSeoWithDefaultRobots(options =&amp;gt; { … })&lt;/code&gt;) I needed to specify these links as options where I defined the method.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;

        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeoWithDefaultRobots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
            &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

                &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateScope&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyArticleService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlogPostLinksAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In this case, when we examine the related method, we can see that the &lt;code&gt;ConfigureServices&lt;/code&gt; method, whose main purpose is to register the related dependencies to the container, is out of its definition, and also that unnecessary code crowds are formed. At this point, I came to the idea that I should find another solution and perform the relevant Options after Dependency Registration processes are finished (in other words, delayed configuration (referred configuration)). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Later, while researching on this subject, I came across an &lt;a href="https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/" rel="noopener noreferrer"&gt;article&lt;/a&gt; from &lt;strong&gt;Andrew Lock&lt;/strong&gt;. When I examined the article in detail, I saw that we encountered the same situation (we came to the same conclusion) even if the scenario we wanted to realize was different, and I encountered the &lt;code&gt;IConfigureOptions&lt;/code&gt; interface. When I examined this interface, I saw that it provided the situation I wanted to realize, that is, by injecting the services I wanted, I could simply define the results I obtained for the Options I specified. And I started to the related refactoring business. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For this, I first defined a class called &lt;strong&gt;SeoConfiguration&lt;/strong&gt; and implemented the &lt;code&gt;IConfigureOptions&amp;lt;SeoOptions&amp;gt;&lt;/code&gt; interface as follows.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SeoConfiguration&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IConfigureOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SeoOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IArticleAppService&lt;/span&gt; &lt;span class="n"&gt;_articleAppService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SeoConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IArticleAppService&lt;/span&gt; &lt;span class="n"&gt;articleAppService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_articleAppService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;articleAppService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SeoOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;articleUrls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_articleAppService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetArticleLinksAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;articleUrls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SitemapUrlOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="n"&gt;RelativeUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;//ilgili değerlerin SeoOptions sınıfına aktarılması&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sitemap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RobotsTxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSitemapUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you pay attention here, we do not deal with the &lt;code&gt;IServiceProvider&lt;/code&gt; interface and resolve the related dependencies. We simply do Constructor Injection and the framework takes care of the rest. When we examine the &lt;strong&gt;Configure&lt;/strong&gt; method, we see that it takes the relevant option &lt;strong&gt;(SeoOptions)&lt;/strong&gt; as a parameter. In this method, we received the relevant urls from our service and assign a value to this option. In this way, we configure the relevant settings. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last thing we need to do is to inform our application that the value of an Options as deferred configuration of the &lt;strong&gt;SeoConfiguration&lt;/strong&gt; class we have created should be done after the relevant dependency registration processes. For this, we can update our &lt;strong&gt;Startup.cs&lt;/strong&gt; file as follows.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConfigureOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SeoOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;SeoConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;           
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeoWithDefaultRobots&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here, we are saying, &lt;strong&gt;"Defer the relevant configurations until the SeoOptions class needs to be used, and perform the relevant configurations according to the operations performed in the Configure method on the class I have defined (here SeoConfiguration) when it should be used"&lt;/strong&gt;.
Now we can run our application and see the result by navigating to the &lt;strong&gt;/sitemap.xml&lt;/strong&gt; and &lt;strong&gt;/robots.txt&lt;/strong&gt; routes. &lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;As you can see, we are now freed from a great burden by dynamically defining the relevant urls. In addition, thanks to the &lt;code&gt;IConfigureOptions&lt;/code&gt; interface, we get rid of the code snippets that we need to define in the &lt;strong&gt;ConfigureServices&lt;/strong&gt; method (first solution). &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading this article, I hope you've enjoyed it. See you in the next article...&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.&lt;br&gt;
👉 Subscribe here → &lt;a href="https://engincanveske.substack.com/" rel="noopener noreferrer"&gt;engincanveske.substack.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/" rel="noopener noreferrer"&gt;https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://benjamincollins.com/blog/using-dependency-injection-while-configuring-services/" rel="noopener noreferrer"&gt;https://benjamincollins.com/blog/using-dependency-injection-while-configuring-services/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>aspnetcore</category>
      <category>optionpattern</category>
    </item>
  </channel>
</rss>
