<?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: Ben Witt</title>
    <description>The latest articles on Forem by Ben Witt (@ben-witt).</description>
    <link>https://forem.com/ben-witt</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%2F1456445%2F4ae90e8b-6b3e-48e7-be98-2d43dacbd68d.jpeg</url>
      <title>Forem: Ben Witt</title>
      <link>https://forem.com/ben-witt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ben-witt"/>
    <language>en</language>
    <item>
      <title>Transitive Dependency – Challenges and Approaches in .NET</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Wed, 12 Feb 2025 09:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/transitive-dependency-challenges-and-approaches-in-net-20a6</link>
      <guid>https://forem.com/ben-witt/transitive-dependency-challenges-and-approaches-in-net-20a6</guid>
      <description>&lt;p&gt;Imagine you are developing a .NET application that runs perfectly at first. Suddenly, however, unexpected runtime errors occur—even though your main project does not explicitly include any additional packages. How can this phenomenon be explained? Often, the cause is a transitive dependency: a library such as Entity Framework Core is silently included in your project via another NuGet package. In complex application scenarios, this issue can lead to version conflicts or even hard-to-diagnose runtime errors like the &lt;code&gt;MissingMethodException&lt;/code&gt;. Without targeted management of these dependencies, the effort required for error resolution can become significant.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Basics and Problem Statement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Transitive References vs. Transitive Dependencies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transitive References:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In a multi-layered solution structure, for example, Project A directly references Project B, which in turn uses Project C. In this way, Project A has an indirect (transitive) reference to Project C—even if it is not explicitly listed in the &lt;code&gt;.csproj&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transitive Dependencies:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With external NuGet packages, the problem appears when they are brought in by a referenced package and do not show up in the main project’s &lt;code&gt;.csproj&lt;/code&gt; file. For instance, if Project A uses a package (e.g., &lt;code&gt;PackageX&lt;/code&gt;) that internally references EF Core in a specific version, then EF Core is included transitively—even though Project A never explicitly added it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Isn’t it remarkable how quickly such subtle dependency chains can emerge in large projects without being noticed?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 A Typical Scenario in .NET
&lt;/h3&gt;

&lt;p&gt;In many .NET applications, Entity Framework Core (EF Core) is used as the data access library. Even if the main project does not directly reference EF Core, it can be silently included as a transitive dependency via another utility or data access package. Initially, everything seems to work smoothly—EF Core provides a stable API. However, when updating the .NET framework or integrating new features, version conflicts may occur that eventually lead to hard-to-diagnose runtime errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Detailed Solution Approaches
&lt;/h2&gt;

&lt;p&gt;To prevent the unwanted propagation of packages, two main approaches are available: the targeted restriction of individual packages using &lt;code&gt;PrivateAssets="all"&lt;/code&gt; or the complete deactivation of transitive project references with &lt;code&gt;&amp;lt;DisableTransitiveProjectReferences&amp;gt;true&amp;lt;/DisableTransitiveProjectReferences&amp;gt;&lt;/code&gt;. Both approaches are critically examined below.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 &lt;code&gt;PrivateAssets="all"&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By adding the attribute &lt;code&gt;PrivateAssets="all"&lt;/code&gt; in the PackageReference, you prevent the respective library from being passed on to other projects in the dependency hierarchy. This way, the package—and its transitive dependencies—remain available only within the current project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Granularity:&lt;/strong&gt; You decide on a package-by-package basis whether a dependency should be inherited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoidance of Side Effects:&lt;/strong&gt; Problematic libraries are not automatically transferred to subordinate projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increased Configuration Effort:&lt;/strong&gt; If another project needs the same library, it must be explicitly referenced again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"SomeDataAccessPackage"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.2.3"&lt;/span&gt; &lt;span class="na"&gt;PrivateAssets=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Could it be that in complex environments the administrative effort for such package-specific configuration outweighs the benefits?&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 DisableTransitiveProjectReferences
&lt;/h3&gt;

&lt;p&gt;With the global setting true in the .csproj file, you prevent the automatic propagation of all transitive project references. Every project must then explicitly list all the required packages.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;br&gt;
    • Increased Transparency: All dependencies are explicitly visible and controllable.&lt;br&gt;
    • Prevention of Unintended Inclusions: No “hidden” libraries are automatically added to the project.&lt;/p&gt;

&lt;p&gt;Disadvantages:&lt;br&gt;
    • Higher Maintenance Effort: Explicitly listing all dependencies can create a significant administrative overhead, especially in larger projects.&lt;br&gt;
    • Risk of Omissions: Essential dependencies might accidentally be overlooked, leading to compile-time or runtime errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;DisableTransitiveProjectReferences&amp;gt;true&amp;lt;/DisableTransitiveProjectReferences&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Given the additional effort, is it really practical to explicitly declare all dependencies?&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Comparison of Both Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;PrivateAssets="all"&lt;/th&gt;
&lt;th&gt;DisableTransitiveProjectReferences&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Granularity&lt;/td&gt;
&lt;td&gt;Package-specific control&lt;/td&gt;
&lt;td&gt;Global deactivation – all dependencies must be explicitly referenced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance Effort&lt;/td&gt;
&lt;td&gt;Requires individual adjustments per project&lt;/td&gt;
&lt;td&gt;Higher maintenance effort due to separate management of all references&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clarity&lt;/td&gt;
&lt;td&gt;Can lead to opaque dependency chains in complex structures&lt;/td&gt;
&lt;td&gt;Clear separation, but increases the risk of overlooking essential packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;High flexibility in isolated control of individual packages&lt;/td&gt;
&lt;td&gt;Strict control, but less adaptability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Practical Example: Abstraction and Interface Encapsulation
&lt;/h2&gt;

&lt;p&gt;Consider a specific application: a .NET Standard library named Common encapsulates various helper methods and tools and internally includes a data access package that brings EF Core as a transitive dependency. In this case, it is advisable not to expose EF Core-specific classes in the public API; instead, these should be abstracted through interfaces.&lt;/p&gt;

&lt;p&gt;Example Implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// In Common
public interface IDataService
{
    IEnumerable&amp;lt;string&amp;gt; GetData();
}

internal class EfDataService : IDataService
{
    public IEnumerable&amp;lt;string&amp;gt; GetData()
    {
        // Internal use of EF Core
        return new List&amp;lt;string&amp;gt; { "Data from EF Core" };
    }
}

public static class DataServiceFactory
{
    public static IDataService CreateService()
    {
        return new EfDataService();
    }
}

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

&lt;/div&gt;



&lt;p&gt;Configuration in Common.csproj:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;ItemGroup&amp;gt;
  &amp;lt;PackageReference Include="SomeDataAccessPackage" Version="1.2.3" PrivateAssets="all" /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;This approach ensures that EF Core remains exclusively available internally—the main application only interacts with the interface without needing to know the underlying implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Advanced Strategies and Alternative Approaches
&lt;/h2&gt;

&lt;p&gt;In addition to the configuration options discussed above, other strategies can help minimize version conflicts and the issues of transitive dependencies:&lt;br&gt;
    • Binding Redirects:&lt;br&gt;
Especially in older .NET Framework applications, binding redirects can help resolve conflicts between different versions of the same library.&lt;br&gt;
    • Modular Architectures:&lt;br&gt;
Separating the application into clearly defined modules allows for targeted isolation of dependencies. This makes it easier to control unwanted side effects.&lt;/p&gt;

&lt;p&gt;Should we not always question whether a focus solely on .csproj configurations in highly complex systems is truly the optimal solution?&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Summary and Outlook
&lt;/h2&gt;

&lt;p&gt;Transitive dependencies represent a serious issue in complex .NET projects. Seemingly insignificant libraries like EF Core can—when included via other NuGet packages—lead to significant version conflicts and runtime errors. Consistent separation of dependencies and the targeted use of measures such as PrivateAssets="all" or  offer practical solutions. It is essential to weigh whether the administrative effort is justified by the benefits and how long-term maintainability can be ensured.&lt;/p&gt;

&lt;p&gt;By using abstractions, explicit references, and additional measures such as binding redirects, developers can not only create a more stable codebase but also reduce ongoing maintenance efforts. Regularly reviewing dependency hierarchies using tools like dotnet list package --include-transitive or NDepend is indispensable.&lt;/p&gt;

&lt;p&gt;Is it not ultimately the task of every developer to continually question whether the current architecture still meets the growing demands of modern applications?&lt;/p&gt;

&lt;p&gt;I think so….!&lt;/p&gt;

</description>
      <category>development</category>
      <category>coding</category>
      <category>csharp</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Efficient Debugging and Precise Logging in C#: Using Caller Attributes</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Wed, 29 Jan 2025 17:01:53 +0000</pubDate>
      <link>https://forem.com/ben-witt/efficient-debugging-and-precise-logging-in-c-using-caller-attributes-3m5f</link>
      <guid>https://forem.com/ben-witt/efficient-debugging-and-precise-logging-in-c-using-caller-attributes-3m5f</guid>
      <description>&lt;p&gt;How often do you wish, while debugging a complex application, to instantly see where in the code an error was triggered? Often, you are left with basic error messages that lack valuable context or exact code lines. This is where the C# attributes [CallerMemberName], [CallerFilePath], and [CallerLineNumber] come into play.&lt;/p&gt;

&lt;p&gt;These attributes automatically provide information about the calling method’s name, the physical file path, and the line number in the source code when a method is called. This enables precise error tracking and logging, which is useful both during debugging and in production logging. Especially when troubleshooting large codebases, they prove to be indispensable tools for quick and targeted analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics of the Attributes
&lt;/h2&gt;

&lt;p&gt;The attributes [CallerMemberName], [CallerFilePath], and [CallerLineNumber] are applied directly to method parameters. The compiler automatically replaces the arguments with the corresponding values from the call context without any extra effort from the developer:&lt;br&gt;
    • [CallerMemberName] provides the name of the calling method.&lt;br&gt;
    • [CallerFilePath] gives the full path to the file where the method call is located.&lt;br&gt;
    • [CallerLineNumber] provides the line number of the call within that file.&lt;/p&gt;

&lt;p&gt;The syntax is straightforward. A classic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void LogInfo(
    string message, 
    [CallerMemberName] string memberName = "",
    [CallerFilePath] string filePath = "", 
    [CallerLineNumber] int lineNumber = 0)
{
    Console.WriteLine($"[{memberName}] {message} (File: {filePath}, Line: {lineNumber})");
}

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

&lt;/div&gt;



&lt;p&gt;Here, the parameters must have default values (e.g., = "" or = 0) so that the compiler can insert the values automatically. When you call the method without specifying these optional parameters, they are substituted automatically.&lt;/p&gt;

&lt;p&gt;Example Method for Capturing Call Stack Information&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public void ProcessData([CallerMemberName] string caller = "",
                        [CallerFilePath] string path = "",
                        [CallerLineNumber] int line = 0)
{
    Console.WriteLine($"Called by: {caller}, File: {path}, Line: {line}");
    // Further data processing code...
}

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

&lt;/div&gt;



&lt;p&gt;In this simple scenario, you can already gain initial insights into the call context and source code position. This meta-information is extremely valuable both for debugging and for later evaluations in operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Examples for Practical Applications
&lt;/h2&gt;

