<?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>DEV Community: Welcome Bonginhlahla Sithole</title>
    <description>The latest articles on DEV Community by Welcome Bonginhlahla Sithole (@sitholewb).</description>
    <link>https://dev.to/sitholewb</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F312471%2F3126a0c6-8126-4e2a-93b0-c5b3d672752e.jpeg</url>
      <title>DEV Community: Welcome Bonginhlahla Sithole</title>
      <link>https://dev.to/sitholewb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sitholewb"/>
    <language>en</language>
    <item>
      <title>Meet AppPipe: The Lightweight, On-Premises Alternative to .NET Aspire</title>
      <dc:creator>Welcome Bonginhlahla Sithole</dc:creator>
      <pubDate>Sun, 21 Jun 2026 21:42:22 +0000</pubDate>
      <link>https://dev.to/sitholewb/meet-apppipe-the-lightweight-on-premises-alternative-to-net-aspire-33lo</link>
      <guid>https://dev.to/sitholewb/meet-apppipe-the-lightweight-on-premises-alternative-to-net-aspire-33lo</guid>
      <description>&lt;p&gt;Modern cloud-native developer environments are fantastic. Frameworks like &lt;strong&gt;.NET Aspire&lt;/strong&gt; have revolutionized local development by providing a unified developer dashboard, automatic service discovery, and OTLP telemetry collection. &lt;/p&gt;

&lt;p&gt;But what happens when it's time to deploy your microservice topology on-premises? &lt;/p&gt;

&lt;p&gt;If your target is &lt;strong&gt;IIS on Windows Server&lt;/strong&gt;, &lt;strong&gt;shared IIS hosting environments&lt;/strong&gt;, or a &lt;strong&gt;systemd service on Linux&lt;/strong&gt;, you've likely realized that deploying the standard .NET Aspire stack on-prem is a complex puzzle. There is no native hosting model for IIS, gRPC telemetry port mapping is fragile, and the dashboard's constant WebSocket connections can consume excessive resources on on-premises virtual machines.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;AppPipe.Hosting&lt;/strong&gt;—a lightweight, developer-friendly NuGet package designed specifically to bring the best features of .NET Aspire to your on-premises environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: On-Premises Microservice Orchestration is Hard
&lt;/h2&gt;

&lt;p&gt;While cloud platforms have native orchestration (like Kubernetes or ECS), traditional Windows and Linux environments still host a massive volume of enterprise applications. When running microservices on IIS or Linux servers, developers face three major friction points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Port Conflicts &amp;amp; Service Discovery&lt;/strong&gt;: Dynamically assigning ports to multiple microservices in IIS or systemd and injecting them into dependent services is tedious.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telemetry Aggregation&lt;/strong&gt;: Running an OpenTelemetry Collector just to aggregate traces, logs, and metrics for a small on-prem cluster is heavy and complex to configure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Exhaustion&lt;/strong&gt;: Standard Blazor Interactive Server dashboards maintain constant WebSockets and high memory usage, which quickly drains hosting environments.&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;&lt;strong&gt;AppPipe&lt;/strong&gt; is a lightweight alternative that integrates a routing gateway, a persistent telemetry database store, and a visual dashboard directly into a single library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Capabilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Service Discovery &amp;amp; Routing via YARP&lt;/strong&gt;: Under the hood, AppPipe uses Microsoft's &lt;strong&gt;YARP (Yet Another Reverse Proxy)&lt;/strong&gt;. It serves as a single entry point for your client, proxying traffic to backend microservices automatically using clean, virtual paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📊 OTLP Telemetry Collection&lt;/strong&gt;: AppPipe exposes a dedicated HTTP/2 Kestrel endpoint that acts as a local OTLP collector. Your microservices export standard traces, logs, and metrics directly to the AppPipe Gateway, which parses and displays them in the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🖥️ Gorgeous Blazor Dashboard&lt;/strong&gt;: View traces as waterfall flamegraphs, inspect console logs, and monitor metrics over time in a responsive Light/Dark theme dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;💾 Persistent Telemetry &amp;amp; Diagnostics&lt;/strong&gt;: Telemetry logs, traces, and metrics are persistent by default using an integrated local SQLite database, surviving gateway restarts and IIS application pool recycles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Dashboard Security&lt;/strong&gt;: Opt-in basic authentication protection to secure your logs and trace panels in intranet environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📈 Ingestion &amp;amp; Diagnostics Panel&lt;/strong&gt;: A dedicated diagnostics page showing real-time telemetry ingestion rates, active connections, database sizes, and host system information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Dual Render Modes&lt;/strong&gt;: Run the dashboard in full WebSocket &lt;strong&gt;InteractiveServer&lt;/strong&gt; mode for real-time updates during development, or swap to &lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt; mode in production to drastically reduce CPU and memory consumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🚀 Built-in On-Prem Deployment Pipeline&lt;/strong&gt;: Using &lt;code&gt;ModularPipelines&lt;/code&gt;, AppPipe provides automatic scripts to publish your services, spin up dedicated IIS Application Pools, configure Windows sub-applications, and inject service endpoints and telemetry tokens safely.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Scaffold Your Solution with .NET Templates
&lt;/h2&gt;

&lt;p&gt;To make starting a brand-new project simple, AppPipe includes an installable &lt;code&gt;.NET template&lt;/code&gt; package (&lt;code&gt;AppPipe.Hosting.Templates&lt;/code&gt;). It scaffolds a complete solution with a pre-configured AppHost orchestrator, an ApiService backend, and a Web frontend:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install the Template Pack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;AppPipe.Hosting.Templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Scaffold Your Solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new apppipe-system &lt;span class="nt"&gt;-n&lt;/span&gt; MySystem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a fully functional solution (&lt;code&gt;MySystem.sln&lt;/code&gt;) with the three pre-integrated projects. You can open and build it instantly, and it is ready to run locally or deploy to IIS.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Easy Is It to Use?
&lt;/h2&gt;

&lt;p&gt;Setting up AppPipe in your orchestration host project takes just a few lines of C#:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;span class="c1"&gt;// Configure the Gateway Dashboard Host Project&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppPipeHostingProjectResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithIISSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default Web Site"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPath&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AppPipeDashboardPool"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define a backend worker microservice using compile-safe generated constant&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendWorker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define a frontend API and declare its reference to BackendWorker&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FrontendApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Injects service discovery variables automatically&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Run the host using AppPipeDevHostRunner&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppPipeDevHostRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&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;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;br&gt;
&lt;strong&gt;Host Project SDK Requirement&lt;/strong&gt;: The host project (e.g. &lt;code&gt;MySystem.AppHost.csproj&lt;/code&gt;) &lt;strong&gt;must&lt;/strong&gt; use the &lt;code&gt;&amp;lt;Project Sdk="Microsoft.NET.Sdk.Web"&amp;gt;&lt;/code&gt; SDK. If it is configured as a standard console app SDK (&lt;code&gt;Microsoft.NET.Sdk&lt;/code&gt;), Razor compilation and Blazor routes will fail, leading to &lt;strong&gt;404 Not Found&lt;/strong&gt; errors when attempting to access the dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Inside your microservices, register the standard OpenTelemetry exporter:&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="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenTelemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracing&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Automatically routes to AppPipe telemetry port&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💾 Telemetry Database Persistence &amp;amp; Custom Stores
&lt;/h2&gt;

&lt;p&gt;By default, AppPipe persists all logs, traces, and metrics in a local SQLite database (&lt;code&gt;SqliteTelemetryStore&lt;/code&gt;), which is automatically created and hydrated on startup.&lt;/p&gt;