&lt;p&gt;a) &lt;strong&gt;Simple Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you are running a library or an API to manage borrowing processes. Everywhere important actions take place—such as registering a book, registering new users, or handling requests—you want to create log entries. You could use a helper class for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class Logger
{
    public static void LogOperation(
        string operation, 
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
    {
        // For example, log to a file or a database
        Console.WriteLine($"Operation: {operation} in {memberName}, File: {filePath}, Line: {lineNumber}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time you call Logger.LogOperation("BookBorrowed"), the log provides much more information than if you had to add it manually.&lt;/p&gt;

&lt;p&gt;b) &lt;strong&gt;More Complex Example with a Base Class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In larger projects, it is advisable to centralize logging and error handling routines in an abstract base class. The following example shows an abstract BaseRepository that catches errors and automatically uses the attributes for meaningful error messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public abstract class BaseRepository
{
    protected void LogError(Exception ex, 
                            [CallerMemberName] string memberName = "",
                            [CallerFilePath] string filePath = "", 
                            [CallerLineNumber] int lineNumber = 0)
    {
        Console.WriteLine($"Error in {memberName} at {filePath} (Line {lineNumber}): {ex.Message}");
    }
}

public class BookRepository : BaseRepository
{
    public void AddBook(Book book)
    {
        try
        {
            // Simulated database operation
            throw new InvalidOperationException("Database error while adding a book.");
        }
        catch (Exception ex)
        {
            LogError(ex);
            throw;  // Re-throw the error to continue the stack
        }
    }
}

public class LibraryService
{
    private readonly BookRepository _repository = new BookRepository();

    public void Execute()
    {
        try
        {
            _repository.AddBook(new Book { Title = "C# Deep Dive" });
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error in the service layer: " + ex.Message);
            throw;
        }
    }
}

// Application
var service = new LibraryService();
service.Execute();

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

&lt;/div&gt;



&lt;p&gt;This approach allows complete tracking of the call stack. When an error is triggered in BookRepository, the LogError method precisely logs the context: the member name (AddBook), the file path, and the line number. At the same time, the error is re-thrown so that the higher layer (LibraryService) can perform further actions, such as separate logging or sending an alert.&lt;/p&gt;

&lt;p&gt;c) &lt;strong&gt;Tracking a Complete Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In complex multi-layered architectures—consisting of service layers, repository layers, and database connections—it is crucial to trace the source of an error without gaps. The above example demonstrates this flow:&lt;br&gt;
    1.  The service layer calls AddBook in BookRepository.&lt;br&gt;
    2.  The repository layer throws an exception due to a database error, logs details using the caller attributes, and re-throws the error.&lt;br&gt;
    3.  The service layer catches the error and can handle it accordingly.&lt;/p&gt;

&lt;p&gt;With this technique, precise logging of each relevant layer is achieved, ensuring that no information about the actual origin is lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Application with a Library Management System
&lt;/h2&gt;

&lt;p&gt;In library software, where daily borrowings, reservations, returns, or new registrations are logged, the caller attributes are used optimally:&lt;br&gt;
    1.  &lt;strong&gt;Logging API Errors&lt;/strong&gt;: If an HTTP request fails, the log immediately provides information about the error location in the code (method, file, line).&lt;br&gt;
    2.  &lt;strong&gt;Logging Database Operations&lt;/strong&gt;: In dynamic environments, faulty SQL queries or transaction conflicts can quickly lead to confusing errors. Thanks to the caller attributes, it is possible to determine exactly where these conflicts were triggered, whether in the repository or service layer.&lt;/p&gt;

&lt;p&gt;Such practical insights into the system not only shorten debugging times but also improve the overall maintainability of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and Best Practices
&lt;/h2&gt;

&lt;p&gt;Although logging file and line details is very helpful during development, it is important to consider potential drawbacks:&lt;br&gt;
    • &lt;strong&gt;Performance Considerations&lt;/strong&gt;: With frequent log calls, excessive capturing of caller information can quickly bloat logs and impact application performance. Therefore, in production environments, it should be carefully considered where detailed logs are truly necessary.&lt;br&gt;
    • &lt;strong&gt;Security Aspects&lt;/strong&gt;: Revealing file paths may be undesirable in some cases, such as in security-critical applications or externally shared logs.&lt;br&gt;
    • &lt;strong&gt;Reusability&lt;/strong&gt;: To keep maintenance effort low, it is recommended to encapsulate the attributes in base classes or utility methods, as shown in the example (BaseRepository). This avoids redundant code and ensures consistent error messages across the entire project.&lt;/p&gt;

&lt;p&gt;With a sensible logging concept and thoughtful filtering mechanisms, the level of detail can be flexibly controlled depending on the environment (development, staging, production).&lt;/p&gt;

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

&lt;p&gt;The attributes [CallerMemberName], [CallerFilePath], and [CallerLineNumber] offer significant benefits for debugging, logging, and error tracing in C# projects. Their versatile applications—from simple logs in the development environment to complex error tracking in multi-layered architectures—make them essential tools in modern application development.&lt;/p&gt;

&lt;p&gt;So why not start implementing more precise error logging and more efficient debugging in your own projects right away? The implementation is straightforward, while the benefits are enormous—especially when errors in production systems need to be quickly isolated and resolved. With targeted use, thoughtful structuring, and a bit of pragmatism, a new level of transparency in error analysis is achieved, significantly easing the developer’s daily work.&lt;/p&gt;

</description>
      <category>development</category>
      <category>microsoft</category>
      <category>csharp</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Critical Look at Cancellation Management in .NET Applications</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Wed, 29 Jan 2025 17:01:36 +0000</pubDate>
      <link>https://forem.com/ben-witt/a-critical-look-at-cancellation-management-in-net-applications-1kfg</link>
      <guid>https://forem.com/ben-witt/a-critical-look-at-cancellation-management-in-net-applications-1kfg</guid>
      <description>&lt;p&gt;The CancellationToken and the related types provided in .NET offer a central and effective foundation for carefully canceling tasks, threads, or asynchronous processes. However, the question arises whether this concept truly provides sufficient flexibility and stability in all application scenarios. But how often is this feature actually used in practice, and what risks come with improper handling? This article explains how the CancellationToken works and offers concrete recommendations for its efficient use. Additionally, potential pitfalls are highlighted and critically examined.&lt;/p&gt;

&lt;p&gt;Definition and Purpose of CancellationToken&lt;/p&gt;

&lt;p&gt;The CancellationToken is a structure from the Task Parallel Library (TPL) that is used to signal an ongoing operation that a cancellation request has been made. In a software architecture increasingly based on asynchronous and parallel processes, the CancellationToken plays a central role: it allows computationally intensive processes or long-running tasks to be terminated early, conserving resources and ensuring an improved user experience.&lt;/p&gt;

&lt;p&gt;Why is this construct indispensable? In a modern .NET application, numerous operations can run in parallel or asynchronously. Without coordinated cancellation and resource release, there is a risk that certain tasks continue running and block system resources, even though they are no longer needed.&lt;/p&gt;

&lt;p&gt;How It Works and Basic Concepts&lt;/p&gt;

&lt;p&gt;Cancellation control is managed by the CancellationTokenSource class. It acts as the central control unit and provides methods such as Cancel(), which sends a cancellation signal to all linked CancellationTokens.&lt;/p&gt;

&lt;p&gt;Resource Management with IDisposable&lt;/p&gt;

&lt;p&gt;A frequently underestimated aspect is that CancellationTokenSource implements the IDisposable interface. If this is not taken into account, it can easily lead to resource leaks—especially in scenarios where a large number of CancellationTokenSource objects are created in parallel. Why should one neglect implementing clean resource management?&lt;/p&gt;

&lt;p&gt;The recommendation is to always use the using keyword to enforce automatic resource release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using (var cts = new CancellationTokenSource())
{
    // Code to execute the operation
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failing to call the Dispose() method can leave unmanaged resources in memory, ultimately affecting the application’s stability.&lt;/p&gt;

&lt;p&gt;Methods to Cancel an Operation&lt;/p&gt;

&lt;p&gt;There are several approaches to sending a cancellation request to an ongoing operation:&lt;br&gt;
    1.  Synchronous Cancellation&lt;br&gt;
The cancellation request is triggered immediately. This means the current method actively checks the CancellationToken and reacts right away.&lt;br&gt;
    2.  Asynchronous Cancellation&lt;br&gt;
The request is performed in the background without immediately blocking the calling code’s flow.&lt;br&gt;
    3.  Delayed Cancellation&lt;br&gt;
A predefined time span is used after which the operation is automatically terminated. This is especially helpful when a user does not want to wait further, such as in network or I/O-intensive tasks.&lt;/p&gt;

&lt;p&gt;A concrete example of this is setting a timeout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this time span elapses, a cancellation request is triggered, ensuring that an operation does not run beyond the set limits. But is it always sensible to set a rigid timeout without being able to flexibly respond to external events?&lt;/p&gt;

&lt;p&gt;Monitoring Cancellation Requests&lt;/p&gt;

&lt;p&gt;Polling&lt;/p&gt;

&lt;p&gt;A common practice is to regularly check the CancellationToken:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while (!token.IsCancellationRequested)
{
    // Continue the operation
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When such loops are implemented in computationally intensive areas, the frequency of checks can significantly affect performance. The question arises: How often should the check be performed to recognize a cancellation promptly without excessively burdening execution time?&lt;/p&gt;

&lt;p&gt;Alternatively, the ThrowIfCancellationRequested() method can be used, which throws an OperationCanceledException if a cancellation request has been made:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This approach is usually cleaner but can also lead to unexpected side effects if the exception is not caught appropriately.&lt;/p&gt;

&lt;p&gt;Callback Registration&lt;/p&gt;

&lt;p&gt;In scenarios where regular polling is impractical or resource-intensive, a callback can be registered to execute automatically when a cancellation request arrives. For example, if a user is downloading a large file and cancels the process, not only should the download threads stop, but any temporary files created should also be deleted to free up space and avoid inconsistencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token.Register(() =&amp;gt; 
{
    Console.WriteLine("Operation canceled.");
    // Additional cleanup, e.g., deleting temporary files
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method has the advantage of calling cancellation actions specifically without the ongoing operation having to constantly check for the cancellation signal. However, callback registration can lead to messy code flow in complex scenarios when multiple cancellation cases and cleanup actions need to be considered.&lt;/p&gt;

&lt;p&gt;Linking Multiple CancellationTokens&lt;/p&gt;

&lt;p&gt;What happens when an application needs to meet several potential cancellation conditions at the same time? For example, an operation might need to respond to both a user event (like closing a window) and a timeout simultaneously. The CancellationTokenSource class provides the CreateLinkedTokenSource method for this purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token1, token2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The created instance combines multiple CancellationTokens into a new token. Once any of the involved tokens is canceled, the combined token is also triggered. This simplifies the implementation of complex cancellation logic and improves readability. However, caution is necessary: incorrect linking can cause operations to be canceled too early or not at all if the relationships between tokens are not clearly defined.&lt;/p&gt;

&lt;p&gt;Best Practices for Handling CancellationToken&lt;/p&gt;

&lt;p&gt;To ensure smooth cancellation management, the following points should be considered:&lt;br&gt;
    1.  Respect the Passed Token&lt;br&gt;
Avoid ignoring the CancellationToken. In every relevant method, actively check it or use an appropriate mechanism (e.g., callback).&lt;br&gt;
    2.  Properly Release Resources&lt;br&gt;
Always call the Dispose() method on CancellationTokenSource, preferably using the using block to prevent memory leaks.&lt;br&gt;
    3.  Avoid Unnecessary Sources&lt;br&gt;
Only create a new CancellationTokenSource if passing an existing CancellationToken is insufficient. Otherwise, you risk an unclear codebase and unnecessary resource consumption.&lt;br&gt;
    4.  Proper Error Handling&lt;br&gt;
Catch the OperationCanceledException and handle it meaningfully. Silently swallowing the exception can lead to hard-to-trace error patterns, as cancellation operations behave “invisibly.”&lt;/p&gt;

&lt;p&gt;Additional Aspects and Critical Consideration&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Cases for Timeouts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While the use of a timeout has already been mentioned, in a highly connected world, variable external factors such as fluctuating network bandwidths or different devices can quickly render a rigid timeout obsolete. Wouldn’t it make sense to take a closer look at adaptive timeout management?&lt;/p&gt;

&lt;p&gt;Consider scenarios where the system responds to environmental parameters and dynamically adjusts timeouts. For example, a microservice that reduces wait times for certain requests under high load or grants more generous timeouts with a good connection could significantly enhance user satisfaction. Load balancers and reverse proxies also play an important role here, as they can capture both the context and the load of the respective services.&lt;/p&gt;

&lt;p&gt;In short, a purely time-based approach without context can lead to misjudgments and, at worst, prematurely cancel important operations. In the age of cloud computing and highly variable environments, it’s worthwhile to consider context-dependent timeout management with circuit breakers or retry mechanisms.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Depth of Example Implementations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although the article uses concise code snippets (such as the using statement), a more in-depth presentation of more complex application examples would be helpful. Especially in microservice architectures or when accessing databases, the layered cancellation logic often becomes apparent in practice.&lt;/p&gt;

&lt;p&gt;A realistic example could cover the following aspects:&lt;br&gt;
    • Multiple CancellationTokens originating from different sources (e.g., user inputs, timeouts, overload signals).&lt;br&gt;
    • A database-heavy operation accessing an external resource that, upon interruption, must not only reset local but also database transactions.&lt;br&gt;
    • Logical separation of cancellation handling at the service level and repository level to ensure a clear separation of concerns and minimize side effects.&lt;/p&gt;

&lt;p&gt;Such comprehensive implementations would allow developers to recognize and avoid common pitfalls—such as forgetting to call Dispose() or improperly catching OperationCanceledException—in a realistic context.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;More Comprehensive Error Handling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The article already emphasizes the importance of catching OperationCanceledException. But how specifically should the error situation be handled in complex systems where multiple components respond to the same token?&lt;/p&gt;

&lt;p&gt;A possible scenario could look like this: A user-initiated cancellation request is recognized, but a downstream sub-component does not respond in time or blocks while requesting resources. Partial failures occur, which might only appear late in the logs and are difficult to attribute to a specific cause. In such cases, structured exception handling is needed, considering the following points:&lt;br&gt;
    • Targeted Logging: Every cancellation request should appear in the logs, ideally with context information (e.g., which sub-component responded, which actions were interrupted).&lt;br&gt;
    • Separation of Regular Exceptions and Cancellation Cases: An OperationCanceledException does not indicate a classic error state but a deliberate cancellation. However, the interplay of multiple CancellationTokens can create unforeseen side effects, making a consistent and well-documented error strategy essential.&lt;br&gt;
    • Fallback Mechanisms: In safety-critical or highly available environments, a cancellation should not lead to an unstable overall system but support orderly fallback procedures (e.g., graceful shutdown).&lt;/p&gt;

&lt;p&gt;Especially in distributed architectures where multiple services and applications interact, a well-documented error handling and logging process is indispensable to make cancellation processes both traceable and controlled.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;The CancellationToken is a central component in the development of modern, reactive, and resource-efficient .NET applications. However, its correct use requires a certain level of attention: underestimating the importance of clean resource management or ignoring cancellation signals in the code can lead to errors and performance problems almost by default.&lt;/p&gt;

&lt;p&gt;Is it really acceptable to forgo this central cancellation mechanism or implement it only half-heartedly? Those who value scalability and user satisfaction will clearly answer no. Consistently following the described best practices leads to robust and controlled cancellation management, enhances application performance, and improves the user experience equally. At the same time, a critical look at advanced concepts such as adaptive timeout management, comprehensive implementation examples, and structured error handling is recommended to make the use of CancellationToken even more effective and secure.&lt;/p&gt;

</description>
      <category>development</category>
      <category>csharp</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Asynchronous HTTP Requests – Explained Simply</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Wed, 29 Jan 2025 17:01:17 +0000</pubDate>
      <link>https://forem.com/ben-witt/asynchronous-http-requests-explained-simply-2jkh</link>
      <guid>https://forem.com/ben-witt/asynchronous-http-requests-explained-simply-2jkh</guid>
      <description>&lt;p&gt;Imagine a waiter taking your order and waiting in the kitchen until your food is ready. During this time, the waiter cannot serve other guests. This is how a synchronous HTTP request works – it blocks the process.&lt;/p&gt;

&lt;p&gt;An asynchronous HTTP request, on the other hand, allows the waiter to serve other guests while your food is being prepared. Once your dish is ready, the waiter brings it to you without others being delayed. Sounds efficient, right?&lt;/p&gt;

&lt;p&gt;Why Are Asynchronous Requests So Important?&lt;/p&gt;

&lt;p&gt;In daily life, we encounter asynchronous HTTP requests all the time, for example:&lt;br&gt;
    • WhatsApp: Messages are loaded while you’re using other functions.&lt;br&gt;
    • Instagram: Images and videos are loaded in the background.&lt;br&gt;
    • Netflix: The next episode is preloaded while you’re still watching.&lt;br&gt;
    • Online Shopping: Products are filtered and sorted in the background.&lt;/p&gt;

&lt;p&gt;What Does Idempotence Mean in This Context?&lt;/p&gt;

&lt;p&gt;Idempotence is an essential concept in web development, particularly for HTTP requests. It describes the property of an operation that delivers the same result no matter how many times it is performed. This principle plays an important role in the reliability and repeatability of requests – especially with asynchronous HTTP requests, as they might be sent multiple times due to network failures or timeouts.&lt;/p&gt;

&lt;p&gt;Examples of idempotent HTTP methods:&lt;br&gt;
    • GET: Repeatedly fetching the same resource always returns the same data (unless the resource changes).&lt;br&gt;
    • PUT: Overwriting a resource with the same data does not change the final state.&lt;br&gt;
    • DELETE: Deleting a resource has no effect if the resource has already been removed.&lt;/p&gt;

&lt;p&gt;In contrast, POST is not idempotent because it often creates new resources or changes states, which can lead to inconsistent behavior when executed multiple times.&lt;/p&gt;

&lt;p&gt;A Simple Introduction in C#&lt;/p&gt;

&lt;p&gt;Here is an example of fetching weather data in C#, which also considers idempotence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class WetterService
{
    private readonly HttpClient _httpClient;

    public WetterService()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("https://api.wetter.de/");
    }

    public async Task&amp;lt;WetterDaten&amp;gt; HoleWetterAsync(string stadt)
    {
        try
        {
            var antwort = await _httpClient.GetAsync($"wetter/{stadt}");

            if (!antwort.IsSuccessStatusCode)
            {
                return new WetterDaten { Fehler = "Ups! Da ist etwas schiefgelaufen." };
            }

            var json = await antwort.Content.ReadAsStringAsync();
            return JsonSerializer.Deserialize&amp;lt;WetterDaten&amp;gt;(json);
        }
        catch (Exception)
        {
            return new WetterDaten { Fehler = "Sorry, wir konnten das Wetter nicht abrufen." };
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: The use of the HTTP-GET method ensures idempotence. Even with multiple requests, the server’s state remains unchanged.&lt;/p&gt;

&lt;p&gt;Practical Example: Online Shop&lt;/p&gt;

&lt;p&gt;A typical controller processing asynchronous and idempotent requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ApiController]
[Route("api/produkte")]
public class ProduktController : ControllerBase
{
    private readonly IProduktService _produktService;

    public ProduktController(IProduktService produktService)
    {
        _produktService = produktService;
    }

    [HttpGet]
    public async Task&amp;lt;ActionResult&amp;lt;List&amp;lt;Produkt&amp;gt;&amp;gt;&amp;gt; HoleAlleProdukte()
    {
        var produkte = await _produktService.HoleAlleProdukteAsync();
        return Ok(produkte);
    }

    [HttpGet("{id}")]
    public async Task&amp;lt;ActionResult&amp;lt;Produkt&amp;gt;&amp;gt; HoleProdukt(int id)
    {
        var produkt = await _produktService.HoleProduktAsync(id);

        if (produkt == null)
        {
            return NotFound("Produkt nicht gefunden");
        }

        return Ok(produkt);
    }

    [HttpDelete("{id}")]
    public async Task&amp;lt;IActionResult&amp;gt; LoescheProdukt(int id)
    {
        var erfolgreich = await _produktService.LoescheProduktAsync(id);

        if (!erfolgreich)
        {
            return NotFound("Produkt nicht gefunden oder bereits gelöscht.");
        }

        return NoContent();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Idempotence in detail: Both GET and DELETE are idempotent here. Even with multiple executions, the results remain consistent, enhancing the system’s stability.&lt;/p&gt;

&lt;p&gt;Benefits of Asynchronous and Idempotent HTTP Requests&lt;br&gt;
    1.  Better User Experience: The app remains responsive and does not freeze.&lt;br&gt;
    2.  More Efficient Resource Utilization: The server can handle other tasks during wait times.&lt;br&gt;
    3.  Increased Reliability: Repeated requests do not lead to inconsistent behavior thanks to idempotence.&lt;br&gt;
    4.  Scalability: Asynchronous processing reduces bottlenecks, especially with parallel requests.&lt;/p&gt;

&lt;p&gt;Advanced Use Case: Parallel Loading&lt;/p&gt;

&lt;p&gt;Sometimes it is necessary to load multiple resources simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class DashboardService
{
    private readonly IBenutzerService _benutzerService;
    private readonly IBestellungService _bestellungService;

    public async Task&amp;lt;DashboardDaten&amp;gt; HoleDashboardDatenAsync()
    {
        var benutzerTask = _benutzerService.HoleStatistikenAsync();
        var bestellungenTask = _bestellungService.HoleStatistikenAsync();

        await Task.WhenAll(benutzerTask, bestellungenTask);

        return new DashboardDaten
        {
            BenutzerStats = await benutzerTask,
            BestellungStats = await bestellungenTask
        };
    }
}

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

&lt;/div&gt;



&lt;p&gt;Common Pitfalls and Solutions&lt;br&gt;
    1.  Forgotten await: Without await, the code remains blocking and loses its asynchronous behavior.&lt;br&gt;
    2.  Neglecting Error Handling: Network issues are common, so plan for appropriate error handling.&lt;br&gt;
    3.  Not Setting Timeouts: Avoid endless wait times by implementing timeout rules.&lt;br&gt;
    4.  Ignoring Idempotence: Non-idempotent requests can lead to hard-to-trace errors.&lt;br&gt;
    5.  Lack of Tests: Asynchronous code is prone to subtle bugs and should be thoroughly tested.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Asynchronous HTTP requests are the foundation of modern, responsive applications. By adhering to idempotence principles, repeated requests do not produce unexpected results. Together, they not only enhance the user experience but also create robust and scalable systems. However, their proper implementation requires careful planning and execution.&lt;/p&gt;

</description>
      <category>devto</category>
      <category>developer</category>
      <category>opensource</category>
      <category>coding</category>
    </item>
    <item>
      <title>WPF Application with Plugin Architecture</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 15 Oct 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/wpf-application-with-plugin-architecture-113h</link>
      <guid>https://forem.com/ben-witt/wpf-application-with-plugin-architecture-113h</guid>
      <description>&lt;p&gt;In modern software architectures, plug-in systems offer a flexible and modular way of extending software without changing the core of the application. A specific WPF project will be used to show how a plug-in architecture can be implemented to create loosely coupled and extensible components. The focus is on the interaction between the main application, plug-ins and the use of a central event aggregator for communication between the components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview over the structure
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  The plug-in interface
&lt;/h2&gt;

&lt;p&gt;Central to any plug-in system is the definition of a clear interface that all plug-ins must implement. This creates consistency and ensures that the main application and the plug-ins can interact seamlessly with each other. In our example, this is achieved through the IPlugin interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace MyPluginInterface
{
  public interface IPlugin
  {
    string Name { get; }
    IEnumerable&amp;lt;Type&amp;gt; ViewTypes { get; } 
    IEnumerable&amp;lt;Type&amp;gt; ViewModelTypes { get; }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface specifies that each plug-in provides a name, a list of views (ViewTypes) and the associated ViewModels (ViewModelTypes). This ensures that all plug-ins can be uniformly integrated into the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of a plug-in
&lt;/h2&gt;

&lt;p&gt;A typical plug-in in this system implements the IPlugin interface. In the following example, the plug-in provides two views and the corresponding ViewModels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using MyFirstPlugin.ViewModels;
using MyFirstPlugin.Views;
using MyPluginInterface;

namespace MyFirstPlugin
{
  public class MyFirstPlugin : IPlugin
  {
    public string Name =&amp;gt; "My First Plugin with Multiple Views";

    public IEnumerable&amp;lt;Type&amp;gt; ViewTypes =&amp;gt; new List&amp;lt;Type&amp;gt;
    {
        typeof(MyPluginView),
        typeof(MyPlugin1View)
    };

    public IEnumerable&amp;lt;Type&amp;gt; ViewModelTypes =&amp;gt; new List&amp;lt;Type&amp;gt;
    {
        typeof(MyPluginViewModel),
        typeof(MyPlugin1ViewModel)
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plug-in defines two views (MyPluginView, MyPlugin1View) and their associated ViewModels (MyPluginViewModel, MyPlugin1ViewModel). This makes it possible to define various UI components within the plug-in that can be integrated into the main application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic loading and registration of plug-ins
&lt;/h2&gt;

&lt;p&gt;One of the central functions of a plug-in system is the dynamic loading of plug-ins at runtime. This is done by searching for and loading assemblies that implement the plug-in interface. The following code in the main application shows how this is achieved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void LoadPlugins(ContainerBuilder builder)
{
  var pluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
  var pluginFiles = Directory.GetFiles(pluginPath, "*.dll");

  foreach (var pluginFile in pluginFiles)
  {
    var assembly = Assembly.LoadFrom(pluginFile);
    var pluginTypes = assembly.GetTypes().Where(t =&amp;gt; typeof(IPlugin).IsAssignableFrom(t) &amp;amp;&amp;amp; !t.IsInterface &amp;amp;&amp;amp; !t.IsAbstract);

    foreach (var pluginType in pluginTypes)
    {
      var plugin = (IPlugin)Activator.CreateInstance(pluginType);

      foreach (var viewType in plugin.ViewTypes)
      {
        builder.RegisterType(viewType).AsSelf().InstancePerDependency();
      }

      foreach (var viewModelType in plugin.ViewModelTypes)
      {
        builder.RegisterType(viewModelType).AsSelf().InstancePerDependency();
      }

      builder.RegisterInstance(plugin).As&amp;lt;IPlugin&amp;gt;();

      // Registers the views and ViewModels
      builder.RegisterViewsAndViewModels(assembly);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, a directory is searched for plug-ins that are stored as DLLs. Each plug-in is loaded dynamically and the views and ViewModels defined in it are registered with Autofac in the dependency injection container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic registration of views and ViewModels with Autofac
&lt;/h2&gt;

&lt;p&gt;We use an extended function of Autofac for efficient registration of views and ViewModels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class AutofacExtensions
{
  public static void RegisterViewsAndViewModels(this ContainerBuilder builder, Assembly assembly)
  {
    builder.RegisterAssemblyTypes(assembly)
           .Where(t =&amp;gt; t.Name.EndsWith("View") &amp;amp;&amp;amp; typeof(FrameworkElement).IsAssignableFrom(t))
           .OnActivated(args =&amp;gt;
           {
             var viewType = args.Instance.GetType(); 
             var viewModelTypeName = viewType.Name.Replace("View", "ViewModel");
             var viewModelType = viewType.Assembly.GetType(viewType.Namespace + "." + viewModelTypeName); 

             if (viewModelType == null)
             {
               viewModelType = assembly.GetTypes().FirstOrDefault(t =&amp;gt; t.Name == viewModelTypeName);
             }

             if (viewModelType != null)
             {
               var viewModel = args.Context.Resolve(viewModelType);
               ((FrameworkElement)args.Instance).DataContext = viewModel;
             }
           })
           .AsSelf()
           .InstancePerDependency();

    builder.RegisterAssemblyTypes(assembly)
           .Where(t =&amp;gt; t.Name.EndsWith("ViewModel"))
           .AsSelf()
           .InstancePerDependency();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method registers all Views and ViewModels of a plug-in based on naming conventions. Views that end with 'View' are registered and the associated ViewModels are also identified and loaded into the DI container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication between plug-ins via the EventAggregator
&lt;/h2&gt;

&lt;p&gt;In complex applications, the plug-ins must communicate with each other without creating direct dependencies in order to ensure modularity. In our project, this is made possible by the EventAggregator. Here is a concrete example from the MyFirstPlugin:&lt;br&gt;
&lt;strong&gt;Plugin 1: Send message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MyPlugin1ViewModel
{
    private readonly IEventAggregator _eventAggregator;

    public ICommand MyCommand { get; }

    public MyPlugin1ViewModel(IEventAggregator eventAggregator)
    {
      _eventAggregator = eventAggregator;
      MyCommand = new RelayCommand(_ =&amp;gt; OnButtonClick());
    }

    private void OnButtonClick()
    {
      var msg = new SpecialEvent("Hello from the other view internal my first plugin");
      _eventAggregator.Publish(msg);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the MyPlugin1ViewModel sends a special event, SpecialEvent, with a message when the user presses a button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plugin 2: Receive message&lt;/strong&gt;&lt;br&gt;
The second plug-in can subscribe to this event and process the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MySecondPluginViewModel : IHandle&amp;lt;SpecialEvent&amp;gt;
{
    private readonly IEventAggregator _eventAggregator;

    public MySecondPluginViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        _eventAggregator.Subscribe(this);
    }

    public void Handle(SpecialEvent message)
    {
        // Process message
        Console.WriteLine(message.Content);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, MySecondPluginViewModel receives the SpecialEvent and processes its content in the handle method. This ensures that the two plug-ins can communicate via the EventAggregator without being directly dependent on each other.&lt;/p&gt;

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

&lt;p&gt;A plug-in architecture offers a flexible and scalable solution for extending an application. In our example, this architecture is realised through the combination of Autofac for dependency injection and an event aggregator for communication between the components. Such systems are ideal for scenarios in which an application needs to be dynamically extended with new functionalities without changing the core of the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2FWittBen%2FWPF-Plugins" rel="noopener noreferrer"&gt;plugIn architecture&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>csharp</category>
      <category>coding</category>
      <category>development</category>
    </item>
    <item>
      <title>HealthChecks</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 08 Oct 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/healthchecks-4aie</link>
      <guid>https://forem.com/ben-witt/healthchecks-4aie</guid>
      <description>&lt;p&gt;From time to time it happens that the connection to the database fails or the services or containers crash. This can lead to a considerable investigation effort to find the reasons why these problems occur, which can be facilitated by the use of “HealthChecks”.&lt;/p&gt;

&lt;p&gt;ASP.NET provides a convenient and effective approach to implementing this functionality.&lt;/p&gt;

&lt;p&gt;To achieve this, the following nuggets need to be installed:&lt;/p&gt;

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

&lt;p&gt;To include the following items in the Program.cs file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddHealthChecks();  
...  
...  
//Now the HealthChecks should be mapped to the current endpoint  
app.MapHealthChecks("/_health");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would provide us with the foundation to check the health of our application. Currently, no checks are performed, so our application is considered to be in a healthy state.&lt;/p&gt;

&lt;p&gt;Upon launching the app, initially, only the Swagger site is loaded and no other content is visible. Let’s simply update the address to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyk2yeyvkj8a3q90hhaue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyk2yeyvkj8a3q90hhaue.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
To extend our HealthCheck with a custom check, we will create a new class called “&lt;strong&gt;MyFirstHealthCheck&lt;/strong&gt;” with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MyFirstHealthCheck : IHealthCheck  
{    
  public Task&amp;lt;HealthCheckResult&amp;gt; CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)    
  {      
    return Task.FromResult(HealthCheckResult.Healthy("This could be a service"));    
  }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class serves the purpose of returning a “HealthCheckResult” with the status “Healthy”.&lt;/p&gt;

&lt;p&gt;We have reached a point where we need to include this check in our Program.cs file. When we call this check, the situation looks a little different. Initially, the appearance remains unchanged. However, if we set a breakpoint in the source code, it will be triggered when we refresh the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqk6r882c8q4kxm2guyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqk6r882c8q4kxm2guyv.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Instead of using the term “healthy”, which is not very meaningful, it would be more helpful to convey a specific message. For example, we could use a more descriptive message to communicate the status or result of the test.&lt;/p&gt;

&lt;p&gt;To accomplish this, we need to ensure that the NuGet package “AspNetCore.HealthChecks.UI.Client” is installed. After that, we can proceed to extend the following functionality:&lt;/p&gt;

&lt;p&gt;In the Program.cs file, we need to incorporate a ResponseWriter into the mapper of the HealthCheck. This will enable us to customize the output or response generated by the HealthCheck module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aziko86nusp3tk82tgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aziko86nusp3tk82tgu.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Let’s restart the program and see what comes out of it.&lt;/p&gt;

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

&lt;p&gt;To enable easy retrieval from a list, we can assign a tag to this check. Implementing the tag in the service would facilitate the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb62jwzxgvrwxnawuktae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb62jwzxgvrwxnawuktae.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Now let’s execute this request once more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz46f2r64zhegupwo8fgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz46f2r64zhegupwo8fgp.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Let’s create another check:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprnzn7lyjd6hsum38v1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprnzn7lyjd6hsum38v1f.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
It can be a bit overwhelming at first.&lt;/p&gt;

&lt;p&gt;To create a dashboard that lists all our checks using the NuGet package “AspNetCore.HealthChecks.UI.Client," we need to make three additions to the Program.cs file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add a new service:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This new dashboard requires persistent storage, so we need to add the NuGet package “AspNetCore.HealthChecks.UI.InMemory.Storage."&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;2. New Mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below the previous AddHealthChecks() method call, we can add a new method call AddHealthChecksUI() that uses the in-memory store.&lt;/p&gt;

&lt;p&gt;To map this new user interface, we can add a route to the Configure() method in the Startup.cs file. The route should point to the Health Checks UI endpoint.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;3.) AppSettings.json&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To configure the dashboard to forward requests to the appropriate address, you can modify the appsettings.json file. Within the file, you will need to update the relevant settings to specify the desired address for request forwarding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81nhu2jl98xxrz62p1fn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81nhu2jl98xxrz62p1fn.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
We start the application and change the address:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2f8mlnvcwfukpbnjganp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2f8mlnvcwfukpbnjganp.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
By utilizing the dashboard we have created, we can now have a comprehensive overview of the checks we have implemented for our application. The dashboard not only displays the current status of the checks but also provides a history section that gives us insights into the events that have occurred from the application’s start until now. To access more detailed information, we can explore the details section within the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv1ug9frczst5nmti33a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv1ug9frczst5nmti33a.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
What could a HealthCheck that monitors the connection to the database look like?&lt;/p&gt;

&lt;p&gt;We create a new object that checks the connection to the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class DatabaseConnectionCheck : IHealthCheck
{
  public async Task&amp;lt;HealthCheckResult&amp;gt; CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
  {
    try
    {
      using var connection = new SqlConnection("Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False");
      await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
      using var command = connection.CreateCommand();
      command.CommandText = "Select 1";
      object result = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);

      return HealthCheckResult.Healthy("The status of the connection is open");
    }
    catch (Exception ex)
    {
      return HealthCheckResult.Unhealthy("The status of the connection is close");
    }

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

&lt;/div&gt;



&lt;p&gt;Here only the connection to the database is established and by the select 1 it is checked whether the query can be sent or not. Accordingly, a result is returned which is healthy or unhealthy.&lt;/p&gt;

&lt;p&gt;Now we only have to announce the service:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv7kcxbhglag5k7sulo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv7kcxbhglag5k7sulo8.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Now we have a comprehensive overview of all the checks that have been configured in our application. This provides a clear and organized display of the various checks and their respective statuses, making it easier to monitor and track the health of our application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycnj937s57c4fkwepomp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycnj937s57c4fkwepomp.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
these checks are queried every 5 seconds as we set, now if the connection to the database is closed or interrupted, this dashboard will react to it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe10uiyn4itpga5p78v8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe10uiyn4itpga5p78v8f.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
you will find a number of nuget dealing with some topics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e8arffwlz22sssalbej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e8arffwlz22sssalbej.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/WittBen/HealthChecks?source=post_page-----4744f10b8970--------------------------------" rel="noopener noreferrer"&gt;Github: HealthChecks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>aspnet</category>
      <category>development</category>
      <category>webdev</category>
    </item>
    <item>
      <title>DBConcurrency with EF Core</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 01 Oct 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/dbconcurrency-with-ef-core-e02</link>
      <guid>https://forem.com/ben-witt/dbconcurrency-with-ef-core-e02</guid>
      <description>&lt;h2&gt;
  
  
  What is concurrency in the database?
&lt;/h2&gt;

&lt;p&gt;Concurrency is the simultaneous processing of data by multiple users or processes. In a database environment, this can lead to conflicts if two transactions attempt to access the same data at the same time.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why is concurrency control important?
&lt;/h2&gt;

&lt;p&gt;Concurrency control is important to ensure that multiple processes or threads can run simultaneously in a system without causing unexpected problems. This is important for efficient resource utilisation, data consistency and avoiding race conditions and deadlocks. Effective concurrency control improves the overall performance of systems, especially in multi-core environments, and contributes to a stable and user-friendly experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conflict detection in Entity Framework Core
&lt;/h2&gt;

&lt;p&gt;Entity Framework Core offers mechanisms for recognising conflicts.&lt;/p&gt;

&lt;p&gt;In Entity Framework Core, conflict detection is often activated by the “Timestamp” attribute or the “ConcurrencyCheck” attribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  „Timestamp” attribute:
&lt;/h2&gt;

&lt;p&gt;Purpose: The “Timestamp” attribute is used to detect conflicts with concurrent updates in Entity Framework Core.&lt;br&gt;
How it works: When the &lt;code&gt;Timestamp&lt;/code&gt; attribute is applied to a property, Entity Framework Core automatically creates a database column of type RowVersion. This column is automatically updated with every update. If two updates occur simultaneously, the system recognises a conflict based on this column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Employee
{
  public int Id { get; set; }
  public string FirstName { get; set; } = string.Empty;
  public string LastName { get; set; } = string.Empty;

  public string Fullname =&amp;gt; $"{FirstName} {LastName}";

  public string Department { get; set; } = string.Empty;
  public int EntryYear { get; set; }

  [Timestamp]
  public byte[] Version { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  „ConcurrencyCheck“ attribute:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; The “ConcurrencyCheck” attribute is also used to detect conflicts during updates by ensuring that no changes were made to certain properties during the update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; When the &lt;code&gt;ConcurrencyCheck&lt;/code&gt; attribute is applied to a property, Entity Framework Core compares the value of that property during the update with the value stored in the database. If differences are detected, a conflict is recognised.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Employee
{
  public int Id { get; set; }
  public string FirstName { get; set; } = string.Empty;
  public string LastName { get; set; } = string.Empty;

  public string Fullname =&amp;gt; $"{FirstName} {LastName}";

  public string Department { get; set; } = string.Empty;
  public int EntryYear { get; set; }

  [ConcurrencyCheck]
  public byte[] RowVersion { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both approaches serve to ensure that database updates can be carried out consistently and without conflict, even if several users access the database at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for conflict resolution
&lt;/h2&gt;

&lt;p&gt;When resolving conflicts in an application, there are various strategies to ensure that simultaneous updates of data are consistent and without data inconsistencies. Here are some common strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last writer wins (lww):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: the last update written to the database is considered the valid version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: If a conflict occurs, the update with the most recent timestamp or transaction ID is favoured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Extended conflict resolution (Custom Conflict Resolution):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: custom logic is implemented to resolve conflicts based on application logic or user preferences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: Developers determine how conflicts should be handled, possibly through user interaction or specific business rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optimistic conflict resolution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: It is assumed that conflicts rarely occur, so that the data is updated without prior blocking. Conflicts are only checked when data is written to the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: Use of mechanisms such as &lt;code&gt;Timestamp&lt;/code&gt; or &lt;code&gt;ConcurrencyCheck&lt;/code&gt; attributes. In the event of a conflict, an exception is raised and the application can react.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pessimistic conflict resolution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: data is locked throughout the update process to ensure that no other updates can be performed at the same time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: Use of explicit locks or transaction isolation levels to ensure that no other operations can access the locked data at the same time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Automatic merging:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: In the event of conflicts, changes are automatically merged if this is possible. This is often used in distributed version control systems or collaborative tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: Automatic merging algorithms are used to recognise and automatically resolve conflicts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The choice of the appropriate strategy depends on the requirements of the application, the type of data and the expected usage patterns. It is important to choose a strategy that ensures data consistency and at the same time does not impair the performance and user-friendliness of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of concurrency control in a sample application
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Assume you have an “Employee” entity class with a “LastName” property and a timestamp attribute for concurrency control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;

public string Fullname =&amp;gt; $"{FirstName} {LastName}";

public string Department { get; set; } = string.Empty;
public int EntryYear { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; } // Timestamp-Eigenschaft für Concurrency
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s how you can use the ConcurrencyCheck attribute for concurrency control in an entity class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Employee
{
  public int Id { get; set; }
  public string FirstName { get; set; } = string.Empty;
  public string LastName { get; set; } = string.Empty;

  public string Fullname =&amp;gt; $"{FirstName} {LastName}";

  public string Department { get; set; } = string.Empty;
  public int EntryYear { get; set; }

  [ConcurrencyCheck]
  public byte[] RowVersion { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your DbContext, you can then specify that the timestamp row acts as RowVersion:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In our example, we select the “ConcurrencyCheck”&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void Configure(EntityTypeBuilder&amp;lt;Employee&amp;gt; builder)
 {
   builder.HasKey(c =&amp;gt; c.Id);
   builder.Property(c =&amp;gt; c.Id).ValueGeneratedOnAdd();
   builder.Property(rv =&amp;gt; rv.RowVersion).IsRowVersion();
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conflict simulation:
&lt;/h2&gt;

&lt;p&gt;To simulate a concurrency conflict, you can perform the following steps:&lt;/p&gt;

&lt;p&gt;Assume you already have an employee in your database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var loadedEmployee = dbContext1.Employees.Find(id);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we simulate a conflict by updating the lastname both in the database and in the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Changes in the database by another process

var existingEmployeeInDatabase = dbContext2.Employees.Find(id);


if (loadedEmployee != null &amp;amp;&amp;amp; existingEmployeeInDatabase != null)
 {
   // Change only loadedEmployee
   loadedEmployee.LastName = "NewLastName1";

   try
   {
     dbContext1.SaveChanges();  // Save changes in LoadedEmployee
   }
   catch (DbUpdateConcurrencyException ex)
   {
     Console.WriteLine($"Concurrency conflict on LoadedEmployee: {ex.Message}");
     // Here you can implement conflict handling, e.g. retry or report errors to the user interface.
   }

   // Change existingEmployeeInDatabase now
   existingEmployeeInDatabase.LastName = "NewLastName2";

   try
   {
     dbContext2.SaveChanges();  // Try to save changes in existingEmployeeInDatabase, which should throw a DbUpdateConcurrencyException
     Console.WriteLine("Records updated successfully.");
   }
   catch (DbUpdateConcurrencyException ex)
   {
     Console.WriteLine($"Concurrency conflict on existingEmployeeInDatabase: {ex.Message}");
     // Here you can implement conflict handling, e.g. retry or report errors to the user interface.
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;dbContext2.SaveChanges()&lt;/code&gt; will throw a &lt;code&gt;DbUpdateConcurrencyException&lt;/code&gt; because the database version of the product no longer matches the one in your application.&lt;/p&gt;

&lt;p&gt;You can now implement the conflict resolution logic to decide how to deal with this conflict. This can include applying strategies such as “client wins”, “database wins” or customised conflict resolution.&lt;/p&gt;

&lt;p&gt;Here is an example of the client wins strategy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using (var dbContext1 = Create.Context())
 using (var dbContext2 = Create.Context())
 {
   var loadedEmployee = dbContext1.Employees.Find(id);
   var existingEmployeeInDatabase = dbContext2.Employees.Find(id);

   if (loadedEmployee != null &amp;amp;&amp;amp; existingEmployeeInDatabase != null)
   {
     // Change only loadedEmployee
     loadedEmployee.LastName = "NewLastName1";

     try
     {
       dbContext1.SaveChanges();  // Save changes in LoadedEmployee
     }
     catch (DbUpdateConcurrencyException ex)
     {
       Console.WriteLine($"Concurrency conflict on LoadedEmployee: {ex.Message}");

       ex.Entries.Single().Reload();
       dbContext1.SaveChanges(); 
       Console.WriteLine("Client Wins: Records updated successfully.");
     }

     // Change existingEmployeeInDatabase now
     existingEmployeeInDatabase.LastName = "NewLastName2";

     try
     {
       dbContext2.SaveChanges();  // Try to save changes in existingEmployeeInDatabase, which should throw a DbUpdateConcurrencyException
       Console.WriteLine("Records updated successfully.");
     }
     catch (DbUpdateConcurrencyException ex)
     {
       Console.WriteLine($"Concurrency conflict on existingEmployeeInDatabase: {ex.Message}");
       // Here you can implement conflict handling, e.g. retry or report errors to the user interface.
     }
   }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concurrency proves to be essential by enabling simultaneous access to database resources. Precise handling of DBConcurrency minimises potential conflicts, ensures data consistency and guarantees integrity in dynamic environments. Clever implementation of mechanisms such as transaction control and locking procedures not only optimises synchronisation, but also increases performance. In complex applications that access shared databases, the accurate handling of concurrency is crucial for a robust system architecture and smooth functioning under intensive load.&lt;br&gt;
&lt;a href="https://github.com/WittBen/DBConcurrency?source=post_page-----3f1c0d6475d1--------------------------------" rel="noopener noreferrer"&gt;GitHub: DBConcurrency&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>microsoft</category>
      <category>developer</category>
      <category>efcore</category>
    </item>
    <item>
      <title>Basics of Clean Architecture with C#</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 24 Sep 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/basics-of-clean-architecture-with-c-218i</link>
      <guid>https://forem.com/ben-witt/basics-of-clean-architecture-with-c-218i</guid>
      <description>&lt;p&gt;This article covers the basics of Clean Architecture with C#. Readers will be empowered to develop solid, well-structured code that excels in maintainability, extensibility, and testability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Clean Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is Clean Architecture?&lt;/strong&gt;&lt;br&gt;
Clean Architecture, proposed by Robert C. Martin or “Uncle Bob,” is an architectural pattern for software projects aimed at keeping the code clean, modular, and independent of external influences. It isolates the business logic of the application from external details such as user interfaces, databases, and frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is Clean Architecture important?&lt;/strong&gt;&lt;br&gt;
Clean Architecture offers a variety of advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High Maintainability&lt;/strong&gt;: The clear separation of business logic and technical details facilitates understanding of the code and enhances its maintainability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;2.** Extensibility**: Thanks to the well-structured architecture, new features can be added or existing ones modified easily without jeopardizing the integrity of the overall application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testability&lt;/strong&gt;: The clean separation of components simplifies the writing of automated tests, thereby improving the quality of the software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Independence from external influences&lt;/strong&gt;: By using abstractions and interfaces, the application remains flexible and is not tied to specific technologies or frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The benefits of Clean Architecture include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear Separation of Responsibilities&lt;/strong&gt;: The clear structure defines the responsibilities of the various components, leading to better-organized code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimization of Dependencies&lt;/strong&gt;: Clean Architecture minimizes dependencies between different parts of the application, enhancing flexibility and maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facilitation of Testing&lt;/strong&gt;: The clean separation of components allows tests to focus on individual parts of the application, improving testability and software quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support for Scalability and Extensibility&lt;/strong&gt;: The modular structure of Clean Architecture makes it easier to scale the application and add new features without redesigning the entire architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Principles of Clean Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Separation of Concerns:&lt;/strong&gt;&lt;br&gt;
This principle demands that various aspects of a software application, such as user interface, business logic, and data persistence, be separated from each other. This leads to better-structured and more maintainable code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency Rule:&lt;/strong&gt;&lt;br&gt;
The dependency rule states that dependencies within an application should always point towards stable layers. For example, the business logic layer should not have direct dependencies on the user interface or databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stable Dependencies Principle:&lt;/strong&gt;&lt;br&gt;
This principle dictates that dependent components should be more stable than the components they depend on. This means that high-level modules should not depend on low-level modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stable Abstractions Principle:&lt;/strong&gt;&lt;br&gt;
Here, the abstraction should be more stable than the concrete implementation. Abstractions should be independent of concrete implementations.&lt;/p&gt;

&lt;p&gt;These principles form the backbone of Clean Architecture and contribute to creating a robust and flexible software architecture. Now, let’s take a look at the components of Clean Architecture.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Components of Clean Architecture
&lt;/h2&gt;

&lt;p&gt;Clean Architecture comprises various components designed to divide the application into clearly defined layers. Here are the main components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Dependency Inversion Principle:&lt;/strong&gt; This principle states that dependencies should depend on abstract interfaces, not concrete implementations. This decouples the layers and increases the flexibility and testability of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Entities:&lt;/strong&gt; Entities represent the core objects of the application, representing the state and behavior of the business logic. They are independent of the database or other external systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use Cases:&lt;/strong&gt; Use Cases contain the application logic and define how user interactions are handled. They are independent of the user interface and other external systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Interface Adapters:&lt;/strong&gt; These adapters connect the application to external systems such as databases, APIs, or user interfaces. They convert data and calls into formats that can be processed by the core application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Frameworks &amp;amp; Drivers:&lt;/strong&gt; This layer contains external frameworks and drivers used by the application, such as web frameworks, database drivers, or user interface libraries.&lt;/p&gt;

&lt;p&gt;By clearly separating these components, changes can be made in one area of the application without affecting other areas. This makes the application flexible and easier to maintain.&lt;/p&gt;

&lt;p&gt;Implementation of Clean Architecture in C# (Continued)&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing the Project Structure:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Creating the Core Layer:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define Entities and Use Cases.&lt;br&gt;
Create a folder named “Core.”&lt;br&gt;
Within this folder, classes for Entities such as “User.cs” or “Product.cs” are created.&lt;br&gt;
Create interfaces or abstract classes for Use Cases like “IUserService.cs” or “IProductService.cs”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Creating the Application Layer:&lt;/strong&gt;&lt;br&gt;
Implement Use Cases.&lt;br&gt;
Create a folder named “Application.”&lt;br&gt;
Implement Use Cases as classes that inherit from the interfaces defined in the Core layer.&lt;br&gt;
For example: “UserRegistrationService.cs”, “ProductManagementService.cs”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Creating the Infrastructure Layer:&lt;/strong&gt;&lt;br&gt;
Implement Interface Adapters.&lt;br&gt;
Create a folder named “Infrastructure.”&lt;br&gt;
Implement concrete classes for the interfaces defined in the Core layer.&lt;br&gt;
For example: “UserRepository.cs” for “IUserRepository”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Creating the Presentation Layer:&lt;/strong&gt;&lt;br&gt;
Implement user interface or API controllers.&lt;br&gt;
Create a folder named “Presentation.”&lt;br&gt;
Implement controllers, such as ASP.NET MVC controllers or WebAPI controllers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing Entities:
&lt;/h2&gt;

&lt;p&gt;Entities represent the core objects of the application and include the state and behavior of the business logic. Here’s an example of a “User” Entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample.Core.Entities;

public class User
{
  public int Id { get; set; }
  public string Username { get; set; }
  public string Email { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this structure, we lay the foundation for a clean and modular architecture that is easily maintainable and extensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Use Cases:
&lt;/h2&gt;

&lt;p&gt;Use Cases contain the application logic and define how user interactions are processed. Here’s an example of a “UserRegistration” Use Case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample.Core.UseCases;

public interface IUserRegistrationUseCase
{
  void RegisterUser(string username, string email);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample.Application.Services;

public class UserService : IUserRegistrationUseCase
{
  private readonly IUserRepository _userRepository;

  public UserService(IUserRepository userRepository)
  {
    _userRepository = userRepository;
  }

  public IUserRepository GetUserRepository()
  {
    return _userRepository;
  }

  public void RegisterUser(string username, string email)
  {
    // The user registration logic is implemented here
    var newUser = new User { Username = username, Email = email };

    _userRepository.AddUser(newUser);
    Console.WriteLine($"User {username} with the email {email} was successfully registered.");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing Interface Adapters:
&lt;/h2&gt;

&lt;p&gt;Interface Adapters connect the application to external systems such as databases or APIs. Here’s an example of a UserRepository implementing the database access interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample.Infrastructure;

public interface IUserRepository
{
  void AddUser(User user);
}

public class UserRepository : IUserRepository
{
  public void AddUser(User user)
  {
    // This is where the logic for adding a user to the database would be implemented
    Console.WriteLine($"User {user.Username} has been added to the database.");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will proceed to manage dependencies between components using Dependency Injection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of Clean Architecture in C
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Setting up Dependency Injection:&lt;/strong&gt;&lt;br&gt;
In this step, we will set up Dependency Injection (DI) in our C# project to manage dependencies between different components.&lt;/p&gt;

&lt;p&gt;First, we need to configure a DI container in our project. We can use the built-in DI container library provided by .NET for this purpose. Here’s an example of how we can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample;

class Program
{
  static void Main(string[] args)
  {
    // Configuration of the DI container
    var services = new ServiceCollection();
    services.AddTransient&amp;lt;IUserRepository, UserRepository&amp;gt;();
    services.AddTransient&amp;lt;IUserRegistrationUseCase, UserService&amp;gt;();

    // Create the DI container
    var serviceProvider = services.BuildServiceProvider();

    // Use the dependent components
    var userService = serviceProvider.GetService&amp;lt;IUserRegistrationUseCase&amp;gt;();
    userService.RegisterUser("Max Mustermann", "max@example.com");

    Console.ReadLine(); // Wait for user input so that the console application is not closed immediately
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we use the &lt;code&gt;ServiceCollection&lt;/code&gt; class to register dependencies. With &lt;code&gt;AddTransient&lt;/code&gt;, we specify that a new instance of the dependent class should be created each time it’s requested.&lt;/p&gt;

&lt;p&gt;Once we have configured our DI container, we can use the dependent components in our classes. The DI container takes care of resolving and providing the dependent instances.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;UserRegistrationService&lt;/code&gt; can access the &lt;code&gt;IUserRepository&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureExample.Application.Services;

public class UserService : IUserRegistrationUseCase
{
  private readonly IUserRepository _userRepository;

  public UserService(IUserRepository userRepository)
  {
    _userRepository = userRepository;
  }

  public IUserRepository GetUserRepository()
  {
    return _userRepository;
  }

  public void RegisterUser(string username, string email)
  {
    // The user registration logic is implemented here
    var newUser = new User { Username = username, Email = email };

    _userRepository.AddUser(newUser);
    Console.WriteLine($"User {username} with the email {email} was successfully registered.");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using Dependency Injection, the components are loosely coupled, which improves the maintainability, extensibility, and testability of our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing in Clean Architecture
&lt;/h2&gt;

&lt;p&gt;Testing is an integral part of Clean Architecture, ensuring that our application functions correctly and that changes do not introduce unwanted side effects. In Clean Architecture, we use various types of tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unit Tests:&lt;/strong&gt; These test individual components of our application in isolation. Mock objects can be used to simulate external dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Integration Tests:&lt;/strong&gt; These test the collaboration of multiple components of our application to ensure they interact properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. End-to-End Tests:&lt;/strong&gt; These test the entire application from an external perspective, such as by executing user interactions through the user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test-Driven Development (TDD) Approach:
&lt;/h2&gt;

&lt;p&gt;In the test-driven development approach, we first write tests that define the expected behavior of the application, and then implement the code that passes these tests. This approach promotes thorough test coverage and often leads to better code quality and structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Tests for Various Components:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of a unit test for the &lt;code&gt;UserRegistrationService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
  public void RegisterUser_Should_Register_User()
  {
    // Arrange
    var mockUserRepository = new Mock&amp;lt;IUserRepository&amp;gt;();
    var userService = new UserService(mockUserRepository.Object);
    var username = "John Smith";
    var email = "john@example.com";

    // Act
    userService.RegisterUser(username, email);

    // Assert
    mockUserRepository.Verify(repo =&amp;gt; repo.AddUser(It.IsAny&amp;lt;User&amp;gt;()), Times.Once);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this test, we use a mock object for &lt;code&gt;IUserRepository&lt;/code&gt; to simulate database access. We verify that the &lt;code&gt;AddUser&lt;/code&gt; method of the repository was called once after registering a user.&lt;/p&gt;

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

&lt;p&gt;In this article, we’ve provided a comprehensive overview of the fundamentals of Clean Architecture with C#, covering everything from basic principles to practical implementation and testing. Clean Architecture offers a multitude of benefits for software project development, including improved maintainability, extensibility, and testability. By clearly separating responsibilities and minimizing dependencies, the code becomes more structured and easier to maintain. The use of Dependency Injection allows for flexible configuration of the application and facilitates testing by isolating components. Overall, Clean Architecture provides a robust approach to developing high-quality software that has proven itself in a variety of application domains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/WittBen/CleanArchitecture" rel="noopener noreferrer"&gt;Clean architecture&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>development</category>
      <category>architecture</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>HybridCache in a console application with Redis</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 17 Sep 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/hybridcache-in-a-console-application-with-redis-4ih1</link>
      <guid>https://forem.com/ben-witt/hybridcache-in-a-console-application-with-redis-4ih1</guid>
      <description>&lt;p&gt;In modern applications, the efficient management of data and the avoidance of unnecessary database queries is essential. An effective way to achieve this is to use a HybridCache, which utilises both a MemoryCache and a Distributed Cache such as Redis. In this article, I will present an implementation of a HybridCache in a console-only application and explain when to query which cache to achieve optimal performance. In addition, I will discuss the importance of cache invalidation and explain how this can be solved in the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a HybridCache?
&lt;/h2&gt;

&lt;p&gt;A HybridCache combines several cache types in order to increase performance. In our case, we first access the fast, memory-based MemoryCache, and if no data is available there, we fall back on a distributed cache — in this case Redis. Only if neither cache returns a hit is a query sent to the database. This approach helps to minimise database queries and significantly reduce latency times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of the HybridCache
&lt;/h2&gt;

&lt;p&gt;I have created a console application that implements the HybridCache with Microsoft.Extensions.Caching.Memory and Redis. The following code shows the basic implementation where cache invalidation plays a role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class HybridCache : IHybridCache
{
    private readonly IMemoryCache _memoryCache;
    private readonly IDistributedCache _distributedCache;
    private static readonly object _lock = new object();  // Lock for thread-safety

    public HybridCache(IMemoryCache memoryCache, IDistributedCache distributedCache)
    {
        _memoryCache = memoryCache;
        _distributedCache = distributedCache;
    }

    public async Task&amp;lt;T&amp;gt; GetOrSetAsync&amp;lt;T&amp;gt;(string key, Func&amp;lt;Task&amp;lt;T&amp;gt;&amp;gt; factory, TimeSpan duration)
    {
        T value;

        // Check MemoryCache first
        lock (_lock)
        {
            if (_memoryCache.TryGetValue(key, out value))
            {
                string valueString = value.ToString();
                valueString = ReplaceSourceInfo(valueString, "MemoryCache");
                value = (T)Convert.ChangeType(valueString, typeof(T));
                return value;
            }
        }

        // Check Distributed Cache (Redis)
        var cachedData = await _distributedCache.GetStringAsync(key);
        if (cachedData != null)
        {
            value = System.Text.Json.JsonSerializer.Deserialize&amp;lt;T&amp;gt;(cachedData);

            string valueString = value.ToString();
            valueString = ReplaceSourceInfo(valueString, "Distributed Cache");
            value = (T)Convert.ChangeType(valueString, typeof(T));

            // Set the value in MemoryCache
            lock (_lock)
            {
                _memoryCache.Set(key, value, absoluteExpirationRelativeToNow: duration);
            }

            return value;
        }

        // Query the database if not found in any cache
        value = await factory();

        // Save the data to both MemoryCache and Redis
        string newValue = ReplaceSourceInfo(value.ToString(), "Database");
        value = (T)Convert.ChangeType(newValue, typeof(T));

        var serializedValue = System.Text.Json.JsonSerializer.Serialize(value);
        await _distributedCache.SetStringAsync(key, serializedValue, new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = duration  // Set expiration time for Redis
        });

        lock (_lock)
        {
            _memoryCache.Set(key, value, duration);  // Set expiration time for MemoryCache
        }

        return value;
    }

    public void InvalidateCache(string key)
    {
        // Remove key from MemoryCache
        lock (_lock)
        {
            _memoryCache.Remove(key);
        }

        // Remove key from Distributed Cache (Redis)
        _distributedCache.Remove(key);
    }

    private string ReplaceSourceInfo(string originalValue, string source)
    {
        string patternToRemove = @"\(Source:.*?\)";
        string updatedValue = System.Text.RegularExpressions.Regex.Replace(originalValue, patternToRemove, "");
        return $"{updatedValue} (Source: {source})";
    }

    public async Task&amp;lt;T&amp;gt; GetAsync&amp;lt;T&amp;gt;(string key)
    {
        // Try getting the value from MemoryCache first
        lock (_lock)
        {
            if (_memoryCache.TryGetValue(key, out T value))
            {
                return value;
            }
        }

        // If not found, get the value from Distributed Cache
        var cachedData = await _distributedCache.GetStringAsync(key);
        return cachedData != null ? System.Text.Json.JsonSerializer.Deserialize&amp;lt;T&amp;gt;(cachedData) : default;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cache invalidation
&lt;/h2&gt;

&lt;p&gt;In the above implementation, a simple time-based expiration is used to ensure that the cache entries in both the MemoryCache and the Redis Cache become invalid after a certain time. This is controlled by the AbsoluteExpirationRelativeToNow method, which determines when an entry expires automatically.&lt;br&gt;
In addition, I have added an InvalidateCache method that enables manual invalidation of the cache entries. With this method, the cache entry can be explicitly deleted from both the MemoryCache and Redis if, for example, the underlying data in the database has been changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is which cache used?
&lt;/h2&gt;

&lt;p&gt;In this implementation, the HybridCache ensures that queries are made according to a clear hierarchy. MemoryCache is queried first, as it is faster. If no data is found there, the cache checks Redis. Redis is favoured in distributed systems as it can serve multiple instances of the application and therefore provides a central source for the cache data. Finally, if both caches fail, the database is queried.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extended strategies for cache invalidation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;manual/explicit invalidation&lt;/strong&gt;: As shown in the code, there is an InvalidateCache method that can be used to manually remove cache entries. This is useful when data in the database changes and the cache contains outdated entries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;event-based invalidation&lt;/strong&gt;: In distributed systems, an event-based model could be used to invalidate cache entries. If the data changes, a message could be sent (e.g. via a message queue system) that invalidates the cache entry in several instances simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sliding expiration&lt;/strong&gt;: Another possible strategy is sliding expiration, in which the expiration time of a cache entry is extended with each access. This ensures that frequently used data remains in the cache for longer, while unused data expires after a certain time.
An example of sliding expiration could look like this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var cacheEntryOptions = new DistributedCacheEntryOptions
{
    SlidingExpiration = TimeSpan.FromMinutes(5)  // Expiration is reset after each access
};

await _distributedCache.SetStringAsync(key, serializedValue, cacheEntryOptions);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Redis - locally or via Docker
&lt;/h2&gt;

&lt;p&gt;Redis is used as a distributed cache for this application. Redis can be installed in various ways. An easy way to install Redis locally is to download the Redis binaries from the official website (&lt;a href="https://redis.io/download" rel="noopener noreferrer"&gt;https://redis.io/download&lt;/a&gt;). Once Redis is installed, it can be executed on the local host on the standard port 6379.&lt;br&gt;
Alternatively, Redis can also be run via Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other options - e.g. with MS SQL Server
&lt;/h2&gt;

&lt;p&gt;In addition to Redis, there are also other options for implementing a distributed cache. One widespread option is the use of an SQL-based cache, e.g. with Microsoft SQL Server. This solution is often used in environments where there is already a strong integration with SQL Server-based systems.&lt;/p&gt;

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

&lt;p&gt;The implementation of a HybridCache in a console application offers numerous advantages, especially when it comes to reducing latency times and database queries. By combining a fast memory-based cache such as MemoryCache and a distributed cache such as Redis, application performance can be significantly increased. The introduction of thread safety and proper synchronisation ensures that parallel requests do not cause inconsistencies. Furthermore, cache invalidation is an important aspect to ensure that only up-to-date data is used and should be customised to the needs of the application.&lt;br&gt;
With Redis as a locally installed cache - or even as a Docker container - and the right cache hierarchy, scalable and high-performance applications can be implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  DEMO:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/WittBen/HybridCache?source=post_page-----71083901f99f--------------------------------" rel="noopener noreferrer"&gt;Github: HybridCache&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>Implementing the Cached Repository Pattern in C#</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 23 Jul 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/cached-repository-in-c-432c</link>
      <guid>https://forem.com/ben-witt/cached-repository-in-c-432c</guid>
      <description>&lt;h4&gt;
  
  
  Introduction to the Concept of a Cached Repository
&lt;/h4&gt;

&lt;p&gt;A cached repository is a design pattern aimed at enhancing application performance by storing data in a fast-access memory area known as a cache. This reduces the number of database accesses, thereby improving response times and the application's scalability. A repository abstracts data access and provides uniform interfaces for CRUD operations (Create, Read, Update, Delete). Combining these concepts offers a powerful method for optimizing data access patterns in modern applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Importance and Benefits of Using Cached Repositories in C# for Advanced Developers
&lt;/h4&gt;

&lt;p&gt;For advanced developers, cached repositories offer several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance Improvement&lt;/strong&gt;: Reducing database accesses significantly enhances response times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Lower database load facilitates better application scalability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Reduction&lt;/strong&gt;: Fewer database queries translate to lower costs, especially with cloud services billed per query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency and Abstraction&lt;/strong&gt;: Using a uniform repository ensures consistent data access and allows for easy abstraction and testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Detailed Implementation of a Cached Repository in C# Using the Decorator Pattern and EF Core
&lt;/h4&gt;

&lt;p&gt;Implementing a cached repository can be effectively achieved through the decorator pattern. This pattern allows additional functionality to be added to an object without altering its structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Define the Repository Interface&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IProductRepository&lt;/span&gt;
&lt;span class="p"&gt;{&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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAllProductsAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;DeleteProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Implement the Base Repository with EF Core&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IProductRepository&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;ApplicationDbContext&lt;/span&gt; &lt;span class="n"&gt;_context&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;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ApplicationDbContext&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="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="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;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAllProductsAsync&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;await&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;DeleteProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;product&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&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;&lt;strong&gt;Implement the Cached Repository&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachedProductRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IProductRepository&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;IProductRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&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;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;_cache&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;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;_cacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CachedProductRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IProductRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Product_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;product&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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProductByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_cacheDuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAllProductsAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AllProducts"&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;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;products&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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAllProductsAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_cacheDuration&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;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;InvalidateCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;InvalidateCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;DeleteProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeleteProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;InvalidateCache&lt;/span&gt;&lt;span class="p"&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;void&lt;/span&gt; &lt;span class="nf"&gt;InvalidateCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllProducts"&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;h4&gt;
  
  
  Best Practices and Potential Pitfalls in Using Cached Repositories in C#
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache Invalidation&lt;/strong&gt;: Ensure the cache is invalidated after write operations (Add, Update, Delete) to maintain consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Duration&lt;/strong&gt;: Choose an appropriate cache duration to balance freshness and performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Management&lt;/strong&gt;: Avoid overloading the cache, especially in memory-intensive applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Potential Pitfalls:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stale Data&lt;/strong&gt;: Cached data can become outdated, leading to inconsistencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Implementing and managing cached repositories increases codebase complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Consumption&lt;/strong&gt;: Excessive caching can lead to high memory usage and potential out-of-memory issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Comparison with Other Caching Strategies and Their Applications
&lt;/h4&gt;

&lt;p&gt;In addition to the decorator pattern for cached repositories, there are several other caching strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Caching&lt;/strong&gt;: Direct use of in-memory data stores like &lt;code&gt;IMemoryCache&lt;/code&gt; or &lt;code&gt;ConcurrentDictionary&lt;/code&gt;. Ideal for short-term, small data sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Caching&lt;/strong&gt;: Use of distributed caches like Redis or Memcached. Suitable for applications with high scalability requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Caching&lt;/strong&gt;: Use of HTTP headers to cache web resources. Ideal for high-traffic web applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each strategy has specific use cases and challenges that need to be carefully evaluated.&lt;/p&gt;

&lt;h4&gt;
  
  
  Advanced Topics: Cache Invalidation and Synchronization Between Cache and Database
&lt;/h4&gt;

&lt;p&gt;Cache invalidation and synchronization are complex topics that require special attention:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Invalidation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time-to-Live (TTL)&lt;/strong&gt;: Set a TTL for cache entries to ensure automatic invalidation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-Based Invalidation&lt;/strong&gt;: Use events or message queues to synchronize cache invalidations in distributed systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Synchronization Between Cache and Database:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write-Through Caching&lt;/strong&gt;: Write operations are performed on both the database and the cache, ensuring consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write-Behind Caching&lt;/strong&gt;: Write operations are initially performed on the cache and later synchronized with the database. This can improve performance but carries the risk of data inconsistency in the event of a crash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Priming&lt;/strong&gt;: Preload frequently accessed data into the cache at application startup to avoid initial latencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A comprehensive understanding and correct implementation of these techniques are crucial for successfully leveraging cached repositories in demanding applications.&lt;/p&gt;

&lt;p&gt;In summary, cached repositories, combined with the decorator pattern and Entity Framework Core, offer an effective method for optimizing data access patterns. They provide significant performance benefits but require careful implementation and management to avoid potential pitfalls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/WittBen/CachedRepository" rel="noopener noreferrer"&gt;https://github.com/WittBen/CachedRepository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>development</category>
    </item>
    <item>
      <title>Artificial Intelligence with ML.NET for text classifications</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 16 Jul 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/artificial-intelligence-with-mlnet-for-text-classifications-42j6</link>
      <guid>https://forem.com/ben-witt/artificial-intelligence-with-mlnet-for-text-classifications-42j6</guid>
      <description>&lt;p&gt;Recently, Artificial Intelligence (AI) has been gaining popularity at breakneck speed.&lt;br&gt;
OpenAI’s ChatGPT was a breakthrough in Artificial Intelligence, and the enthusiasm was huge.&lt;br&gt;
ChatGPT triggered a trend towards AI applications that many companies followed.&lt;br&gt;
You read and hear about AI everywhere. Videos and images of celebrities performing strange dance moves appear, or you hear interviews and songs by artists who have “actually” been dead for several years.&lt;br&gt;
The exact functioning of Artificial Intelligence will be explained below. As developers, we are used to thinking in categories and breaking problems down into small steps, and this is exactly how all Artificial Intelligence works. They are based on so-called models that have been trained for their respective areas of application.&lt;br&gt;
&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
If you ask ChatGPT for a picture, a separate model such as DALL-E must be used.&lt;br&gt;
If you ask for a poem, ChatGPT uses a language model.&lt;br&gt;
ChatGPT is nothing more than a ChatBot that uses different models for the task at hand.&lt;br&gt;
This is where I would like to start and program a small Artificial Intelligence that classifies a customer review, which is available as free text, as positive or negative.&lt;br&gt;
As a .NET developer, Microsoft offers a low-threshold entry into the world of machine learning with ML.NET. This free open-source framework makes it easy to integrate ML models into new and existing .NET applications — without any knowledge of Python, R, or other ML libraries. ML.NET already contains many ready-made models for common use cases and can be easily integrated into the existing .NET app lifecycle.&lt;br&gt;
Basics of machine learning&lt;br&gt;
The term “machine learning” describes the approach of teaching computers to learn patterns and relationships from data instead of explicitly programming them with rules and program logic. The aim is to enable the machine to “learn” independently from sample data and apply this knowledge to solve similar problems.&lt;br&gt;
There are essentially three types of learning:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supervised learning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Supervised learning is a method of machine learning in which a model is provided with training data consisting of input data (e.g. texts) and the corresponding desired outputs (e.g. classifications such as ‘positive’ or ‘negative’). Using these examples, the system learns to recognize patterns that relate the inputs to the outputs. This enables it to later predict correct outputs for new, unseen inputs. A classic application example is object recognition in images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
An application example for supervised learning with ML.NET could be the classification of product reviews as ‘positive’ or ‘negative’. The procedure would be as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Loading the training data with reviews and labels
var data = mlContext.Data.LoadFromTextFile&amp;lt;ReviewData&amp;gt;(pathToFile);
// Creating the processing pipeline
var pipeline = mlContext.Transforms.Text.FeaturizeText("ReviewText", "Features")
                        .Append(mlContext.BinaryClassification.Trainers.SdcaLogisticRegression("Label"));
// Training the model            
var model = pipeline.Fit(data);


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

&lt;/div&gt;

&lt;p&gt;At this point, we have a trained model that can classify new reviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unsupervised learning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unsupervised learning is a machine learning approach in which the model is only provided with unlabelled input data, e.g. text or images, without any associated target variables or classifications. The system must independently recognize structures, patterns, and correlations in this raw data. A typical area of application is clustering, in which similar input data is summarised into groups. Possible examples are the identification of customer segments based on marketing data or the grouping of news articles according to subject areas when analyzing text.&lt;br&gt;
&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
Unsupervised learning can be used with ML.NET for text clustering, for example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Loading the text data
var data = mlContext.Data.LoadFromTextFile&amp;lt;ArticleData&amp;gt;(pathToFile);
// Create the text featurisation pipeline
var pipeline = mlContext.Transforms.Text.FeaturizeText("ArticleText", "Features");
// Train the clustering model
var model = mlContext.Clustering.Trainers.KMeans(pipeline, numberOfClusters: 5).Fit(data);
// Predict the cluster membership for new articles
var predictions = model.Transform(data);


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

&lt;/div&gt;

&lt;p&gt;In this way, for example, news articles can be automatically categorized into thematic clusters such as politics, business, sports, etc.&lt;br&gt;
ML.NET focuses mainly on supervised and partially unsupervised learning. ML.NET does not currently offer any specific support for reinforcement learning, in which an AI is trained through rewards in a simulated environment. This learning paradigm is used in particular for strategy-finding tasks, such as games or the optimization of processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reinforcement Learning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reinforcement learning is a learning paradigm in which Artificial Intelligence is trained by trial and error in a simulated environment. In contrast to supervised learning, there are no complete training examples with input-output pairs. Instead, the system receives a score (reward) for each action, which indicates how “good” this action was. By maximizing the accumulated rewards, the AI learns to find the optimal strategy for a task. This principle of reinforcement learning is used in particular for tasks that require a complex strategy to be found. Examples include board games such as chess or Go, but also applications for robot control, process optimization, or autonomous driving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
A classic application example is a chess player agent. Here the environment could be a chessboard, possible actions are move movements. The agent then receives an evaluation for each move, e.g. +1 for a sure pawn win, -5 for an impending piece loss. Through many training games, the AI learns which moves to lead to a win in the long term and thus maximizes its cumulative reward.&lt;br&gt;
Set up the development environment&lt;br&gt;
To get started with ML.NET, we need the .NET Core or .NET Framework environment in version 4.6.1 or higher. ML.NET can be used in Visual Studio, Visual Studio Code, or the command line.&lt;/p&gt;

&lt;p&gt;The first step is to install the required NuGet packages of ML.NET:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Install-Package Microsoft.ML


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

&lt;/div&gt;

&lt;p&gt;This main package contains basic functionality such as the ML.NET context, data transformations, and catalogs with models and tasks. For additional functionality, other packages such as Microsoft.ML.Vision for image processing or Microsoft.ML.Recommender for recommender systems can be added.&lt;br&gt;
After installation, we can create a .NET Core or .NET Framework console application in Visual Studio and start development.&lt;br&gt;
Optionally, integrated tools such as the Model Builder interface in Visual Studio or the ML.NET CLI can be used to visualize data and create models. I’ll come back to this later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the first ML model
&lt;/h2&gt;

&lt;p&gt;In the following, the basic concepts of ML.NET are explained step by step using an example:&lt;br&gt;
The classification of customer reviews is based on free text.&lt;br&gt;
The result should be to categorize these ratings as positive or negative.&lt;br&gt;
This is a classic supervised learning problem.&lt;br&gt;
We create a new C# console application and import the ML.NET NuGet packages.&lt;br&gt;
Next, we load the data set into the working memory. In our example, we need a TSV (tab-separated file) file for this. This is represented by a simple class in our console application:&lt;br&gt;
This class stands for each line within the TSV file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class ReciewData
{
}


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

&lt;/div&gt;

&lt;p&gt;Now we add properties for each column of the TSV data:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class ReviewData   
{    
 public string ReviewText { get; set; }    
 public bool Label { get; set; }   
}


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

&lt;/div&gt;

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

&lt;p&gt;Unfortunately, this is not enough to load this TSV file and teach ML.Net how to reference this file.&lt;br&gt;
We need attributes for this: LoadColumn plus the index of the column.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class ReviewData
{
  [LoadColumn(0)]
  public string ReviewText { get; set; }

 [LoadColumn(1)]
  public bool Label { get; set; }
}


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

&lt;/div&gt;

&lt;p&gt;This is a simple mapping from the ReviewText property to the TSV file ReviewText column.&lt;br&gt;
Now let’s take care of the storage paths for the files and save them in our application:&lt;br&gt;
First, we create the path to our training file, the TSV file (Review.tsv).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string baseDirectory = @"..\..\AI"; 
string subDirectory = "TrainingData";

string trainingDataFilePath = Path.Combine(baseDirectory, subDirectory, "Reviews.tsv");


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

&lt;/div&gt;

&lt;p&gt;We will use this file to train the model that we want to use for classification.&lt;br&gt;
Now we define the path to the model that we want to train. This model is saved as a ZIP file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

string modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "model.zip");


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

&lt;/div&gt;

&lt;p&gt;Now we need a variable for our MLContext. The MLContext is comparable to an EF DbContext. So, whenever you want to work with machine learning, you have to interact with the MLContext, similar to the DBContext in EF Core.&lt;br&gt;
Now we are ready to load the data. We can manipulate it as we need it to train the model according to our requirements.&lt;br&gt;
Now we build our model:&lt;br&gt;
For this, we need a specific type from ML.NET. The IDataView:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

var trainingDataView = mlContext.Data.LoadFromTextFile&amp;lt;ReviewData&amp;gt;(trainingDataFilePath, separatorChar: '\t', hasHeader: true);


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

&lt;/div&gt;

&lt;p&gt;Here I can now use a function from the MLContext called LoadFromTextFile. &lt;code&gt;&amp;lt;ReviewData&amp;gt;&lt;/code&gt; is now used to project the data into our initially created class. The path to the training file must now be passed to this function. As a bonus, we can specify further options here, such as the separator from the TSV file and whether our training file has a header or not (we have a header, so I set this parameter to true).&lt;br&gt;
Now that we have loaded the data into a DataView, we can start preprocessing. Preprocessing is a means of creating what is called a pipeline. This can later be used to perform predictions when we have incoming data (e.g. our customer score). To do this, we need to extract the data from the data view we have created (IDataView) and then transform it so that it can be used as part of the machine-learning process.&lt;/p&gt;

&lt;p&gt;Now let’s create our pipeline. This has a specific return type: &lt;strong&gt;IEstimator&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

/// &amp;lt;summary&amp;gt;
/// Creates the machine learning pipeline for review classification.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name="context"&amp;gt;The ML context.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;The machine learning pipeline.&amp;lt;/returns&amp;gt;
public static IEstimator&amp;lt;ITransformer&amp;gt; CreatePipeline(MLContext context)
{
  return context.Transforms.Text.FeaturizeText("Features", "ReviewText")
      .Append(context.BinaryClassification.Trainers.SdcaLogisticRegression(labelColumnName: "Label"))
      .AppendCacheCheckpoint(context);
}



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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Context.Transforms.Text.FeaturizeText&lt;/strong&gt; is a text transformation component in ML.NET that is used to convert text data into numerical features.&lt;br&gt;
“&lt;strong&gt;Features&lt;/strong&gt;” is the new column name that contains the resulting numerical characteristics.&lt;br&gt;
„&lt;strong&gt;ReviewText&lt;/strong&gt;” is the name of the column from the TSV file that contains the text to be converted into numerical characteristics.&lt;br&gt;
Now the training of the model begins:&lt;br&gt;
.&lt;strong&gt;Append&lt;/strong&gt; adds the next step to the pipeline. The model is trained with &lt;strong&gt;Context.BinaryClassification.Trainers.SdcaLogisticRegression&lt;/strong&gt;, which defines the algorithm with which the model is to be trained — in this case the &lt;strong&gt;SdcaLogisticRegression&lt;/strong&gt; algorithm. “&lt;strong&gt;Label&lt;/strong&gt;” is the column or the name of the column that represents the target variable to be predicted. In our example, it is the classification of whether this evaluation is positive or negative.&lt;/p&gt;

&lt;p&gt;Let’s summarise this again:&lt;/p&gt;

&lt;p&gt;This command builds a pipeline that performs the following steps:&lt;br&gt;
text featurisation: the text from the “&lt;strong&gt;ReviewText&lt;/strong&gt;” column is converted into numerical features and stored in the new “Features” column.&lt;br&gt;
model training: The numerical features in the “Feature” column are used to train a binary classification model with the target variable in the “&lt;strong&gt;Label&lt;/strong&gt;” column.&lt;br&gt;
Now we have created our pipeline and can start training the model.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

var pipeline = CreatePipeline(mlContext);
var model = pipeline.Fit(trainingDataView);


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

&lt;/div&gt;

&lt;p&gt;We use the &lt;strong&gt;pipeline.Fit(…)&lt;/strong&gt; command to train the model on the data from the TSV file. The model is then stored in memory.&lt;br&gt;
However, to save the model and not have to create and retrain it each time, we can use the &lt;strong&gt;Save&lt;/strong&gt;() method. This allows us to save the trained model in a specific file path. In this way, we can simply load and use the model when needed without having to retrain it each time.&lt;br&gt;
There is an option in MlContext where we can find the Save() function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

context.Model.Save(model, schema, modelPath);


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

&lt;/div&gt;

&lt;p&gt;Firstly, we transfer the trained model and the schema from the IDataView from the TSV file, and then the path where we want to save the model.&lt;/p&gt;

&lt;p&gt;We have now trained our model and saved it to the hard disc.&lt;br&gt;
Now we need another class that represents the trained DataSet, i.e. the results from our model (prediction class). To do this, we create another class that has a similar structure to the class for the DataSet from the TSV file (ReviewData).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public class ReviewPrediction
  {
    [ColumnName("PredictedLabel")]
    public bool PredictedLabel { get; set; }
  }


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

&lt;/div&gt;

&lt;p&gt;The attribute defines that the name of the “PredictedLabel” column is to be used in the MLContext to read the predicted value there.&lt;/p&gt;

&lt;p&gt;To connect the model to the prediction class, we now need a PipelineEngine. We create this engine again from the MLContext.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

var predictionEngine = mlContext.Model.CreatePredictionEngine&amp;lt;ReviewData, ReviewPrediction&amp;gt;(model);


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

&lt;/div&gt;

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

&lt;p&gt;We use the generic types TSrc (Source) and TDst (Destination) for our input and output classes, “&lt;strong&gt;ReviewData&lt;/strong&gt;” and “&lt;strong&gt;ReviewPrediction&lt;/strong&gt;” (“Good rating”, “Positiv”).&lt;/p&gt;

&lt;p&gt;The individual components and their interaction should now be understandable: &lt;br&gt;
Why we need to convert texts into numerical features, why we need to prepare data so that the computer can understand it, and that our “&lt;strong&gt;ReviewData&lt;/strong&gt;” class is our source class and “&lt;strong&gt;ReviewDataPrediction&lt;/strong&gt;” is our target class.&lt;/p&gt;

&lt;p&gt;Now that we have completed the preparation steps, we can move on to the prediction phase.&lt;br&gt;
Let’s now take care of the code that performs the prediction and returns the expected target variable.&lt;br&gt;
In our example, the predicted result is a Boolean value.&lt;/p&gt;

&lt;p&gt;The first step is to enter a score, which we then write to our “&lt;strong&gt;ReviewData&lt;/strong&gt;” input class.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

var input = new ReviewData { ReviewText = inputReviewText };


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

&lt;/div&gt;

&lt;p&gt;The variable “&lt;strong&gt;ReviewText&lt;/strong&gt;” is a string variable that contains our review.&lt;br&gt;
We now pass this input variable of type “&lt;strong&gt;ReviewData&lt;/strong&gt;” to the pipeline that we have just created.&lt;br&gt;
As a result, we receive our “ReviewPrediction” output class and can now access the target variable from the model via the variable.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Classify the new rating and display the result
var prediction = predictionEngine.Predict(input);
Console.WriteLine($"The classification for the rating ‘{ input.ReviewText}’ is: { (prediction.PredictedLabel ? "Positiv" : "Negativ")}");


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

&lt;/div&gt;

&lt;p&gt;If the model had good training data, we should now get the correct predicted results.&lt;/p&gt;

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

&lt;p&gt;It seems to work.&lt;/p&gt;

&lt;p&gt;Of course, this is only a small example, and the model is not perfect, but if you train it well, it should ideally always return correct results.&lt;br&gt;
The following extensions have been added to the example to retrain the model if inputs were incorrect or unknown.&lt;br&gt;
When starting the application, you can choose between adding new data to the model and direct training or you can create new comments.&lt;br&gt;
In the following article, I will take a closer look at image recognition and prediction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary:
&lt;/h2&gt;

&lt;p&gt;Although this article is limited to text classification, on closer inspection, and with a little creativity, the approach described opens up numerous possibilities. For example, the recognition of a certain classification could be used to trigger further processes automatically.&lt;br&gt;
Overall, it can be said that Artificial Intelligence has enormous potential to support us in many areas of everyday life and to optimize processes. Text classification is just one example of the many possible applications. It can be assumed that more and more areas will benefit from the possibilities of AI in the future and that this will open up new opportunities for companies and private individuals alike.&lt;br&gt;
To fully exploit this potential, it is important that we are open to new technologies and that we address the possibilities and limitations of AI. This is the only way we can ensure that Artificial Intelligence is used responsibly and for the benefit of all in the future.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/WittBen/AIUnderCSharp" rel="noopener noreferrer"&gt;https://github.com/WittBen/AIUnderCSharp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;br&gt;
ML.wNET | Machine learning made for .NET&lt;br&gt;
What is ML.NET and how does it work? — ML.NET&lt;/p&gt;

</description>
      <category>ai</category>
      <category>csharp</category>
      <category>coding</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>The Chain of Responsibility</title>
      <dc:creator>Ben Witt</dc:creator>
      <pubDate>Tue, 09 Jul 2024 08:00:00 +0000</pubDate>
      <link>https://forem.com/ben-witt/the-chain-of-responsibility-1pl4</link>
      <guid>https://forem.com/ben-witt/the-chain-of-responsibility-1pl4</guid>
      <description>&lt;p&gt;&lt;strong&gt;The essence of the Responsibility of Chain pattern&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;The Chain of Responsibility pattern is a design pattern that allows code to be structured to route requests through a series of handlers. Each handler has the ability to process a request or pass it on to the next handler in the chain. This flexibility facilitates the handling of complex requests in a modular and scalable way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The basics in detail:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To fully grasp the pattern, an understanding of how it works is necessary. Here we will highlight the mechanism of the responsibility-of-chain pattern and compare it to other common approaches such as if-else statements to highlight its strengths and potential uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation in C#:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let’s get down to business and implement the pattern in C#. Step by step, we will be guided through the creation of handler classes, their linking to a chain and the handling of requests through this chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical application&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;To reinforce what we have learned, we present a practical example. Here we illustrate the pattern using a concrete scenario, show its implementation and carry out tests to demonstrate its effectiveness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced concepts and subtleties:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Looking outside the box reveals advanced concepts and optimizations of the Responsibility of Chain pattern. We discuss ways to improve the implementation, the dynamic adaptation of the chain at runtime as well as effective error handling and traceability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, we summarize the key points and give advice for further deepening and applying the Chain of Responsibility pattern in your own projects.&lt;br&gt;
Ready to unleash the power of the Chain of Responsibility pattern? Then let’s dive into the world of responsibility chains together and take your C# development to a new level.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is the Chain of Responsibility pattern?
&lt;/h2&gt;

&lt;p&gt;The Chain of Responsibility pattern, an established design pattern in software development, acts as a proven tool for the efficient processing of requests or events. Its strength lies in the elegant decoupling of the requester from the recipients by providing a dynamic chain of potential recipients. Each recipient in this chain is able to decide independently whether it can handle the request adequately or forward it appropriately to the next recipient. This flexible structure ensures effective and modular processing of requests, which significantly improves both the maintainability and extensibility of software solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purpose of the pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Decoupling of sender and recipient: The Chain of Responsibility pattern enables a clean separation between the sender of a request and the potential recipients. This avoids the need for the sender to have specific knowledge about the recipients, as the responsibility for processing the request lies within the chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility and expandability:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;By structuring it as a chain of recipients, the template offers a high degree of flexibility when adding new recipients or changing existing ones. These changes can be made without modifying the sender, which significantly improves the maintainability and expandability of the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoidance of hard-coded dependencies:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Chain of Responsibility pattern helps to avoid rigid dependencies by allowing the handling of requests to be configured dynamically. This means that the system remains open to change and can adapt to changing requirements without being tied to specific implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of the pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved maintainability:&lt;/strong&gt; The clear separation of sender and recipient leads to code that is easier to understand and maintain. This level of abstraction makes the code base cleaner and less prone to unexpected side effects during maintenance.&lt;br&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt;: Adding new recipients or changing the order of existing recipients is straightforward, as this can be done without making any adjustments to the sender. This keeps the code base flexible and open for future adaptations or extensions.&lt;br&gt;
&lt;strong&gt;Decoupling&lt;/strong&gt;: The individual components of the system are loosely coupled thanks to the Chain of Responsibility pattern. This enables improved reusability and testability of the components, as they can be developed, tested and maintained independently of each other.&lt;br&gt;
&lt;strong&gt;Use cases&lt;/strong&gt;:&lt;br&gt;
Processing of requests: The pattern is ideal for processing requests in different scenarios. For example, it could be used in an e-commerce application to validate payment methods depending on various criteria, such as the amount of the purchase or the selected currency.&lt;br&gt;
&lt;strong&gt;Event handling&lt;/strong&gt;: In a GUI framework, the Chain of Responsibility pattern can be used to process user interactions efficiently. Different components within the chain can respond to events such as mouse clicks or keystrokes, ensuring flexible and scalable handling of user actions.&lt;br&gt;
The pattern thus proves to be an extremely versatile tool for structuring complex processing logic in a wide variety of application areas, which can significantly increase the flexibility and maintainability of software systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand the basics:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to understand the The Chain of Responsibility pattern in depth, it is crucial to penetrate the fundamental workings of the pattern and relate them to other approaches. Such an understanding makes it possible to fully appreciate the strengths and potential of this design pattern.&lt;br&gt;
The The Chain of Responsibility pattern operates on the basis of a hierarchical sequence of handlers that receive a request and either handle it themselves or pass it on to the next handler in the chain. This process of processing takes place in turn until the request is successfully processed or the chain is exhausted.&lt;br&gt;
A key point of comparison is the contrast with other common approaches such as the use of if-else statements. While the latter represent a sequential and static decision structure in which each condition is explicitly defined in the code, the responsibility-of-chain pattern offers a dynamic and flexible alternative.&lt;br&gt;
By using a chain of handlers, the pattern enables an elegant decoupling of the sender from the receivers, creating a loosely coupled architecture. This level of abstraction not only facilitates the maintenance and extension of the code, but also promotes the reusability of the individual components.&lt;br&gt;
Overall, the Chain of Responsibility pattern enables efficient processing of requests or events in complex systems by providing a flexible and extensible structure. An in-depth understanding of how it works and a comparison with other approaches are therefore essential in order to recognize its full potential and make optimal use of it.&lt;/p&gt;
&lt;h2&gt;
  
  
  How does the Chain of Responsibility pattern work?
&lt;/h2&gt;

&lt;p&gt;The Chain of Responsibility pattern is divided into three main components that form the framework for its functionality:&lt;br&gt;
&lt;strong&gt;1. Handler (recipient):&lt;/strong&gt; Each handler embodies a potential recipient of a request or event. This component implements a method or interface for processing the request. If a handler is unable to handle the request, it forwards it to the next handler in the chain. This flexible structure makes it possible to dynamically distribute responsibility for the request between the handlers, depending on the specific requirements of the system.&lt;br&gt;
&lt;strong&gt;2. Chain&lt;/strong&gt;: The chain forms the structural basis of the pattern and consists of a series of handlers that are linked together. Each handler in the chain references the next handler, creating a sequential order. In this way, the request can be passed through the chain until a suitable handler is found that can process it successfully. This flexible forwarding functionality enables efficient and modular handling of requests in complex systems.&lt;br&gt;
&lt;strong&gt;3. Client (sender):&lt;/strong&gt; The client initiates the request process by creating a request and passing it to the first handler in the chain. An important feature of the pattern is that the client does not need to be aware of the specific handling of the request. This abstraction layer enables a clean separation between client and receivers, which increases the flexibility and maintainability of the system.&lt;br&gt;
Through the interaction of these three main components, the Chain of Responsibility pattern enables elegant and flexible handling of requests or events in complex software systems. It promotes the reusability, extensibility and maintainability of the code by providing a clear and modular structure that allows the processing logic to be easily adapted and extended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparison with other patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A frequent comparison between the Chain of Responsibility pattern and the use of if-else statements shows clear differences:&lt;br&gt;
If-Else statements: The request processing logic is embedded directly in the sender. This can lead to confusing and difficult to maintain code, especially with many conditions. Adding new conditions often requires changes to the sender, which makes maintenance more difficult.&lt;br&gt;
&lt;strong&gt;Chain of Responsibility pattern:&lt;/strong&gt; Here the processing logic is encapsulated in separate handler classes that are connected in a chain. The sender only has to send the request to the beginning of the chain without having to worry about the details of request processing. This promotes modularity and flexibility in the code.&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
To illustrate the concept of the Chain of Responsibility pattern, let’s look at a simple example from the field of authentication:&lt;br&gt;
Suppose we have a chain of authentication checks that must be run in sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;checking user authorizations.&lt;/li&gt;
&lt;li&gt;checking the validity of the password.&lt;/li&gt;
&lt;li&gt;verification of two-factor authentication
.
In this scenario, each check is represented as a handler in the chain. If a check fails, the request is forwarded to the next handler in the chain. This structure allows for flexible and extensible authentication logic, as new checks can easily be added or the order changed without changing the main authentication mechanism.
&lt;strong&gt;Summary&lt;/strong&gt;:
The Chain of Responsibility pattern presents an elegant solution for forwarding requests or events through a chain of handlers. This makes the code more flexible, maintainable and extensible. In the next section, we will take a closer look at the implementation of this pattern in C#.
&lt;strong&gt;Step 1:&lt;/strong&gt; Creating the handler classes for support staff
We create handler classes for processing support requests.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class SupportHandler
{
  protected SupportHandler NextHandler;
  public void SetNextHandler(SupportHandler handler)
  {
    NextHandler = handler;
  }
  public abstract void HandleRequest(SupportRequest request);
}
public class Level1SupportHandler : SupportHandler
{
  public override void HandleRequest(SupportRequest request)
  {
    // Verifying if Level-1 support can handle the request
    if (/* Condition for successful processing */)
    {
      Console.WriteLine("Support request successfully processed by Level-1 support.");
    }
    else if (NextHandler != null)
    {
      // Forwarding request to the next handler
      NextHandler.HandleRequest(request);
    }
    else
    {
      Console.WriteLine("No support personnel could handle the request.");
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Linking the handlers to a chain&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Similarly, implement the other handler classes for level 2 support, level 3 support, etc.

//We create a chain of support staff.
public class SupportChain
{
  private SupportHandler _firstHandler;
  public SupportChain()
  {
    // The order of the handlers is defined here
    _firstHandler = new Level1SupportHandler();
    _firstHandler.SetNextHandler(new Level2SupportHandler());
    _firstHandler.SetNextHandler(new Level3SupportHandler());
    // Further handlers can be added here
  }
  public void ProcessSupportRequest(SupportRequest request)
  {
    // Send request to the beginning of the chain
    _firstHandler.HandleRequest(request);
  }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Processing support requests through the chain&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//We use the created chain to process support requests.
public class SupportClient
{
  public void ProcessSupport()
  {
    SupportChain chain = new SupportChain();
    SupportRequest request = new SupportRequest(/* Details of the support request */);
    chain.ProcessSupportRequest(request);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, support requests are routed through a chain of support employees. Each employee checks whether they can process the request and forwards it to the next employee if necessary. This structure enables efficient processing of support requests and facilitates the expansion of the system with additional support levels or functions.&lt;br&gt;
&lt;strong&gt;Practical example:&lt;/strong&gt;&lt;br&gt;
To further illustrate the responsibility-of-chain pattern, we will now implement a simple practical example. In this scenario, we will create an application to handle customer support requests.&lt;br&gt;
In this application, support requests are routed through a chain of support employees. Each employee checks whether they can process the request and forwards it to the next employee if necessary. This enables efficient processing of support requests and easy expansion of the system with additional support levels or functions.&lt;br&gt;
&lt;strong&gt;Scenario:&lt;/strong&gt;&lt;br&gt;
For our application, we plan to receive support requests from customers and forward them to different support levels according to their urgency. The support employees are to be organized in a chain, with each employee having the option of processing the request or forwarding it to the next employee in the chain.&lt;br&gt;
To implement this, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a chain of support agents.&lt;/li&gt;
&lt;li&gt;each employee in the chain should have a method to handle a support request or pass it on to the next employee.&lt;/li&gt;
&lt;li&gt;the application should provide a way to receive support requests and forward them to the first employee in the chain.
By implementing this scenario, we will see the Responsibility of Chain pattern in action and how it provides an efficient and flexible solution for handling support requests.
Implementation:
We will carry out the implementation in C#.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Definition of the support request
public class SupportRequest
{
  public string CustomerName { get; set; }
  public string RequestDetails { get; set; }
  public int Priority { get; set; }
  // Further relevant properties can be added here
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Abstract handler class for support agents
public abstract class SupportHandler
{
  protected SupportHandler NextHandler;
  public void SetNextHandler(SupportHandler handler)
  {
    NextHandler = handler;
  }
  public abstract void HandleRequest(SupportRequest request);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Handler class for level 1 support
public class Level1SupportHandler : SupportHandler
{
  public override void HandleRequest(SupportRequest request)
  {
    if (request.Priority &amp;lt;= 3) // Example priority check
    {
      Console.WriteLine($"Support request from {request.CustomerName} processed by level 1 support.");
    }
    else if (NextHandler != null)
    {
      NextHandler.HandleRequest(request);
    }
    else
    {
      Console.WriteLine("No support agent was able to process the request.");
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Handler class for level 2 support
public class Level2SupportHandler : SupportHandler
{
  public override void HandleRequest(SupportRequest request)
  {
    if (request.Priority &amp;lt;= 6) // Example priority check
    {
      Console.WriteLine($"Support request from {request.CustomerName} processed by level 2 support.");
    }
    else if (NextHandler != null)
    {
      NextHandler.HandleRequest(request);
    }
    else
    {
      Console.WriteLine("No support agent was able to process the request.");
    }
  }
}

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

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Handler-Klasse für Level-3-Support
public class Level3SupportHandler : SupportHandler
{
  public override void HandleRequest(SupportRequest request)
  {
    // Level-3-Support bearbeitet alle Anfragen, da es die letzte Ebene ist
    Console.WriteLine($"Supportanfrage von {request.CustomerName} vom Level-3-Support bearbeitet.");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Implementation of the clients
public class SupportClient
{
  public void ProcessSupportRequest(SupportRequest Anfrage)
  {
    SupportHandler chain = new Level1SupportHandler();
    chain.SetNextHandler(new Level2SupportHandler());
    chain.SetNextHandler(new Level3SupportHandler());
    chain.HandleRequest(request);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Application of the example:&lt;/strong&gt;&lt;br&gt;
Now we can use our example to process requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Program
{
  static void Main(string[] args)
  {
    SupportClient client = new SupportClient();
    // Create example request
    SupportRequest request = new SupportRequest
    {
      CustomerName = "John Doe",
      RequestDetails = "I am facing issues with my account login.",
      Priority = 5 // Example priority
    };
    // Process request
    client.ProcessSupportRequest(request);
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
Depending on the priority of the request, it is forwarded to the appropriate support level and processed accordingly. The Chain of Responsibility pattern enables flexible and scalable handling of support requests in a customer support system.&lt;br&gt;
Advanced concepts and optimizations:&lt;br&gt;
After looking at the basic understanding of the Chain of Responsibility pattern and its implementation in C#, we can turn to advanced concepts and optimizations to improve our solution. These include efficient chain traversal strategies, dynamic configuration of the support chain, improvements in error handling and optimization of resource usage. These measures help to increase the performance, efficiency and robustness of our application to meet the needs of our users.&lt;br&gt;
Dynamic Chain Adjustment at Runtime:&lt;br&gt;
One way to enhance the flexibility of our system is by dynamically adjusting the chain of support agents at runtime. This entails the ability to modify the order or composition of support agents as needed without altering the code.&lt;br&gt;
Through this dynamic adjustment, we can, for instance, add new support tiers, replace existing agents, or alter the prioritization of agents in the chain, all without the necessity of modifying the source code. This endows our system with high flexibility and adaptability to changing requirements or business scenarios.&lt;br&gt;
By implementing this dynamic adjustment, we can ensure that our support system consistently responds optimally to our customers’ needs, ensuring efficient handling of support inquiries even in rapidly changing environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class SupportClient
{
  private SupportHandler _chain;
  public SupportClient(SupportHandler initialHandler)
  {
    _chain = initialHandler;
  }

  public void SetSupportChain(SupportHandler handler)
  {
    _chain = handler;
  }

  public void ProcessSupportRequest(SupportRequest request)
  {
    _chain.HandleRequest(request);
  }
}
  public void ProcessSupportRequest(SupportRequest request)
  {
    _chain.HandleRequest(request);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementation of a Dynamic Support Chain Adjustment Mechanism:&lt;br&gt;
By implementing a mechanism for dynamically adjusting the support chain, the application can flexibly respond to changes in the support process without requiring modifications to the source code. This facilitates the adaptation of the support structure as needed, allowing for the addition of new support tiers, replacement of existing staff, or reordering of staff members. This approach ensures that the application remains agile and adaptable to meet evolving requirements and business scenarios.&lt;br&gt;
Error Handling and Traceability:&lt;br&gt;
Another crucial consideration is error handling and traceability within the chain. It is essential for the application to handle errors appropriately and provide the capability to trace the processing status of requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class SupportHandler
{
  // …
  public virtual void HandleRequest(SupportRequest request)
  {
    try
    {
      // Processing of the Request
    }
    catch (Exception ex)
    {
      Console.WriteLine($"Error occurs during the process: {ex.Message}");
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By implementing error handling in each handler, we can ensure that the application is resilient against unexpected errors. Furthermore, we can add traceability mechanisms, such as logging or appending additional information to the request.&lt;br&gt;
&lt;strong&gt;Further Optimizations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Implementation of mechanisms for parallel processing of requests in the chain to enhance performance.&lt;/li&gt;
&lt;li&gt;Use of Dependency Injection for easier configuration of the support chain and improved testability.&lt;/li&gt;
&lt;li&gt;Implementation of mechanisms for automatic adjustment of support priorities based on specific criteria.
Considering these advanced concepts and optimizations allows us to further enhance the flexibility, performance, and robustness of our system.
In this tutorial, we extensively covered the Chain of Responsibility pattern in C#. We explained its purpose, how it works, and its implementation through a practical example. Additionally, we examined advanced concepts and optimizations to improve the performance and flexibility of our solution. With this understanding, we are now able to effectively utilize the Chain of Responsibility pattern in our own projects and develop robust, flexible applications.
&lt;strong&gt;Summary&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;The Chain of Responsibility pattern allows requests or events to be passed through a chain of handlers, with each handler having the ability to process the request or pass it to the next handler.&lt;/li&gt;
&lt;li&gt;Using this pattern enables the separation of sender and receiver, leading to more flexible, maintainable, and extensible code.&lt;/li&gt;
&lt;li&gt;Implementing the pattern in C# involves creating handler classes and linking them to form a chain that processes requests.&lt;/li&gt;
&lt;li&gt;Advanced concepts such as dynamic adjustment of the chain at runtime and error handling enhance the flexibility and robustness of our solution.
&lt;strong&gt;Conclusions&lt;/strong&gt;:
The Chain of Responsibility pattern is undeniably a powerful tool in software development that can be employed in numerous application domains to structure complex processing logic. Through careful implementation and consideration of advanced concepts, we can develop flexible, robust, and high-performing systems. Applying this pattern enables clean separation of concerns, increased maintainability and extensibility of the code, and improved responsiveness to changing requirements. By mastering the principles of the Chain of Responsibility pattern and fully harnessing its potential, we can develop software solutions that meet the highest standards in terms of flexibility, robustness, and performance.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoft</category>
      <category>csharp</category>
      <category>development</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