&lt;p&gt;If you are running in production and want to disable database persistence or plug in another database (such as PostgreSQL, SQL Server, or ClickHouse), you can implement the &lt;code&gt;ITelemetryStore&lt;/code&gt; interface and register it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AppPipe.Gateway.Services&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;interface&lt;/span&gt; &lt;span class="nc"&gt;ITelemetryStore&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ParsedTrace&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Traces&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ConcurrentQueue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ParsedLog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Logs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ConcurrentQueue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExportMetricsServiceRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Metrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExportTraceServiceRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExportLogsServiceRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExportMetricsServiceRequest&lt;/span&gt; &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;OnTelemetryReceived&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;h3&gt;
  
  
  Implementing and Configuring a Custom Database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write Your Implementation&lt;/strong&gt;: Create a class implementing &lt;code&gt;ITelemetryStore&lt;/code&gt; that persists traces, logs, and metrics to your chosen database (e.g. PostgreSQL, SQLite, or ClickHouse):
&lt;/li&gt;
&lt;/ol&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;SqliteTelemetryStore&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ITelemetryStore&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Implement parsing, writing, and querying logic here...&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExportTraceServiceRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Save to SQLite DB&lt;/span&gt;
        &lt;span class="n"&gt;OnTelemetryReceived&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register It in the DI Container&lt;/strong&gt;: Register your custom telemetry store with the gateway builder during startup:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AppPipeHostingApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the Gateway Dashboard Host Project&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppPipeHostingProjectResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/AppHost"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register custom Telemetry Store on the Gateway's DI container:&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gatewayBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;gatewayBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITelemetryStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SqliteTelemetryStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Run the host using AppPipeDevHostRunner&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppPipeDevHostRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&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;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Built for the Realities of On-Prem Hosting
&lt;/h2&gt;

&lt;p&gt;When deploying to production IIS environments, AppPipe features specific optimizations to ensure maximum reliability and low overhead:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. IIS Token Overwrite Filter
&lt;/h3&gt;

&lt;p&gt;IIS integration validates incoming requests using an &lt;code&gt;MS-ASPNETCORE-TOKEN&lt;/code&gt; pairing token. Since outgoing HTTP calls from your microservices carry mismatched pairing tokens from different AppPools, IIS natively rejects telemetry exports. AppPipe implements a custom startup filter at index 0 that overrides these headers for loopback connections on the telemetry port, allowing OTLP traffic to flow without compromising security.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. High-Performance SSR Mode
&lt;/h3&gt;

&lt;p&gt;To prevent Blazor Interactive Server from spawning idle WebSockets and consuming RAM on your servers, setting &lt;code&gt;Dashboard:UseWebSockets&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; configures the dashboard in pure SSR mode. All refreshes, trace waterfalls, and searches are handled cleanly with base-relative URLs that work perfectly under IIS sub-applications (e.g. &lt;code&gt;/your-sub-app/&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Persistence Retention Pruning
&lt;/h3&gt;

&lt;p&gt;AppPipe prunes database entries to the last 2,000 logs, metrics, and trace IDs to prevent database size from expanding indefinitely.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Shared IIS Hosting Support (&lt;code&gt;iis-shared&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;For shared hosting setups (such as cPanel, Plesk, or Azure App Service) where background processes and command execution are restricted, AppPipe compiles all microservice binaries into subdirectories of the Gateway.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-Generated web.config&lt;/strong&gt;: Generates isolated &lt;code&gt;web.config&lt;/code&gt; configurations inside each subdirectory, enabling developers to run microservices in-process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Backup &amp;amp; Rollback&lt;/strong&gt;: Optionally syncs build directories to the server over FTP/FTPS. If configured, AppPipe takes a remote backup before deploying and executes an automatic rollback (restoring original files using Mirror mode) if the deployment encounters an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Universal Selective Project Deployment
&lt;/h3&gt;

&lt;p&gt;Deployments support filtering at runtime to compile, start, and restart only specified services, drastically speeding up pipelines when only single microservices change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--project&lt;/span&gt; AppHost.csproj &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--deploy&lt;/span&gt; iis &lt;span class="nt"&gt;--AppPipe&lt;/span&gt;:Deployment:FilterProjects &lt;span class="s2"&gt;"BackendWorker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Integrating AppPipe into DevOps Pipelines
&lt;/h2&gt;

&lt;p&gt;In professional staging and production environments, you build your code once (generating DLLs) and deploy those pre-compiled artifacts across multiple environments. The target servers do not contain the source code or the .NET SDK.&lt;/p&gt;

&lt;p&gt;AppPipe is built to accommodate this separation of concerns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Build Once (CI)
&lt;/h3&gt;

&lt;p&gt;On your build server, compile and publish your orchestrator and microservice projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet publish samples/AppPipe.DevHost/AppPipe.DevHost.csproj &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; ./publish/AppPipe.DevHost
dotnet publish samples/BackendWorker/BackendWorker.csproj &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; ./publish/BackendWorker
dotnet publish samples/FrontendApi/FrontendApi.csproj &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; ./publish/FrontendApi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Package and upload the &lt;code&gt;./publish&lt;/code&gt; directory as a build artifact.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Deploy Anywhere (CD)
&lt;/h3&gt;

&lt;p&gt;On the target machine, download your pre-compiled files and invoke the orchestrator with the &lt;code&gt;--prepublished-dir&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet C:&lt;span class="se"&gt;\i&lt;/span&gt;netpub&lt;span class="se"&gt;\a&lt;/span&gt;pps&lt;span class="se"&gt;\A&lt;/span&gt;ppPipe&lt;span class="se"&gt;\A&lt;/span&gt;ppPipe.DevHost.dll &lt;span class="nt"&gt;--deploy&lt;/span&gt; iis &lt;span class="nt"&gt;--prepublished-dir&lt;/span&gt; C:&lt;span class="se"&gt;\i&lt;/span&gt;netpub&lt;span class="se"&gt;\a&lt;/span&gt;pps&lt;span class="se"&gt;\A&lt;/span&gt;ppPipe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flag tells the orchestrator to &lt;strong&gt;bypass &lt;code&gt;.csproj&lt;/code&gt; checks and local compilation steps entirely&lt;/strong&gt;, deploying direct to IIS or SCM from your pre-compiled DLLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Injecting Environment Configs &amp;amp; Secrets
&lt;/h3&gt;

&lt;p&gt;To configure environment-specific settings (like AppPool names or site directories) and environment secrets (like service passwords), bind the topology definition to standard &lt;code&gt;.NET Configuration&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"APH__"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommandLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the Gateway Dashboard Host Project&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppPipeHostingProjectResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostProject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithIISSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default Web Site"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPath&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AppPipeDashboardPool"&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;appPool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"BackendWorker:AppPoolName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"ProdPool"&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;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"BackendWorker:ServicePassword"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPipeProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendWorker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAppPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithServiceAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"DOMAIN\ServiceAccount"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithServicePassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During your CD release stage, you can inject these settings dynamically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables&lt;/strong&gt;: Map DevOps secrets/variables as environment variables (e.g., &lt;code&gt;APH__BackendWorker__ServicePassword&lt;/code&gt; $\rightarrow$ &lt;code&gt;$(SecretPassword)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command Line Params&lt;/strong&gt;: Pass them directly to the orchestrator:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  dotnet AppPipe.DevHost.dll &lt;span class="nt"&gt;--deploy&lt;/span&gt; iis &lt;span class="nt"&gt;--prepublished-dir&lt;/span&gt; C:&lt;span class="se"&gt;\i&lt;/span&gt;netpub&lt;span class="se"&gt;\a&lt;/span&gt;pps&lt;span class="se"&gt;\A&lt;/span&gt;ppPipe &lt;span class="nt"&gt;--BackendWorker&lt;/span&gt;:AppPoolName &lt;span class="s2"&gt;"ProdPool"&lt;/span&gt; &lt;span class="nt"&gt;--BackendWorker&lt;/span&gt;:ServicePassword &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;SecretPassword&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Get Started Today
&lt;/h2&gt;

&lt;p&gt;AppPipe is open-source and ready to streamline your on-premises microservices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/SitholeWB/AppPipe.Hosting" rel="noopener noreferrer"&gt;SitholeWB/AppPipe.Hosting&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NuGet Package&lt;/strong&gt;: &lt;a href="https://www.nuget.org/packages/AppPipe.Hosting" rel="noopener noreferrer"&gt;AppPipe.Hosting&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aspire</category>
      <category>dotnet</category>
      <category>deployment</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Save documets or files using EntityFrameworkCore to database</title>
      <dc:creator>Welcome Bonginhlahla Sithole</dc:creator>
      <pubDate>Sat, 01 Jul 2023 11:19:52 +0000</pubDate>
      <link>https://dev.to/sitholewb/save-documets-or-files-using-entityframeworkcore-1761</link>
      <guid>https://dev.to/sitholewb/save-documets-or-files-using-entityframeworkcore-1761</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;By default, EntityFrameworkCore doesn’t support storing files in the database.&lt;br&gt;
As a reader of this article, I assume you have worked with Microsoft.EntityFrameworkCore before for saving data into the database.&lt;/p&gt;

&lt;p&gt;There are times when applications or API needs to work with images, for example, you need to store user profile images or some documents submitted by a user. &lt;br&gt;
You can use external sources to store those files, but what if you want to handle them yourself easier and more efficiently?&lt;br&gt;
Well, the good news is this NuGet library &lt;a href="https://www.nuget.org/packages/Files.EntityFrameworkCore.Extensions"&gt;Files.EntityFrameworkCore.Extensions&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The solution to files inside the database &amp;amp; memory usage
&lt;/h2&gt;

&lt;p&gt;This library solves the memory problem that comes with storing files inside the database. If you store a 100MB file as a base64 string in the database, you will need a memory of 100MB available so that the conversion of Stream to base64 string can happen. If 20 users upload about 100MB files all at once, then your API is in trouble because you now need memory that can handle 20*100MB which is about 2GB. Same as file downloading, you will need more memory for base64/Stream conversion.&lt;br&gt;
This library solves this problem by storing bytes in smaller chunks, you can also set your chunk size, the default is 64k.&lt;/p&gt;

&lt;p&gt;You can use this library on any database that is supported by Microsoft.EntityFrameworkCore.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code examples
&lt;/h2&gt;

&lt;p&gt;Enough talking, let's jump into code examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// You must extend this IFileEntity interface on entity/class that represents the database table that will store files&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserImage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFileEntity&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;FileId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MimeType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;TimeStamp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;NextId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ChunkBytesLength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;TotalBytesLength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FilesExtensionsResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;UploadFile&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromForm&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//Save file chunks from stream to database, you can also save from filePath&lt;/span&gt;
             &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fileDetails&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;SaveFileAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserImage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenReadStream&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileDetails&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="k"&gt;else&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File is required."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}/download"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;DownLoadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&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;fileDetails&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;GetFileInfoAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserImage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//Create a Stream and download file chunks from the database into this stream.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&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;DownloadFileToStreamAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserImage&lt;/span&gt;&lt;span class="p"&gt;&amp;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;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MimeType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;DeleteUserImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteFileAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserImage&lt;/span&gt;&lt;span class="p"&gt;&amp;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;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&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;The image below shows chunks inside the database after saving.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqXWJ4Zb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/699td6wxgxpe5dqyoni0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqXWJ4Zb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/699td6wxgxpe5dqyoni0.jpg" alt="Chunks inside the database after saving" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source code can be found here, there are more examples inside this repository: &lt;a href="https://github.com/SitholeWB/Files.EntityFrameworkCore.Extensions"&gt;https://github.com/SitholeWB/Files.EntityFrameworkCore.Extensions&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We have learned that we can save files inside a database with very minimum effort, we don't have to worry about handling chunks ourselves. The library will require about 64k of memory no matter how big the file is.&lt;br&gt;
Thanks, happy coding!&lt;/p&gt;

</description>
      <category>entityframeworkcore</category>
      <category>dotnet</category>
      <category>database</category>
      <category>extensions</category>
    </item>
  </channel>
</rss>
