<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <title>ABP.IO Stories</title>
    <link>https://abp.io/community/articles</link>
    <description>A hub for ABP Framework, .NET, and software development. Access articles, tutorials, news, and contribute to the ABP community.</description>
    <lastBuildDate>Sat, 26 Sep 2026 02:45:37 Z</lastBuildDate>
    <generator>Community - ABP.IO</generator>
    <image>
      <url>https://abp.io/assets/favicon.ico/favicon-32x32.png</url>
      <title>ABP.IO Stories</title>
      <link>https://abp.io/community/articles</link>
    </image>
    <a10:link rel="self" type="application/rss+xml" title="self" href="https://abp.io/community/rss?member=EngincanV" />
    <a10:link rel="next" type="application/rss+xml" title="next" href="https://abp.io/community/rss?page=2&amp;author=EngincanV" />
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/c-15-union-types-and-closed-hierarchies-in-.net-11-ixs91te3</guid>
      <link>https://abp.io/community/posts/c-15-union-types-and-closed-hierarchies-in-.net-11-ixs91te3</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-framework</category>
      <category>dotnet</category>
      <category>new-features</category>
      <category>csharp</category>
      <category>.net</category>
      <title>C# 15 Union Types and Closed Hierarchies in .NET 11</title>
      <description>In this article, we'll start with the simplest possible usage of each feature, look at what they actually compile to, compare them with the patterns we use today, and then build a small order API where both features do real work. I built and tested every example on .NET 11 RC1, and I'll share the exact outputs, including a few surprises that ABP developers should know about before they start using these features.</description>
      <pubDate>Wed, 23 Sep 2026 12:38:25 Z</pubDate>
      <a10:updated>2026-09-25T22:51:42Z</a10:updated>
      <content:encoded><![CDATA[<h1>C# 15 Union Types and Closed Hierarchies: Exhaustive Domain Models and API Contracts</h1>
<blockquote>
<p><strong>Release status.</strong> .NET 11 isn't released as stable yet. Everything in this article was built and tested on <strong>.NET 11 RC1</strong> (SDK <code>11.0.100-rc.1.26425.128</code>, runtime <code>11.0.0-rc.1.26425.128</code>, released September 8, 2026). RC1 comes with a go-live license, but it isn't the final release, and .NET 11 GA is expected in November 2026. Compiler messages, serializer behavior, and tooling can still change before then, so rerun the sample's tests against each new SDK before you ship anything.</p>
</blockquote>
<p>Almost every C# codebase has at least one switch that lies:</p>
<pre><code class="language-csharp">return state switch
{
    Draft =&gt; &quot;draft&quot;,
    Placed =&gt; &quot;placed&quot;,
    Paid =&gt; &quot;paid&quot;,
    _ =&gt; throw new InvalidOperationException(&quot;Unknown state&quot;) // &quot;this can't happen&quot;
};
</code></pre>
<p>That last arm is there because the compiler has no idea that <code>Draft</code>, <code>Placed</code>, and <code>Paid</code> are the only states that exist. So we add a default arm to keep it quiet, and that arm quietly turns into a trap. The day someone adds a <code>Refunded</code> state, this code still compiles fine and throws at runtime.</p>
<p>C# 15 fixes this with two features that ship together in .NET 11: <strong>union types</strong> and <strong>closed hierarchies</strong>. Both tell the compiler &quot;this is the complete list of possibilities&quot;, and in return the compiler checks your <code>switch</code> expressions against that list.</p>
<p>In this article, we'll start with the simplest possible usage of each feature, look at what they actually compile to, compare them with the patterns we use today, and then build a small order API where both features do real work. I built and tested every example on .NET 11 RC1, and I'll share the exact outputs, including a few surprises that ABP developers should know about before they start using these features.</p>
<p>Here's what we'll cover:</p>
<ul>
<li>Where .NET 11 and C# 15 stand today</li>
<li>Closed hierarchies: a simple example and the rules</li>
<li>Union types: a simple example and what they compile to</li>
<li>How they compare with enums, abstract base classes, interfaces, and result wrappers</li>
<li>A realistic scenario: an order API with exhaustive states and results</li>
<li>Going further: JSON contracts, a generic <code>Result&lt;T&gt;</code>, <code>default</code> values, and versioning</li>
<li>Using these features in an ABP solution</li>
<li>RC1 constraints and production readiness</li>
<li>Adoption and migration guidance</li>
<li>Running the sample yourself</li>
</ul>
<h2>Where .NET 11 and C# 15 Stand Today</h2>
<p>A quick status check first, because it affects everything else in this article:</p>
<ul>
<li><strong>.NET 11 RC1</strong> shipped on <strong>September 8, 2026</strong>. The official release metadata lists the channel in the <strong>go-live</strong> support phase, with release type <strong>STS</strong>.</li>
<li><strong>GA is expected in November 2026.</strong> Until then, &quot;stable&quot; isn't the right word, so treat everything here as release-candidate behavior.</li>
<li>RC1 makes <strong>C# 15 the default language version</strong> for projects that target <code>net11.0</code>, and it stabilizes union types and closed class hierarchies, along with collection expression arguments, labeled <code>break</code>/<code>continue</code>, and extension indexers. You <strong>don't</strong> need <code>&lt;LangVersion&gt;preview&lt;/LangVersion&gt;</code> anymore. None of the projects in this article set a <code>LangVersion</code>.</li>
<li>The docs are catching up. At the time of writing, the &quot;What's new in C# 15&quot; page still calls C# 15 &quot;the latest C# preview release&quot; and notes that some features from the union proposal aren't implemented yet. For the language status in RC1, the <a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/csharp.md">C# in .NET 11 RC 1 release notes</a> are the more precise source.</li>
</ul>
<blockquote>
<p><strong>For ABP developers:</strong> the latest stable ABP release on NuGet at the time of writing is <strong>10.6.1</strong>, and ABP 10.x packages target up to <code>net10.0</code>. You need a <code>net11.0</code> project to use C# 15. As usual, ABP will ship a <strong>.NET 11-based ABP 11</strong> release, so you'll get first-class .NET 11 support there. Until then, I tested ABP 10.6.1 packages inside a <code>net11.0</code> app, and you'll find the results in the <a href="#using-these-features-in-an-abp-solution">ABP section</a> below.</p>
</blockquote>
<h2>Closed Hierarchies: A Simple Example</h2>
<p>Let's start with the easier one. You add the <code>closed</code> modifier to a class (or a record class), and from then on, <strong>only code in the same assembly can derive directly from it</strong>:</p>
<pre><code class="language-csharp">public closed record OrderState;

public sealed record Draft : OrderState;
public sealed record Placed(DateTimeOffset PlacedAt) : OrderState;
public sealed record Paid(DateTimeOffset PlacedAt, string PaymentId) : OrderState;
public sealed record Shipped(string PaymentId, string TrackingNumber) : OrderState;
public sealed record Cancelled(string Reason) : OrderState;
</code></pre>
<p>Because the compiler now knows every direct descendant, a <code>switch</code> expression that handles all of them is exhaustive. There's no default arm, and there's no warning:</p>
<pre><code class="language-csharp">static string Describe(OrderState state) =&gt; state switch
{
    Draft =&gt; &quot;draft&quot;,
    Placed(var placedAt) =&gt; $&quot;placed at {placedAt:HH:mm}&quot;,
    Paid(_, var paymentId) =&gt; $&quot;paid with {paymentId}&quot;,
    Shipped(_, var trackingNumber) =&gt; $&quot;shipped, tracking {trackingNumber}&quot;,
    Cancelled(var reason) =&gt; $&quot;cancelled: {reason}&quot;,
};

// Describe(new Paid(now, &quot;pay_7Hq2&quot;)) -&gt; &quot;paid with pay_7Hq2&quot;
</code></pre>
<p>Here are the rules I think are worth remembering. The ones with error codes are straight from the RC1 compiler; the rest come from the language reference:</p>
<ul>
<li><strong>A closed class is implicitly abstract.</strong> <code>new OrderState()</code> fails with <code>CS0144: Cannot create an instance of the abstract type or interface 'OrderState'</code>. You also can't combine <code>closed</code> with <code>sealed</code>, <code>static</code>, or an explicit <code>abstract</code>.</li>
<li><strong>Other assemblies can't derive from it.</strong> In the versioning experiment later in this article, declaring <code>public sealed record Lost : ShipmentStatus;</code> in a consumer project failed with <code>CS9382: 'Lost': cannot use a closed type 'ShipmentStatus' from another assembly as a base type</code>.</li>
<li><strong>It isn't transitive.</strong> Only <em>direct</em> descendants are restricted. If <code>Paid</code> were a normal, unsealed record, another assembly could derive from <code>Paid</code>. That's why I seal the leaves. Alternatively, you can mark an intermediate type <code>closed</code> too.</li>
<li><strong>Classes only.</strong> <code>closed interface</code> fails with <code>CS0106: The modifier 'closed' is not valid for this item</code>. The spec lists closed interfaces as a possible future feature, but they aren't in C# 15.</li>
<li><strong>Nullable inputs need a <code>null</code> arm.</strong> A switch over <code>OrderState?</code> isn't exhaustive until you handle <code>null</code>.</li>
<li><strong>It's a contextual keyword.</strong> Existing variables named <code>closed</code> keep compiling.</li>
</ul>
<p>So what does the compiler actually emit? I checked with reflection. <code>OrderState</code> becomes a regular <strong>abstract class</strong> marked with <code>[IsClosedType]</code>, and its constructors are <code>protected</code> and marked <code>[CompilerFeatureRequired(&quot;ClosedClasses&quot;)]</code>. That last attribute is how the restriction survives compilation: a compiler that doesn't understand closed classes refuses to call those constructors, so it can't derive from the type either.</p>
<h2>Union Types: A Simple Example</h2>
<p>A union is a value that is <strong>exactly one of a fixed list of case types</strong>. The case types already exist, and the union just groups them:</p>
<pre><code class="language-csharp">public sealed record OrderPlaced(Guid OrderId, decimal Total);
public sealed record OutOfStock(string ProductCode, int Requested, int Available);
public sealed record CreditLimitExceeded(decimal Limit, decimal Attempted);
public sealed record InvalidOrder(IReadOnlyList&lt;string&gt; Errors);

public union PlaceOrderResult(OrderPlaced, OutOfStock, CreditLimitExceeded, InvalidOrder);
</code></pre>
<p>Each case converts to the union implicitly, so you just assign or return the case:</p>
<pre><code class="language-csharp">PlaceOrderResult result = new OutOfStock(&quot;MONITOR&quot;, Requested: 5, Available: 3);
</code></pre>
<p>And consuming code pattern-matches on the cases directly, with no default arm:</p>
<pre><code class="language-csharp">var message = result switch
{
    OrderPlaced placed =&gt; $&quot;Order {placed.OrderId} placed&quot;,
    OutOfStock(var product, var requested, var available) =&gt; $&quot;Only {available} of {product} left, you asked for {requested}&quot;,
    CreditLimitExceeded(var limit, _) =&gt; $&quot;Credit limit of {limit} exceeded&quot;,
    InvalidOrder(var errors) =&gt; string.Join(&quot; &quot;, errors),
};

// &quot;Only 3 of MONITOR left, you asked for 5&quot;
</code></pre>
<p>If you forget a case, the compiler tells you which one:</p>
<pre><code class="language-text">warning CS8509: The switch expression does not handle all possible values of its input type
(it is not exhaustive). For example, the pattern 'OrderDemo.Domain.Orders.InvalidOrder' is not covered.
</code></pre>
<p>Now, what <em>is</em> a union at runtime? The compiler turns the <code>union</code> declaration into a <strong>struct</strong>. The struct is marked with <code>[Union]</code>, implements <code>System.Runtime.CompilerServices.IUnion</code>, has one constructor per case type, and stores the active case in a single <code>object? Value</code> property. A few consequences follow from that:</p>
<ul>
<li><strong>Patterns unwrap the union.</strong> <code>result is OutOfStock</code> tests <code>result.Value</code>, not the struct itself, so it returns <code>true</code> in the example above.</li>
<li><strong>Value-type cases are boxed.</strong> In <code>union IntOrString(int, string)</code>, assigning <code>42</code> stores a boxed <code>Int32</code> in <code>Value</code>. For hot paths, the docs describe how to write a custom union with a non-boxing access pattern.</li>
<li><strong>Case types can be almost anything:</strong> records, classes, structs, primitives, interfaces, and even other unions.</li>
<li><strong>A case type can belong to several unions.</strong> In the sample, <code>OutOfStock</code> is also used by <code>union ReserveStockResult(StockReserved, OutOfStock)</code>. A closed hierarchy can't do that, because a class has only one base class.</li>
<li><strong>You can add members to a union body</strong> (methods and computed properties), but you can't add instance fields or auto-properties.</li>
</ul>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-09-23-csharp-15-union-types-and-closed-hierarchies/union-vs-closed.png" alt="Union types compose existing types, while closed hierarchies form one family inside the declaring assembly" /></p>
<p><em>Figure: A union groups independent case types (one of them shared by two unions). A closed hierarchy is one inheritance family that other assemblies can't extend.</em></p>
<h2>How They Compare With What We Do Today</h2>
<p>We've been modeling &quot;one of several shapes&quot; for years without these features. Here's how the usual suspects behave in a <code>switch</code> expression, based on the diagnostics RC1 actually produced:</p>
<p>| Approach | Exhaustive without <code>_</code>? | New case flagged at compile time? | Unrelated types as cases? |
|---|---|---|---|
| <code>enum</code> | No (CS8524) | Only if you avoid <code>_</code> | Not applicable |
| Open abstract base class | No (CS8509 on <code>_</code>) | No | No |
| Marker interface | No | No | Only types you own |
| Result wrapper library | Through <code>Match(...)</code> | Depends on the library | Yes |
| <code>closed</code> hierarchy | Yes | Yes (CS8509) | No |
| <code>union</code> | Yes | Yes (CS8509) | Yes |</p>
<p>A few notes on this table:</p>
<ul>
<li><strong>Enums</strong> get halfway there. If you handle every named member, the compiler still warns with <code>CS8524</code> because an enum can hold unnamed values like <code>(LegacyOrderStatus)3</code>. Most teams silence that with <code>_ =&gt; ...</code>, and from then on, adding an enum member is silent. Enums also can't carry per-state data, which is how we end up with a <code>Status</code> enum next to a bag of nullable properties (<code>PaidAt</code>, <code>TrackingNumber</code>, <code>CancelReason</code>) that are only valid in some states.</li>
<li><strong>Open abstract base classes</strong> are what most of us use for this today. Without a default arm, the compiler warns that <code>the pattern '_' is not covered</code>, because any assembly could add a subclass. So you add <code>_ =&gt; throw</code>, and you're back to the lying switch from the introduction.</li>
<li><strong>Result wrappers</strong> such as a hand-written <code>Result&lt;T&gt;</code> or OneOf-style libraries work, but exhaustiveness lives in the library's <code>Match</code> API instead of in regular C# patterns, and each library brings its own conventions.</li>
</ul>
<p>One important limit applies to <em>every</em> row: <strong>only <code>switch</code> expressions get exhaustiveness checking.</strong> A <code>switch</code> statement that forgets a case compiles without a warning (I tried it with both a union and a closed hierarchy), and so does an <code>if</code>/<code>else</code> chain. If you want the safety net, write <code>switch</code> expressions.</p>
<p>The following decision flow is how I pick between the options now:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-09-23-csharp-15-union-types-and-closed-hierarchies/choosing-a-model.png" alt="Decision flow for choosing between enum, open base class, union, and closed hierarchy" /></p>
<h2>A Realistic Scenario: An Order API</h2>
<p>Let's put both features to work in a scenario ABP developers will recognize: a small ordering API with an order aggregate, a domain service that places orders, and HTTP endpoints on top. To keep the sample focused on the language features, it's a plain .NET 11 solution with in-memory stores. We'll bring ABP into the picture in a separate section.</p>
<pre><code class="language-text">OrderDemo.slnx
├── src/OrderDemo.Domain    // OrderState (closed), PlaceOrderResult (union), Order, OrderPlacementService
├── src/OrderDemo.Api       // Minimal API endpoints + DTOs
└── test/OrderDemo.Tests    // xUnit + Shouldly + WebApplicationFactory
</code></pre>
<h3>Order states as a closed hierarchy</h3>
<p>The order moves through a small state machine. Each state carries only the data that makes sense for it: a <code>Paid</code> order has a <code>PaymentId</code>, and a <code>Shipped</code> order has a <code>TrackingNumber</code>. Nothing is nullable &quot;because it depends on the status&quot;.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-09-23-csharp-15-union-types-and-closed-hierarchies/order-state-machine.png" alt="Order state machine modeled as a closed hierarchy" /></p>
<p>Here's the aggregate. Every transition is a <code>switch</code> expression over the current state:</p>
<pre><code class="language-csharp">public sealed class Order
{
    private readonly List&lt;OrderLine&gt; _lines;

    public Guid Id { get; }

    public string CustomerId { get; }

    public IReadOnlyList&lt;OrderLine&gt; Lines =&gt; _lines;

    public OrderState State { get; private set; }

    public decimal Total =&gt; _lines.Sum(line =&gt; line.UnitPrice * line.Quantity);

    public Order(Guid id, string customerId, IEnumerable&lt;OrderLine&gt; lines)
    {
        Id = id;
        CustomerId = customerId;
        _lines = [.. lines];
        State = new Draft();
    }

    public void Place(DateTimeOffset now) =&gt; State = State switch
    {
        Draft =&gt; new Placed(now),
        Placed or Paid or Shipped or Cancelled =&gt; throw InvalidTransition(nameof(Place)),
    };

    public void MarkAsPaid(string paymentId) =&gt; State = State switch
    {
        Placed placed =&gt; new Paid(placed.PlacedAt, paymentId),
        Draft or Paid or Shipped or Cancelled =&gt; throw InvalidTransition(nameof(MarkAsPaid)),
    };

    public void Ship(string trackingNumber) =&gt; State = State switch
    {
        Paid paid =&gt; new Shipped(paid.PaymentId, trackingNumber),
        Draft or Placed or Shipped or Cancelled =&gt; throw InvalidTransition(nameof(Ship)),
    };

    public void Cancel(string reason) =&gt; State = State switch
    {
        Draft or Placed =&gt; new Cancelled(reason),
        Paid paid =&gt; new Cancelled($&quot;{reason} (refund payment {paid.PaymentId})&quot;),
        Shipped =&gt; throw new OrderStateException(
            &quot;OrderDemo:CannotCancelShippedOrder&quot;,
            &quot;A shipped order can't be cancelled. Create a return instead.&quot;),
        Cancelled =&gt; State,
    };

    private OrderStateException InvalidTransition(string action) =&gt; new(
        &quot;OrderDemo:InvalidOrderStateTransition&quot;,
        $&quot;Can't run '{action}' on an order in the '{State.GetType().Name}' state.&quot;);
}
</code></pre>
<p>Notice that I list the invalid states explicitly (<code>Draft or Paid or Shipped or Cancelled =&gt; throw ...</code>) instead of writing <code>_ =&gt; throw ...</code>. It's a little more typing, but it's the whole point: a discard arm would &quot;handle&quot; any future state automatically, and the compiler would have nothing to warn about. With explicit arms, adding a state forces a decision in every transition. We'll see that in action in a moment.</p>
<blockquote>
<p><code>OrderStateException</code> carries a namespaced error code such as <code>OrderDemo:CannotCancelShippedOrder</code>. In an ABP application, this would simply be a <code>BusinessException</code> with the same code, since ABP's domain services throw business exceptions for rule violations.</p>
</blockquote>
<h3>Placement outcomes as a union</h3>
<p>Placing an order has four <em>expected</em> outcomes, and only one of them is a success. Running out of stock isn't exceptional in an online shop; it happens every day, and the caller must handle it. That's a perfect fit for the <code>PlaceOrderResult</code> union we declared earlier. The domain service returns cases directly, and the implicit conversion works even through <code>Task&lt;PlaceOrderResult&gt;</code>:</p>
<pre><code class="language-csharp">public async Task&lt;PlaceOrderResult&gt; PlaceAsync(PlaceOrderInput input, CancellationToken cancellationToken = default)
{
    if (input.Lines.Count == 0)
    {
        return new InvalidOrder([&quot;An order must contain at least one line.&quot;]);
    }

    var lines = new List&lt;OrderLine&gt;();
    var errors = new List&lt;string&gt;();

    foreach (var line in input.Lines)
    {
        // ... validation and stock lookup ...

        if (stock.Available &lt; line.Quantity)
        {
            return new OutOfStock(line.ProductCode, line.Quantity, stock.Available);
        }

        lines.Add(new OrderLine(line.ProductCode, line.Quantity, stock.UnitPrice));
    }

    if (errors.Count &gt; 0)
    {
        return new InvalidOrder(errors);
    }

    var order = new Order(Guid.NewGuid(), input.CustomerId, lines);

    var limit = await customerCredit.GetLimitAsync(input.CustomerId, cancellationToken);
    if (order.Total &gt; limit)
    {
        return new CreditLimitExceeded(limit, order.Total);
    }

    order.Place(clock.GetUtcNow());
    await orderRepository.InsertAsync(order, cancellationToken);

    return new OrderPlaced(order.Id, order.Total);
}
</code></pre>
<p>The method signature is now honest: it tells every caller exactly what can come back, without try/catch blocks for expected outcomes and without a generic <code>Result</code> whose error part is just a string.</p>
<h3>Mapping the union to HTTP responses</h3>
<p>At the API boundary, each case gets its own status code. ASP.NET Core's <code>Results&lt;...&gt;</code> type already models &quot;one of these HTTP results&quot;, so the two fit together nicely. The <code>switch</code> is target-typed to the endpoint's return type, and it's exhaustive over <code>PlaceOrderResult</code>:</p>
<pre><code class="language-csharp">app.MapPost(&quot;/orders&quot;, async Task&lt;Results&lt;Created&lt;OrderPlaced&gt;, Conflict&lt;OutOfStock&gt;, UnprocessableEntity&lt;CreditLimitExceeded&gt;, ValidationProblem&gt;&gt; (
    PlaceOrderInput input,
    OrderPlacementService service,
    CancellationToken cancellationToken) =&gt;
    await service.PlaceAsync(input, cancellationToken) switch
    {
        OrderPlaced placed =&gt; TypedResults.Created($&quot;/orders/{placed.OrderId}&quot;, placed),
        OutOfStock outOfStock =&gt; TypedResults.Conflict(outOfStock),
        CreditLimitExceeded exceeded =&gt; TypedResults.UnprocessableEntity(exceeded),
        InvalidOrder invalid =&gt; TypedResults.ValidationProblem(
            new Dictionary&lt;string, string[]&gt; { [&quot;lines&quot;] = [.. invalid.Errors] }),
    });
</code></pre>
<p>If someone adds a fifth outcome to <code>PlaceOrderResult</code>, this endpoint stops compiling until they decide which HTTP response it deserves.</p>
<h3>Exposing the state as a discriminated DTO</h3>
<p>For reading an order, the API returns an <code>OrderDto</code> whose <code>State</code> property is a <strong>closed DTO hierarchy with an explicit JSON discriminator</strong>. I nest the cases inside the base record to keep the family together (<code>OrderStateDto.Paid</code>), and I give each case an explicit wire name:</p>
<pre><code class="language-csharp">[JsonPolymorphic(TypeDiscriminatorPropertyName = &quot;status&quot;)]
[JsonDerivedType(typeof(Draft), &quot;draft&quot;)]
[JsonDerivedType(typeof(Placed), &quot;placed&quot;)]
[JsonDerivedType(typeof(Paid), &quot;paid&quot;)]
[JsonDerivedType(typeof(Shipped), &quot;shipped&quot;)]
[JsonDerivedType(typeof(Cancelled), &quot;cancelled&quot;)]
public closed record OrderStateDto
{
    public sealed record Draft : OrderStateDto;
    public sealed record Placed(DateTimeOffset PlacedAt) : OrderStateDto;
    public sealed record Paid(string PaymentId) : OrderStateDto;
    public sealed record Shipped(string TrackingNumber) : OrderStateDto;
    public sealed record Cancelled(string Reason) : OrderStateDto;
}

public sealed record OrderDto(Guid Id, string CustomerId, decimal Total, OrderStateDto State);

public static class OrderMapper
{
    public static OrderDto ToDto(this Order order) =&gt; new(order.Id, order.CustomerId, order.Total, order.State.ToDto());

    public static OrderStateDto ToDto(this OrderState state) =&gt; state switch
    {
        Draft =&gt; new OrderStateDto.Draft(),
        Placed placed =&gt; new OrderStateDto.Placed(placed.PlacedAt),
        Paid paid =&gt; new OrderStateDto.Paid(paid.PaymentId),
        Shipped shipped =&gt; new OrderStateDto.Shipped(shipped.TrackingNumber),
        Cancelled cancelled =&gt; new OrderStateDto.Cancelled(cancelled.Reason),
    };
}
</code></pre>
<p>The mapper is exhaustive too. That's a small detail with a big payoff: the domain and the contract can't silently drift apart. (Notice that the DTO intentionally drops <code>PlacedAt</code> from <code>Paid</code>. The contract doesn't have to mirror the domain model; it only has to cover it.)</p>
<h3>Does It Actually Work?</h3>
<p>Let's run the API and walk through the whole flow. These are the real responses from the sample running on .NET 11 RC1:</p>
<pre><code class="language-bash">dotnet run --project src/OrderDemo.Api --urls http://localhost:5189
</code></pre>
<p><strong>Happy path:</strong></p>
<pre><code class="language-bash">curl -i -X POST http://localhost:5189/orders -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;customerId&quot;:&quot;acme&quot;,&quot;lines&quot;:[{&quot;productCode&quot;:&quot;KEYBOARD&quot;,&quot;quantity&quot;:2},{&quot;productCode&quot;:&quot;MOUSE&quot;,&quot;quantity&quot;:1}]}'
</code></pre>
<pre><code class="language-text">HTTP 201
{&quot;orderId&quot;:&quot;0757817c-70c7-41db-a5d7-3b8eea41eda2&quot;,&quot;total&quot;:144.30}
</code></pre>
<p><strong>The other three union cases</strong>, each with its own status code:</p>
<pre><code class="language-text">POST /orders  {&quot;customerId&quot;:&quot;acme&quot;,&quot;lines&quot;:[{&quot;productCode&quot;:&quot;MONITOR&quot;,&quot;quantity&quot;:5}]}
HTTP 409
{&quot;productCode&quot;:&quot;MONITOR&quot;,&quot;requested&quot;:5,&quot;available&quot;:3}

POST /orders  {&quot;customerId&quot;:&quot;startup-42&quot;,&quot;lines&quot;:[{&quot;productCode&quot;:&quot;MONITOR&quot;,&quot;quantity&quot;:2}]}
HTTP 422
{&quot;limit&quot;:500,&quot;attempted&quot;:658.00}

POST /orders  {&quot;customerId&quot;:&quot;acme&quot;,&quot;lines&quot;:[{&quot;productCode&quot;:&quot;LAPTOP&quot;,&quot;quantity&quot;:1},{&quot;productCode&quot;:&quot;MOUSE&quot;,&quot;quantity&quot;:0}]}
HTTP 400
{&quot;type&quot;:&quot;https://tools.ietf.org/html/rfc9110#section-15.5.1&quot;,&quot;title&quot;:&quot;One or more validation errors occurred.&quot;,&quot;status&quot;:400,
 &quot;errors&quot;:{&quot;lines&quot;:[&quot;Unknown product 'LAPTOP'.&quot;,&quot;Quantity for 'MOUSE' must be greater than zero.&quot;]},&quot;traceId&quot;:&quot;...&quot;}
</code></pre>
<p><strong>Reading the order and moving it through the state machine:</strong></p>
<pre><code class="language-text">GET /orders/0757817c-...
HTTP 200
{&quot;id&quot;:&quot;0757817c-...&quot;,&quot;customerId&quot;:&quot;acme&quot;,&quot;total&quot;:144.30,&quot;state&quot;:{&quot;status&quot;:&quot;placed&quot;,&quot;placedAt&quot;:&quot;2026-09-23T08:47:32.0829309+00:00&quot;}}

POST /orders/0757817c-.../ship  {&quot;trackingNumber&quot;:&quot;TRK-1&quot;}
HTTP 409
{&quot;type&quot;:&quot;https://tools.ietf.org/html/rfc9110#section-15.5.10&quot;,&quot;title&quot;:&quot;Can't run 'Ship' on an order in the 'Placed' state.&quot;,&quot;status&quot;:409,&quot;code&quot;:&quot;OrderDemo:InvalidOrderStateTransition&quot;}

POST /orders/0757817c-.../pay  {&quot;paymentId&quot;:&quot;pay_7Hq2&quot;}
HTTP 200
{&quot;id&quot;:&quot;0757817c-...&quot;,&quot;customerId&quot;:&quot;acme&quot;,&quot;total&quot;:144.30,&quot;state&quot;:{&quot;status&quot;:&quot;paid&quot;,&quot;paymentId&quot;:&quot;pay_7Hq2&quot;}}

POST /orders/0757817c-.../ship  {&quot;trackingNumber&quot;:&quot;TRK-90210&quot;}
HTTP 200
{&quot;id&quot;:&quot;0757817c-...&quot;,&quot;customerId&quot;:&quot;acme&quot;,&quot;total&quot;:144.30,&quot;state&quot;:{&quot;status&quot;:&quot;shipped&quot;,&quot;trackingNumber&quot;:&quot;TRK-90210&quot;}}

POST /orders/0757817c-.../cancel  {&quot;reason&quot;:&quot;Customer changed their mind&quot;}
HTTP 409
{&quot;type&quot;:&quot;https://tools.ietf.org/html/rfc9110#section-15.5.10&quot;,&quot;title&quot;:&quot;A shipped order can't be cancelled. Create a return instead.&quot;,&quot;status&quot;:409,&quot;code&quot;:&quot;OrderDemo:CannotCancelShippedOrder&quot;}
</code></pre>
<p>Every state carries its own data, and the <code>status</code> discriminator tells a TypeScript or C# client exactly which shape it's looking at.</p>
<blockquote>
<p>On Windows PowerShell 5.1, <code>curl.exe -d '{...}'</code> loses the JSON quotes when it passes arguments to the native process. Save the body to a file and use <code>--data-binary &quot;@body.json&quot;</code>, or use PowerShell 7.</p>
</blockquote>
<h3>The moment it pays off: adding a new state</h3>
<p>This is the part I was most curious about. Imagine the business asks for refunds, so we add one line to the domain:</p>
<pre><code class="language-csharp">public sealed record Refunded(string PaymentId, decimal Amount) : OrderState;
</code></pre>
<p>Then we run <code>dotnet build</code>:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-09-23-csharp-15-union-types-and-closed-hierarchies/refunded-build-errors.png" alt="Build errors after adding a Refunded state to the closed OrderState hierarchy" /></p>
<p>The build fails in all four transition methods of <code>Order</code>. The sample promotes <code>CS8509</code> to an error (more on that in the migration section), so these aren't warnings you can scroll past. When I built with warnings instead of errors so that the API project could compile too, a fifth location appeared: the <code>OrderState.ToDto()</code> mapper in <code>OrderDemo.Api</code>. That's every place in the solution that needs a decision about refunds, and the compiler found all five. With <code>_ =&gt; throw</code> arms, the build would have passed and the first refund would have failed in production.</p>
<h2>Going Further</h2>
<p>Now that the basics are in place, let's look at the parts that need a bit more care.</p>
<h3>Unions and closed hierarchies on the wire</h3>
<p>The two features look similar in C#, but they serialize very differently with <code>System.Text.Json</code> in .NET 11:</p>
<ul>
<li><strong>A union writes only its active case, with no discriminator.</strong> The <code>/orders/preview</code> endpoint in the sample returns <code>PlaceOrderResult</code> as-is, and the body is <code>{&quot;productCode&quot;:&quot;MONITOR&quot;,&quot;requested&quot;:5,&quot;available&quot;:3}</code> with <code>200 OK</code>. Nothing in that JSON says &quot;this is an <code>OutOfStock</code>&quot;; the client has to figure it out from the shape.</li>
<li><strong>Reading a union back needs help when several cases are JSON objects.</strong> Deserializing that same body into <code>PlaceOrderResult</code> throws <code>JsonException: JSON value type 'Object' is ambiguous for union type ... because multiple case types can use this value type. Specify a custom type classifier to support deserialization.</code> Adding <code>[JsonUnion(TypeClassifier = typeof(JsonUnionTypeStructuralClassifier))]</code> to the union lets the serializer pick the case by property names. I verified this with a two-case union whose cases have different property names.</li>
<li><strong><code>closed</code> alone doesn't change the JSON.</strong> To get a discriminator, you opt in with <code>[JsonPolymorphic]</code>. With <code>[JsonPolymorphic(InferClosedTypePolymorphism = true)]</code>, the serializer discovers the cases from the closed hierarchy and uses the type names as discriminators. For nested cases, that's the simple name, so you get <code>{&quot;$type&quot;:&quot;Paid&quot;,&quot;paymentId&quot;:&quot;pay_123&quot;}</code>. With explicit <code>[JsonDerivedType]</code> names, as in the sample, you control both the property name and the values: <code>{&quot;status&quot;:&quot;paid&quot;,&quot;paymentId&quot;:&quot;pay_7Hq2&quot;}</code>.</li>
</ul>
<p>For public contracts, I prefer the explicit names. Renaming a C# class shouldn't change your API, and the explicit list is easy to review in a pull request.</p>
<p>This lines up with Microsoft's own guidance in <a href="https://devblogs.microsoft.com/dotnet/unions-and-closed-hierarchies-in-aspnetcore/">Use C# unions and closed hierarchies in ASP.NET Core</a>: for a new contract where you own every case, use a closed hierarchy with a discriminator. Use a union when you must preserve an existing discriminator-free contract, or when the cases can't share a base class (primitives, types you don't own). The same post lists the binding limitations: unions work in JSON request and response bodies, but not in query strings, route values, headers, or form fields, and in SignalR they only work with <code>JsonHubProtocol</code>.</p>
<blockquote>
<p>If you want to go deeper on the serializer side (the union contract kind, classifiers, OpenAPI <code>anyOf</code> output, and generated TypeScript clients), my teammate Okan Koca covered it in detail in <a href="https://github.com/abpframework/abp/blob/dev/docs/en/Community-Articles/2026-09-21-system-text-json-in-net11-naming-policies-union-types-ndjson-streaming/post.md">System.Text.Json in .NET 11: naming policies, union types, and NDJSON streaming</a>.</p>
</blockquote>
<h3>A generic Result<T>: where the union falls short</h3>
<p>Here's the first thing I tried to build with unions, and it's probably the first thing you'll try too:</p>
<pre><code class="language-csharp">public sealed record Error(string Code, string Message);

public union Result&lt;T&gt;(T, Error) where T : notnull;
</code></pre>
<p>The declaration compiles, and concrete usage like <code>Result&lt;int&gt;</code> with an <code>int value =&gt;</code> arm works fine. But the moment you write a <em>generic</em> helper, it breaks:</p>
<pre><code class="language-csharp">public static TOut Match&lt;T, TOut&gt;(Result&lt;T&gt; result, Func&lt;T, TOut&gt; onSuccess, Func&lt;Error, TOut&gt; onFailure)
    where T : notnull =&gt; result switch
{
    T value =&gt; onSuccess(value),      // error CS8780
    Error error =&gt; onFailure(error),
};
</code></pre>
<pre><code class="language-text">error CS8780: A variable may not be declared within a 'not' or an 'or' pattern or a union matching
involving matching against either the instance, or its underlying value.
</code></pre>
<p>The compiler can't know whether <code>T</code> is itself a union, so it can't decide whether <code>T value</code> should match the union instance or its <code>Value</code>. A <code>where T : class</code> constraint doesn't help (I tried). And there's a second catch: <code>Result&lt;Error&gt;</code> makes both constructors identical, so <code>Result&lt;Error&gt; r = new Error(...)</code> fails with <code>CS0457: Ambiguous user defined conversions</code>.</p>
<p>For a <em>generic</em> result type, a closed generic hierarchy works better. Combined with C# 14 extension members, it reads nicely:</p>
<pre><code class="language-csharp">public closed record Result&lt;T&gt;
{
    public static implicit operator Result&lt;T&gt;(T value) =&gt; new Success&lt;T&gt;(value);

    public static implicit operator Result&lt;T&gt;(Error error) =&gt; new Failure&lt;T&gt;(error);
}

public sealed record Success&lt;T&gt;(T Value) : Result&lt;T&gt;;

public sealed record Failure&lt;T&gt;(Error Error) : Result&lt;T&gt;;

public static class ResultExtensions
{
    extension&lt;T&gt;(Result&lt;T&gt; result)
    {
        public bool IsSuccess =&gt; result is Success&lt;T&gt;;

        public TOut Match&lt;TOut&gt;(Func&lt;T, TOut&gt; onSuccess, Func&lt;Error, TOut&gt; onFailure) =&gt; result switch
        {
            Success&lt;T&gt;(var value) =&gt; onSuccess(value),
            Failure&lt;T&gt;(var error) =&gt; onFailure(error),
        };

        public Result&lt;TOut&gt; Map&lt;TOut&gt;(Func&lt;T, TOut&gt; map) =&gt; result switch
        {
            Success&lt;T&gt;(var value) =&gt; map(value),
            Failure&lt;T&gt;(var error) =&gt; error,
        };

        public Result&lt;TOut&gt; Bind&lt;TOut&gt;(Func&lt;T, Result&lt;TOut&gt;&gt; next) =&gt; result switch
        {
            Success&lt;T&gt;(var value) =&gt; next(value),
            Failure&lt;T&gt;(var error) =&gt; error,
        };
    }
}
</code></pre>
<p>Every switch here is exhaustive with no default arm, because the closed hierarchies rules also cover generic types: each derived type uses the base's type parameter, so for any <code>Result&lt;T&gt;</code> there is exactly one <code>Success&lt;T&gt;</code> and one <code>Failure&lt;T&gt;</code>. Usage looks like this, and the sample's tests confirm all three paths:</p>
<pre><code class="language-csharp">Result&lt;int&gt; ParseQuantity(string input) =&gt;
    int.TryParse(input, out var quantity) &amp;&amp; quantity &gt; 0
        ? quantity
        : new Error(&quot;OrderDemo:InvalidQuantity&quot;, $&quot;'{input}' isn't a valid quantity.&quot;);

Result&lt;decimal&gt; PriceFor(int quantity) =&gt;
    quantity &lt;= 10
        ? quantity * 59.90m
        : new Error(&quot;OrderDemo:BulkOrder&quot;, &quot;Bulk orders need a quote.&quot;);

var message = ParseQuantity(input)
    .Bind(PriceFor)
    .Map(total =&gt; $&quot;Total: {total}&quot;)
    .Match(ok =&gt; ok, error =&gt; error.Code);

// &quot;2&quot;   -&gt; &quot;Total: 119.80&quot;
// &quot;abc&quot; -&gt; &quot;OrderDemo:InvalidQuantity&quot;
// &quot;50&quot;  -&gt; &quot;OrderDemo:BulkOrder&quot;
</code></pre>
<p>My rule of thumb: <strong>use a union for concrete, operation-specific outcomes</strong> (like <code>PlaceOrderResult</code>), and <strong>use a closed generic hierarchy when you need a reusable, generic result type</strong>.</p>
<h3><code>default</code>: the value the compiler doesn't see</h3>
<p>Unions are structs, and every struct has a <code>default</code> value. For a union, <code>default</code> means <code>Value</code> is <code>null</code>, and <strong>none</strong> of the cases match it. The compiler doesn't warn you, because a non-nullable union parameter is assumed to hold a value:</p>
<pre><code class="language-csharp">var results = new PlaceOrderResult[1]; // results[0] is default

static string Describe(PlaceOrderResult result) =&gt; result switch
{
    OrderPlaced =&gt; &quot;placed&quot;,
    OutOfStock =&gt; &quot;out of stock&quot;,
    CreditLimitExceeded =&gt; &quot;credit&quot;,
    InvalidOrder =&gt; &quot;invalid&quot;,
};

Describe(results[0]); // SwitchExpressionException: Non-exhaustive switch expression failed to match its input.
</code></pre>
<p>That's a real test in the sample, and it passes by throwing. Arrays, uninitialized fields, and <code>default(T)</code> in generic code are the usual sources. If a union can reach your code that way, add a <code>null</code> arm. It's allowed, and it catches the default value:</p>
<pre><code class="language-csharp">    InvalidOrder =&gt; &quot;invalid&quot;,
    null =&gt; &quot;no result (default value)&quot;,
</code></pre>
<h3>Versioning: exhaustive today, an exception tomorrow</h3>
<p>Exhaustiveness is a <strong>compile-time</strong> check. At runtime, the compiler still emits a fallback that throws <code>SwitchExpressionException</code> for anything unexpected. I wanted to see what that means across assemblies, so I built a tiny experiment:</p>
<ol>
<li>A <code>Shipping</code> library with <code>closed record ShipmentStatus</code> and three cases.</li>
<li>A <code>Consumer</code> app with an exhaustive switch over those three cases, compiled against v1.</li>
<li>A v2 of the library that adds <code>Returned</code>, dropped into the consumer's output folder <strong>without</strong> recompiling the consumer.</li>
</ol>
<pre><code class="language-text">--- 1) consumer built against v1
in transit with UPS
--- 2) ship Shipping v2 only, no consumer rebuild
Unhandled exception. System.Runtime.CompilerServices.SwitchExpressionException: Non-exhaustive switch expression failed to match its input.
Unmatched value was Returned { Reason = Damaged box }.
</code></pre>
<p>So adding a case to a public closed hierarchy (or a public union) is a <strong>breaking change</strong>. Recompiled consumers get new warnings or errors, and consumers that aren't recompiled get runtime exceptions. The closed hierarchies spec lists this as a drawback too: adding <code>closed</code> to an existing class, or adding a new derived class to a closed one, can be a breaking change. Inside a single solution, that's exactly what you want. For a NuGet package or a reusable module, it's a versioning decision you should make on purpose.</p>
<h2>Using These Features in an ABP Solution</h2>
<p>This is the section I care about most, because ABP adds its own serialization, caching, and layering conventions on top of plain .NET.</p>
<h3>Can you use them today?</h3>
<p>ABP 10.x targets up to <code>net10.0</code>. As with every major .NET release, ABP will ship a .NET 11-based version, <strong>ABP 11</strong>, which is the release to use for production apps on .NET 11. In the meantime, .NET runs <code>net10.0</code> libraries in <code>net11.0</code> apps. To check it in practice, I created a <code>net11.0</code> console app on the RC1 SDK, referenced <strong>ABP 10.6.1</strong> packages (<code>Volo.Abp.Ddd.Domain</code>, <code>Volo.Abp.Json.SystemTextJson</code>, <code>Volo.Abp.Caching</code>), and booted it with <code>AbpApplicationFactory</code>. The app initialized fine, and an <code>AggregateRoot&lt;Guid&gt;</code> with a closed state property compiled cleanly and moved from <code>Waiting</code> to <code>OnTheWay</code> as expected:</p>
<pre><code class="language-csharp">public closed record ShipmentState;
public sealed record Waiting : ShipmentState;
public sealed record OnTheWay(string Carrier) : ShipmentState;

public class Shipment : AggregateRoot&lt;Guid&gt;
{
    public ShipmentState State { get; private set; } = new Waiting();

    protected Shipment() { }

    public Shipment(Guid id) : base(id) { }

    public Shipment Dispatch(string carrier)
    {
        State = State switch
        {
            Waiting =&gt; new OnTheWay(carrier),
            OnTheWay =&gt; throw new BusinessException(&quot;Shipping:AlreadyDispatched&quot;),
        };
        return this;
    }
}
</code></pre>
<blockquote>
<p><strong>Scope of this check:</strong> it's a smoke test of ABP's core, DDD, JSON, and caching packages. It's not a full ABP solution with EF Core, a UI, or generated client proxies. Persistence in particular is its own topic: mapping a closed hierarchy or a union to EF Core columns is outside this article, and I haven't tested it.</p>
</blockquote>
<h3>Where each feature fits in ABP's layers</h3>
<ul>
<li><strong>Domain layer:</strong> closed hierarchies fit aggregate states and value objects perfectly. ABP already encourages entities that are valid from creation and change state through meaningful domain methods. A closed state hierarchy makes those methods exhaustive.</li>
<li><strong>Domain services:</strong> keep throwing <code>BusinessException</code> with namespaced codes for rule violations. That's what ABP's exception handling, localization, and auditing are built around. Keep in mind that ABP maps an <code>IBusinessException</code> to <strong>HTTP 403</strong> by default unless you register a mapping, for example <code>options.Map(&quot;OrderDemo:CannotCancelShippedOrder&quot;, HttpStatusCode.Conflict)</code> with <code>AbpExceptionHttpStatusCodeOptions</code>.</li>
<li><strong>Application layer:</strong> use unions for <em>expected</em> outcomes that the caller must branch on (such as <code>OutOfStock</code>), especially when a domain service returns them to an application service that turns them into a DTO. For the application service contract itself, I'd keep regular DTO classes and put any polymorphic part inside a property typed as a closed DTO hierarchy with explicit discriminators, like <code>OrderDto.State</code> in the sample. I haven't verified how ABP's C#, JavaScript, and Angular client proxy generation handle unions or closed hierarchies, so test your proxies before exposing these types from auto API controllers.</li>
</ul>
<h3>Two ABP-specific gotchas I hit</h3>
<p><strong>1. <code>IJsonSerializer.Serialize(object)</code> drops the discriminator of a top-level closed hierarchy.</strong> ABP's <code>IJsonSerializer.Serialize</code> takes an <code>object</code>, so System.Text.Json serializes the runtime type (<code>InTransit</code>) instead of the declared base type, and the <code>[JsonPolymorphic]</code> metadata on the base is never used. Here's the output from ABP's <code>AbpSystemTextJsonSerializer</code> on RC1:</p>
<pre><code class="language-text">closed hierarchy                  -&gt; {&quot;carrier&quot;:&quot;UPS&quot;}
closed hierarchy as a DTO property -&gt; {&quot;trackingNumber&quot;:&quot;TRK-1&quot;,&quot;state&quot;:{&quot;status&quot;:&quot;inTransit&quot;,&quot;carrier&quot;:&quot;UPS&quot;}}
union                             -&gt; {&quot;trackingNumber&quot;:&quot;TRK-404&quot;}
</code></pre>
<p>The discriminator is there when the closed type is a <em>property</em> of another object, because then the declared type is known. It's also there in MVC responses: ABP's MVC pipeline uses the standard <code>SystemTextJsonOutputFormatter</code>, and a plain MVC controller on RC1 that returns <code>Task&lt;ShipmentStateDto&gt;</code> produced <code>{&quot;status&quot;:&quot;inTransit&quot;,&quot;carrier&quot;:&quot;UPS&quot;}</code> and bound <code>{&quot;status&quot;:&quot;pending&quot;}</code> correctly as input.</p>
<p><strong>2. <code>IDistributedCache&lt;TClosedBase&gt;</code> can write values it can't read back.</strong> ABP's default <code>Utf8JsonDistributedCacheSerializer</code> goes through that same <code>IJsonSerializer.Serialize(object)</code> call. So caching a closed base type directly writes JSON without a discriminator, and reading it fails:</p>
<pre><code class="language-text">cache (base type)        -&gt; NotSupportedException: The JSON payload for polymorphic interface or abstract type
                            'ShipmentStateDto' must specify a type discriminator.
cache (wrapped in a DTO) -&gt; ShipmentDto { TrackingNumber = TRK-1, State = InTransit { Carrier = UPS } }
</code></pre>
<p>The fix is simple: cache a regular class that <em>contains</em> the polymorphic value, which is what ABP cache items usually look like anyway. Unions can't be cache items directly either, because <code>IDistributedCache&lt;TCacheItem&gt;</code> requires <code>TCacheItem : class</code> and a union is a struct.</p>
<h3>A note for ABP module authors</h3>
<p>ABP modules are designed to be extended: virtual methods, replaceable services, and object extensions. <code>closed</code> goes the other way. It tells consumers &quot;you can't add to this&quot;. Both are valid, but don't mix them up by accident:</p>
<ul>
<li>Use <code>closed</code> freely for <strong>internal</strong> state and for DTO families that you intend to version deliberately.</li>
<li>Keep extension points open. If application developers should be able to add their own variants, an open base class plus a default arm is still the right design.</li>
<li>Treat adding a case to a public closed hierarchy or union as a <strong>breaking change</strong> in your release notes, for the versioning reasons shown above.</li>
</ul>
<h2>RC1 Constraints and Production Readiness</h2>
<p>Here's everything I'd keep in mind before relying on these features in production:</p>
<ul>
<li><strong>It's still a release candidate.</strong> RC1 has a go-live license, but GA is expected in November 2026. Recheck behavior on RC2 and GA.</li>
<li><strong>Support lifecycle.</strong> .NET 11 is an STS release with two years of support. .NET 10 is LTS and supported until November 14, 2028. If your policy is LTS-only, these features wait for .NET 12. For ABP, the current stable line targets .NET 10, and the .NET 11-based ABP 11 release will follow, as usual.</li>
<li><strong>The docs are still moving.</strong> Some pages still describe C# 15 as a preview, and the &quot;What's new&quot; page says some features from the union proposal aren't implemented yet. Trust the SDK you run, and check the release notes for each new build.</li>
<li><strong>Only <code>switch</code> expressions are checked.</strong> <code>switch</code> statements and <code>if</code>/<code>else</code> chains don't get exhaustiveness warnings.</li>
<li><strong><code>default</code> escapes the check.</strong> A <code>default</code> union value matches none of its cases (see above).</li>
<li><strong>It's a compiler guarantee, not a runtime one.</strong> Stale binaries throw <code>SwitchExpressionException</code> when they meet a new case.</li>
<li><strong>Closed interfaces don't exist in C# 15.</strong> Only classes and record classes can be <code>closed</code>.</li>
<li><strong>Generic unions have sharp edges.</strong> Type-parameter cases can't be matched with declaration patterns in generic code (<code>CS8780</code>), and some instantiations become ambiguous (<code>CS0457</code>).</li>
<li><strong>Serialization is opt-in and asymmetric.</strong> Unions write without a discriminator and may need a classifier to be read. Closed hierarchies need <code>[JsonPolymorphic]</code> to get a discriminator at all. In ABP, watch out for top-level <code>IJsonSerializer.Serialize(...)</code> calls and cached base types.</li>
<li><strong>Binding limits.</strong> Unions aren't supported for query strings, route values, headers, or form fields, and in SignalR they only work with <code>JsonHubProtocol</code>, not the MessagePack or Newtonsoft protocols.</li>
<li><strong>Performance.</strong> Compiler-generated unions box value-type cases and always store an <code>object?</code>. That's fine for results and messages. For hot paths with value-type cases, the docs show how to write a custom union with the non-boxing access pattern.</li>
<li><strong>Tooling.</strong> You need the .NET 11 SDK (or a Visual Studio version that ships it). Roslyn exposes union cases to analyzers through <code>ITypeSymbol.UnionCaseTypes</code>, so expect analyzer and source-generator support to improve over time.</li>
</ul>
<h2>Adoption and Migration Guidance</h2>
<p>Here's the order I'd adopt these features in an existing codebase, from lowest to highest risk.</p>
<p><strong>1. Get on the SDK and pin it.</strong> Target <code>net11.0</code> and pin the SDK with a <code>global.json</code>, so every developer and CI agent uses the same compiler:</p>
<pre><code class="language-json">{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;11.0.100-rc.1.26425.128&quot;
  }
}
</code></pre>
<p><strong>2. Make non-exhaustive switches fail the build.</strong> This one line in <code>Directory.Build.props</code> turns the feature from a nice warning into a guarantee. It's what produced the build errors in the <code>Refunded</code> screenshot:</p>
<pre><code class="language-xml">&lt;Project&gt;
  &lt;PropertyGroup&gt;
    &lt;WarningsAsErrors&gt;$(WarningsAsErrors);CS8509&lt;/WarningsAsErrors&gt;
  &lt;/PropertyGroup&gt;
&lt;/Project&gt;
</code></pre>
<p><strong>3. Close your internal hierarchies first.</strong> Search for abstract base classes and records whose switches end with <code>_ =&gt; throw</code>. Add <code>closed</code>, seal the leaves, delete the discard arms, and let the compiler show you what was never handled. This change is invisible to anyone outside the assembly, so it's the safest place to start.</p>
<p><strong>4. Replace enum-plus-nullables with state types.</strong> If you have a <code>Status</code> enum and properties that are only valid in some states, move that data into closed state records, like the <code>Order</code> sample does. The invalid combinations simply stop being representable.</p>
<p><strong>5. Turn expected failures into return values.</strong> For operations with a fixed set of outcomes (out of stock, credit limit, duplicate name), return a union of records instead of throwing and catching. Keep exceptions, and <code>BusinessException</code> in ABP, for real rule violations and unexpected failures.</p>
<p><strong>6. Pick the right shape for generic results.</strong> Use a closed generic hierarchy for a reusable <code>Result&lt;T&gt;</code>. If you already use a result library that works for you, there's no rush to replace it; migrate when you touch the code anyway.</p>
<p><strong>7. Be deliberate at the API boundary.</strong> Use closed DTO hierarchies with explicit <code>[JsonDerivedType]</code> names for new contracts. Use unions when you need to preserve an existing discriminator-free contract, add a classifier if you also deserialize them, and test your generated clients.</p>
<p><strong>8. Version public types carefully.</strong> For NuGet packages and reusable ABP modules, treat new cases as breaking changes, and only close a public hierarchy when you're sure consumers shouldn't extend it.</p>
<p>A short checklist to go with it:</p>
<ul>
<li>[ ] SDK pinned, project targets <code>net11.0</code>, no <code>LangVersion</code> override needed</li>
<li>[ ] <code>CS8509</code> promoted to an error</li>
<li>[ ] No <code>_ =&gt;</code> arms in switches over closed hierarchies or unions, unless it's truly intentional</li>
<li>[ ] Leaves of closed hierarchies are <code>sealed</code> (or intentionally <code>closed</code>)</li>
<li>[ ] Unions that can be <code>default</code> have a <code>null</code> arm</li>
<li>[ ] Polymorphic API types have explicit discriminators and are tested with your client generators</li>
<li>[ ] ABP caches store DTO classes that wrap polymorphic values</li>
<li>[ ] Release notes mention every new case in a public hierarchy or union</li>
</ul>
<h2>Running the Sample Yourself</h2>
<p>Everything shown above comes from one small solution. Here's how to set it up and test it.</p>
<h3>Setup</h3>
<p>Install the RC1 SDK side by side. This doesn't require admin rights and doesn't touch your existing SDKs:</p>
<pre><code class="language-powershell">Invoke-WebRequest https://dot.net/v1/dotnet-install.ps1 -OutFile dotnet-install.ps1
./dotnet-install.ps1 -Version 11.0.100-rc.1.26425.128 -InstallDir &quot;$HOME\.dotnet-rc&quot; -NoPath

$env:DOTNET_ROOT = &quot;$HOME\.dotnet-rc&quot;
$env:PATH = &quot;$env:DOTNET_ROOT;$env:PATH&quot;
dotnet --version   # 11.0.100-rc.1.26425.128
</code></pre>
<p>Create the solution:</p>
<pre><code class="language-powershell">dotnet new globaljson --sdk-version 11.0.100-rc.1.26425.128
dotnet new sln -n OrderDemo
dotnet new classlib -n OrderDemo.Domain -o src/OrderDemo.Domain
dotnet new web -n OrderDemo.Api -o src/OrderDemo.Api
dotnet new xunit -n OrderDemo.Tests -o test/OrderDemo.Tests
dotnet sln add src/OrderDemo.Domain src/OrderDemo.Api test/OrderDemo.Tests
dotnet add src/OrderDemo.Api reference src/OrderDemo.Domain
dotnet add test/OrderDemo.Tests reference src/OrderDemo.Domain src/OrderDemo.Api
dotnet add test/OrderDemo.Tests package Shouldly
dotnet add test/OrderDemo.Tests package Microsoft.AspNetCore.Mvc.Testing --prerelease
dotnet add test/OrderDemo.Tests package Microsoft.Extensions.TimeProvider.Testing
</code></pre>
<p>Then add the code from this article: the domain types, <code>Order</code>, <code>OrderPlacementService</code>, the in-memory stores, the API's <code>Program.cs</code> and DTOs, and the <code>Directory.Build.props</code> from the migration section. The API also needs <code>public partial class Program;</code> at the end of <code>Program.cs</code>, so that <code>WebApplicationFactory&lt;Program&gt;</code> can find it.</p>
<h3>Test steps</h3>
<ol>
<li>Run <code>dotnet test</code> from the solution folder.</li>
<li>Run the API with <code>dotnet run --project src/OrderDemo.Api --urls http://localhost:5189</code> and replay the requests from the &quot;Does It Actually Work?&quot; section.</li>
<li>Add <code>public sealed record Refunded(string PaymentId, decimal Amount) : OrderState;</code> to the domain, run <code>dotnet build</code>, and watch the four <code>CS8509</code> errors. Then remove the line again.</li>
</ol>
<h3>Results</h3>
<p>The test project has 24 tests, covering four areas:</p>
<ul>
<li><strong>State transitions:</strong> the happy path, invalid transitions, the refund note, the shipped-order rule, and idempotent cancellation.</li>
<li><strong>Placement outcomes:</strong> every union case, the exhaustive consumer switch, and the <code>default</code> trap.</li>
<li><strong>The generic <code>Result&lt;T&gt;</code>:</strong> <code>Map</code>, <code>Bind</code>, <code>Match</code>, and <code>IsSuccess</code>.</li>
<li><strong>JSON contracts and HTTP:</strong> discriminator output and round trips, union output and ambiguity, and status codes from <code>WebApplicationFactory</code>.</li>
</ul>
<p>All 24 passed on .NET 11 RC1:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-09-23-csharp-15-union-types-and-closed-hierarchies/test-results.png" alt="All 24 tests passing on the .NET 11 RC1 SDK" /></p>
<h2>Conclusion</h2>
<p>Union types and closed hierarchies aren't flashy features. They don't change how your code runs, and at runtime a closed record is just an abstract class and a union is just a struct with an <code>object?</code> inside. What they change is how honest your types are. A method that returns <code>PlaceOrderResult</code> tells you every outcome, an <code>OrderState</code> can't be in a combination that makes no sense, and a new case shows up as a compiler error in every place that needs a decision, instead of as a production incident.</p>
<p>My short version:</p>
<ul>
<li>Use <strong>closed hierarchies</strong> for families of types you own: domain states, events, commands, and DTO families with explicit discriminators.</li>
<li>Use <strong>unions</strong> for fixed sets of <em>existing</em> or unrelated types, and for operation-specific results.</li>
<li>Make <strong>CS8509 an error</strong>, stop writing <code>_ =&gt; throw</code>, and remember the <code>default</code> and versioning edges.</li>
</ul>
<p>In ABP applications, both features fit naturally into the domain and application layers. Just keep polymorphic values inside DTO properties when they go through <code>IJsonSerializer</code> or the distributed cache, and test your client proxies before exposing these types from your APIs.</p>
<p>Thanks for reading, see you in the next one!</p>
<h2>References</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-15">What's new in C# 15</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-11/overview">What's new in .NET 11</a></li>
<li><a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/csharp.md">C# in .NET 11 RC 1 release notes</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union">Union types (C# reference)</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/closed">The <code>closed</code> modifier (C# reference)</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#closed-hierarchy-patterns">Closed hierarchy patterns (C# reference)</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/unions">Unions feature specification</a></li>
<li><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/closed-hierarchies">Closed hierarchies feature specification</a></li>
<li><a href="https://devblogs.microsoft.com/dotnet/unions-and-closed-hierarchies-in-aspnetcore/">Use C# unions and closed hierarchies in ASP.NET Core (.NET Blog)</a></li>
<li><a href="https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core">.NET and .NET Core support policy</a></li>
<li><a href="https://abp.io/docs/latest">ABP Framework documentation</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a23e073-1729-bdc0-6532-e196796555ab" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a23e073-1729-bdc0-6532-e196796555ab" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/we-built-the-workspace-our-ai-agents-actually-needed-then-ran-our-own-company-on-it-xzaononm</guid>
      <link>https://abp.io/community/posts/we-built-the-workspace-our-ai-agents-actually-needed-then-ran-our-own-company-on-it-xzaononm</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>ai</category>
      <category>productivity</category>
      <category>volobox</category>
      <title>We Built the Workspace Our AI Agents Actually Needed, Then Ran Our Own Company On It</title>
      <description>Read to learn how Volobox turns an assigned task into a reviewed pull request with every step visible, permissioned, and audited...</description>
      <pubDate>Wed, 26 Aug 2026 07:06:15 Z</pubDate>
      <a10:updated>2026-08-26T07:06:49Z</a10:updated>
      <content:encoded><![CDATA[Read to learn how Volobox turns an assigned task into a reviewed pull request with every step visible, permissioned, and audited...<br \><a href="https://engincanveske.substack.com/p/volobox-ai-agent-workspace" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a234f10-ec3f-98e6-5732-9672643ccaf7" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a234f10-ec3f-98e6-5732-9672643ccaf7" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.7-rc-has-been-released-2u85sb02</guid>
      <link>https://abp.io/community/posts/abp-platform-10.7-rc-has-been-released-2u85sb02</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>new-features</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP Platform 10.7 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.7 RC (Release Candidate). This blog post introduces the new features and important changes in this version.</description>
      <pubDate>Wed, 05 Aug 2026 07:48:00 Z</pubDate>
      <a10:updated>2026-09-26T00:04:00Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.7 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.7 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this version.</p>
<p>Try this version and provide feedback to help us deliver a more stable ABP v10.7 release. Thanks in advance!</p>
<h2>Get Started with the 10.7 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/refs/heads/dev/docs/en/Blog-Posts/2026-08-05%20v10_7_Preview/studio-switch-to-preview.png" alt="studio-switch-to-preview" /></p>
<h2>Migration Guide</h2>
<p>Check the <a href="https://abp.io/docs/10.7/release-info/migration-guides/abp-10-7">ABP Version 10.7 Migration Guide</a> before upgrading from v10.6 or earlier. It covers the services that take new constructor dependencies, the Blazor antiforgery middleware order, the dependency updates, and the AI Management schema change that requires a new EF Core migration.</p>
<h2>What's New with ABP v10.7?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>BLOB Encryption at Rest and Content Pipeline</li>
<li>HTTP QUERY Method Support</li>
<li>Angular Resource API Proxies</li>
<li>ABP Suite React CRUD Page Generation</li>
<li>ABP Suite Decimal Precision</li>
<li>ABP Studio MCP Configuration</li>
<li>AI Management Web Page Data Sources</li>
<li>Dependency Updates</li>
<li>Other Improvements and Enhancements</li>
</ul>
<h3>BLOB Encryption at Rest and Content Pipeline</h3>
<p>ABP v10.7 adds opt-in, transparent encryption at rest for the BLOB Storing system. Encryption uses AES-256-GCM and works on top of the configured storage provider, so application code can continue using <code>IBlobContainer</code> as before. It requires a platform with AES-GCM support and is not available on .NET Standard 2.0 targets.</p>
<p>You can enable encryption per container and configure the passphrase from your application's secure configuration:</p>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.Configure&lt;ProfilePictureContainer&gt;(container =&gt;
    {
        container.UseEncryption();
    });
});

Configure&lt;AbpBlobStoringEncryptionOptions&gt;(options =&gt;
{
    options.DefaultPassPhrase = context.Configuration[&quot;MyApp:BlobPassPhrase&quot;];
});
</code></pre>
<p>The new BLOB content pipeline lets you transparently transform content when it is saved and read. You can create contributors for compression, validation, watermarking, or other stream transformations without changing the storage provider or the code that uses the container.</p>
<p>Both features are disabled by default. When enabling encryption for a container that already contains plaintext BLOBs, first allow legacy plaintext reads, re-save the existing content, and then remove the legacy option so the container reads encrypted data only.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.7/framework/infrastructure/blob-storing/encryption">BLOB Encryption</a> and <a href="https://abp.io/docs/10.7/framework/infrastructure/blob-storing/pipeline">BLOB Content Pipeline</a> documents and <a href="https://github.com/abpframework/abp/pull/25836">#25836</a> for details.</p>
</blockquote>
<h3>HTTP QUERY Method Support</h3>
<p>ABP now supports the HTTP <code>QUERY</code> method for endpoints that need to send request data without using a query string. A <code>QUERY</code> endpoint is treated as a safe method: like <code>GET</code>, it starts a non-transactional unit of work and is not audited by default. <code>GET</code>, <code>HEAD</code> and <code>QUERY</code> share the <code>AbpAuditingOptions.IsEnabledForGetRequests</code> setting.</p>
<p>To expose an action as a <code>QUERY</code> endpoint, use the ASP.NET Core <code>[AcceptVerbs(&quot;QUERY&quot;)]</code> attribute. Because the method carries a request body, it still requires an anti-forgery token.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.7/framework/api-development/auto-controllers#http-method">Auto API Controllers</a> documentation and <a href="https://github.com/abpframework/abp/pull/25797">#25797</a> for details.</p>
</blockquote>
<h3>Angular Resource API Proxies</h3>
<p>The Angular proxy generator can now generate the <code>GET</code> endpoints against the Resource API. Pass the <code>--resource-api</code> option and every generated <code>GET</code> member returns an <code>rxResource</code>-based <code>ResourceRef</code> instead of an <code>Observable</code>. An endpoint with parameters takes them as a single <code>Signal</code>, a parameterless endpoint has no signal parameter, and the optional request configuration stays a normal argument. The other HTTP methods keep the Observable-based form.</p>
<p>This option requires Angular 22 or later and is disabled by default, so existing generated proxies continue to work without changes. Regenerate the proxies with the option only when you are ready to consume the resource form in your components.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.7/framework/ui/angular/service-proxies">Angular Service Proxies</a> documentation and <a href="https://github.com/abpframework/abp/pull/25761">#25761</a> for details.</p>
</blockquote>
<h3>ABP Suite React CRUD Page Generation</h3>
<p>ABP Suite now supports generating CRUD pages for the React applications in modern solutions, bringing the same productive code-generation experience available for other ABP UI options to React projects. The generation is template-based and does not use AI.</p>
<p>Generated React pages include list, search, sorting, paging, filtering, export, create, edit, single and bulk delete operations. They also support validation, permissions, localization, file upload, navigation properties, many-to-many relationships, and master-detail pages with child create, edit, delete, and paging operations.</p>
<p>The generator respects the entity and field configuration you define in ABP Suite, including <code>ShowOn*</code>, <code>IsFilterable</code>, and <code>ReadonlyOnEditModal</code> options. Navigation lookups use server-side search.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/refs/heads/dev/docs/en/Blog-Posts/2026-08-05%20v10_7_Preview/react-crud-page.gif" alt="" /></p>
<h3>ABP Suite Decimal Precision</h3>
<p>You can now set the precision and scale of a <code>decimal</code> property in ABP Suite. For the relational database providers that support fixed-point columns, the generated entity configuration includes the matching <code>HasPrecision(...)</code> call.</p>
<h3>ABP Studio MCP Configuration</h3>
<p>ABP Studio provides a simpler experience for configuring Model Context Protocol (MCP) integrations. You can add common integrations through focused configuration forms or manage the complete MCP server list as JSON, with support for secret placeholders and secure platform storage. It is available in ABP Studio v3.0.9 and later.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.7/studio/ai-agent-configuration">ABP Studio AI Agent configuration</a> documentation and <a href="https://github.com/abpframework/abp/pull/25870">#25870</a> for details.</p>
</blockquote>
<h3>AI Management Web Page Data Sources</h3>
<p>A workspace data source can now be created from a web page URL, not only from an uploaded file. The page content is converted to markdown and indexed like any other data source, and you can refresh it later to pick up changes to the page.</p>
<p>The model name fields of the workspace configuration can also suggest the available models of the selected provider, so you don't have to remember the exact model names. The OpenAI and Ollama model catalogs are included; a provider without a registered catalog simply has no suggestions.</p>
<h3>Dependency Updates</h3>
<p>ABP v10.7 RC includes the following dependency updates:</p>
<ul>
<li>MudBlazor upgraded to <strong>9.7.0</strong></li>
<li><code>MySql.EntityFrameworkCore</code> upgraded to <strong>10.0.9</strong></li>
</ul>
<blockquote>
<p>Check the <a href="https://abp.io/docs/10.7/package-version-changes">Package Version Changes</a> document for all updates.</p>
</blockquote>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>BLOB storing</strong>: The storage providers have improved support for transformed and non-seekable streams.</li>
<li><strong>Identity sessions</strong>: The inactive session cleanup uses the sign-in time when a session has not recorded a last-accessed time yet, so valid token sessions are not removed too early.</li>
<li><strong>Identity</strong>: The user's last sign-in time is written as a best-effort update in its own unit of work, so a concurrency conflict no longer fails the sign-in request (<a href="https://github.com/abpframework/abp/pull/25905">#25905</a>).</li>
<li><strong>Blazor templates</strong>: <code>UseAntiforgery()</code> is called after <code>UseAuthorization()</code>, which is the order required by ASP.NET Core. Existing solutions keep their own middleware order, so check the migration guide (<a href="https://github.com/abpframework/abp/pull/25874">#25874</a>).</li>
<li><strong>MySQL</strong>: The <code>Guid[]</code> query parameters are mapped correctly, and the passkey and user invitation columns are stored as <code>json</code>.</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/how-i-use-a-custom-ai-skill-to-upgrade-a-large-abp-solution-h5fllft1">How I Use a Custom AI Skill to Upgrade a Large ABP Solution</a> by <a href="https://github.com/kfrancis">Kori Francis</a></li>
<li><a href="https://abp.io/community/articles/why-does-my-tiered-abp-app-show-an-empty-menu-while-the-user-7g46886w">Why Does My Tiered ABP App Show an Empty Menu While the User Is Still Signed In?</a> by <a href="https://github.com/kfrancis">Kori Francis</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.7/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.7 RC and provide feedback to help us release a more stable version.</p>
<p>For the complete list of changes, see the <a href="https://github.com/abpframework/abp/releases/tag/10.7.0-rc.1">ABP 10.7.0-rc.1 release notes</a>.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a22e311-9ac9-b181-bc87-9fc5c4caa1b4" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a22e311-9ac9-b181-bc87-9fc5c4caa1b4" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.6-final-has-been-released-xnnz5p4q</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.6-final-has-been-released-xnnz5p4q</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>version</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP.IO Platform 10.6 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.6 stable version has been released. Read this blog post to learn what's new.</description>
      <pubDate>Mon, 27 Jul 2026 11:30:06 Z</pubDate>
      <a10:updated>2026-09-26T02:33:18Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.6 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.6 stable version has been released.</p>
<h2>What's New With Version 10.6?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/abp-platform-10.6-rc-has-been-released-reoq6kzw">10.6 RC Announcement Post</a>, so there is no need to review them all again. You can check it out for more details.</p>
<p>Here are some of the highlights of this version:</p>
<ul>
<li>Background jobs now support dedicated workers, parallel execution, and successful job retention.</li>
<li>API definition and generated proxies have better support for response content types, remote streams, and multipart uploads.</li>
<li>Angular UI packages and templates have been upgraded to Angular 22.</li>
<li>Antiforgery and OpenIddict flows include important security and reliability improvements.</li>
<li>ABP Commercial adds OpenIddict access-token generation from the UI and React CRUD page generation support in ABP Suite.</li>
<li>AI Management indexing is more resilient for large or memory-constrained workloads.</li>
<li>The final release also includes dependency updates and stability fixes collected during the RC period.</li>
</ul>
<h2>Getting Started with 10.6</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-07-27%20v10_6_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>This version includes explicitly marked migration-impacting changes for specific customization scenarios, especially custom background job stores/workers and custom AI Management document chunk repositories. The new background job runtime features are opt-in and existing applications keep the current behavior unless they enable them explicitly.</p>
<p>Please read the migration guide carefully, if you are upgrading from v10.5 or earlier versions: <a href="https://abp.io/docs/10.6/release-info/migration-guides/abp-10-6">ABP Version 10.6 Migration Guide</a></p>
<p>If you use the Angular UI, also check the dedicated <a href="https://abp.io/docs/10.6/release-info/migration-guides/abp-10-6-angular-22">Angular 22 and ABP 10.6 Upgrade Guide</a>.</p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/announcements/introducing-abp-lowcode-build-real-abp-apps-in-minutes-647ymozi">Introducing ABP Low-Code: Build Real ABP Apps in Minutes</a> by <a href="https://abp.io/community/members/salih">Salih Ozkara</a></li>
<li><a href="https://abp.io/community/articles/building-a-vendor-onboarding-workflow-with-abp-lowcode-1wx0ckzc">Building a Vendor Onboarding Workflow with ABP Low-Code</a> by <a href="https://abp.io/community/members/salih">Salih Ozkara</a></li>
<li><a href="https://abp.io/community/articles/event-recap-wearedevelopers-world-congress-2026-v59t8vfn">Event Recap - WeAreDevelopers World Congress 2026</a> by <a href="https://abp.io/community/members/iremdemirci">Irem Demirci</a></li>
<li><a href="https://abp.io/community/articles/empathy-in-the-workplace-for-software-companies-wsjjw9we">Empathy in the Workplace for Software Companies</a> by <a href="https://abp.io/community/members/alper">Alper Ebicoglu</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.7. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a22b583-b5c3-547b-1879-91b1a838e63a" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a22b583-b5c3-547b-1879-91b1a838e63a" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.6-rc-has-been-released-reoq6kzw</guid>
      <link>https://abp.io/community/posts/abp-platform-10.6-rc-has-been-released-reoq6kzw</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>version</category>
      <category>new-features</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP Platform 10.6 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.6 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.</description>
      <pubDate>Wed, 08 Jul 2026 05:41:45 Z</pubDate>
      <a10:updated>2026-09-25T22:35:38Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.6 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.6 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.6! Thanks to you in advance.</p>
<h2>Get Started with the 10.6 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-07-07%20v10_6_Preview/studio-switch-to-preview.png" alt="studio-switch-to-preview" /></p>
<h2>Migration Guide</h2>
<p>You can check the migration guide if you are upgrading from v10.5 or earlier: <a href="https://abp.io/docs/10.6/release-info/migration-guides/abp-10-6">ABP Version 10.6 Migration Guide</a>.</p>
<h2>What's New with ABP v10.6?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>Background Jobs: Dedicated Workers, Parallel Execution, and Successful Job Retention</li>
<li>API Definition and Proxy Improvements for Content Types and Multipart Uploads</li>
<li>Angular UI: Upgrade to Angular 22</li>
<li>Antiforgery and OpenIddict Security Improvements</li>
<li>OpenIddict: Generate Access Token from the UI</li>
<li>Dependency Updates</li>
</ul>
<h3>Background Jobs: Dedicated Workers, Parallel Execution, and Successful Job Retention</h3>
<p>ABP v10.6 adds three opt-in enhancements to the default background job worker. All of them are disabled by default, so existing applications keep the current behavior unless you enable them explicitly.</p>
<p><strong>Storing successful jobs</strong></p>
<p>By default, a job is deleted as soon as it runs successfully. You can now set <code>StoreSuccessfulJobs = true</code> to keep completed jobs in the store. A new <code>CompletionTime</code> column marks completed jobs, and a cleanup worker prunes them after <code>SuccessfulJobRetentionTime</code> (default: 7 days).</p>
<p><strong>Dedicated workers per job type</strong></p>
<p><code>AddDedicatedWorker(...)</code> registers a worker that processes only the configured job argument types, each with its own distributed lock. The default worker continues handling all remaining job types.</p>
<p><strong>Parallel job execution</strong></p>
<p>Set <code>MaxParallelJobExecutionCount</code> greater than 1 to execute multiple jobs in the same poll cycle. In this mode, each job is claimed with its own distributed lock so different application instances can process different jobs concurrently without running the same job twice.</p>
<p>Example configuration:</p>
<pre><code class="language-csharp">Configure&lt;AbpBackgroundJobWorkerOptions&gt;(options =&gt;
{
    options.StoreSuccessfulJobs = true;
    options.SuccessfulJobRetentionTime = TimeSpan.FromDays(30);

    options.AddDedicatedWorker&lt;EmailJobArgs, SmsJobArgs&gt;(&quot;NotificationWorkerLock&quot;);
    options.AddDedicatedWorker&lt;ReportJobArgs&gt;(&quot;ReportWorkerLock&quot;);

    options.MaxParallelJobExecutionCount = 4;
});
</code></pre>
<p>These options are useful when you need better isolation between job types, higher throughput in clustered deployments, or an audit trail of successfully completed jobs.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.6/framework/infrastructure/background-jobs">Background Jobs</a> documentation and <a href="https://github.com/abpframework/abp/pull/25742">#25742</a> for details.</p>
</blockquote>
<h3>API Definition and Proxy Improvements for Content Types and Multipart Uploads</h3>
<p>ABP v10.6 improves API definition generation and client proxies for file upload scenarios and non-JSON response types.</p>
<p>The API definition now exposes response <code>ContentTypes</code> and an <code>IsRemoteStream</code> flag. C#, jQuery, and Angular proxies can use the declared media type instead of collapsing everything to <code>application/json</code> and <code>text/plain</code>.</p>
<p>For upload DTOs containing <code>IRemoteStreamContent</code>, generated Angular and jQuery proxies now forward <code>FormData</code> as multipart requests instead of silently dropping the file payload or trying to serialize the stream as JSON.</p>
<p>Server-side setup still follows the existing ABP pattern:</p>
<pre><code class="language-csharp">Configure&lt;AbpAspNetCoreMvcOptions&gt;(options =&gt;
{
    options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(UploadFileDto));
});
</code></pre>
<p>Angular client example after proxy regeneration:</p>
<pre><code class="language-typescript">const fd = new FormData();
fd.append('Name', 'logo');
fd.append('File', fileInput.files[0], 'logo.png');
this.fileService.uploadFile(fd).subscribe(result =&gt; ...);
</code></pre>
<p>This closes long-standing gaps in generated proxies for stream-based uploads and improves support for text, blob, and custom response types.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25639">#25639</a> for details.</p>
</blockquote>
<h3>Angular UI: Upgrade to Angular 22</h3>
<p>ABP v10.6 upgrades the Angular UI stack to <strong>Angular 22.0.x</strong>.</p>
<p>This release also improves the locale loading mechanism with a fallback path, so culture resources load more reliably when optional locale files are missing or partially available.</p>
<p>If you maintain a custom Angular UI on top of ABP, plan for the Angular 22 upgrade together with your ABP package update and regenerate proxies after upgrading.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25690">#25690</a> and <a href="https://github.com/abpframework/abp/pull/25734">#25734</a> for details.</p>
</blockquote>
<h3>Antiforgery and OpenIddict Security Improvements</h3>
<p>ABP v10.6 includes several security-focused fixes for mixed authentication scenarios.</p>
<p><strong>Antiforgery claim issuer normalization</strong></p>
<p>When an application serves a token-authenticated SPA and cookie-authenticated MVC pages on the same origin, antiforgery validation could fail because the user id claim issuer differed between JWT and cookie authentication schemes. ABP now normalizes the user id claim issuer while generating and validating antiforgery tokens.</p>
<p>This behavior is enabled by default through <code>AbpAntiForgeryOptions.NormalizeUserIdClaimIssuer</code>. Razor Pages antiforgery validation was also aligned with the same normalization logic, which fixes failures in modules such as Setting Management.</p>
<p><strong>Prevent OpenIddict <code>client_id</code> from leaking into the interactive auth cookie</strong></p>
<p>ABP fixed a case where an OpenIddict authorization request could stamp the requested <code>client_id</code> into the interactive authentication cookie during security-stamp refresh. That could corrupt audit logs and make later cookie-authenticated requests appear to belong to the OAuth client.</p>
<p>The fix strips <code>client_id</code> when the interactive cookie is refreshed. Tokens are unaffected, and cookies that were already corrupted self-heal on the next refresh.</p>
<p><strong>Forward the current access token for authenticated client requests</strong></p>
<p><code>HttpContextAbpAccessTokenProvider</code> now forwards the incoming access token whenever the request is authenticated, including <code>client_credentials</code> requests. This prevents unnecessary fallback to configured identity clients in machine-to-machine scenarios.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25655">#25655</a>, <a href="https://github.com/abpframework/abp/pull/25669">#25669</a>, <a href="https://github.com/abpframework/abp/pull/25711">#25711</a>, and <a href="https://github.com/abpframework/abp/pull/25740">#25740</a> for details.</p>
</blockquote>
<h3>OpenIddict: Generate Access Token from the UI</h3>
<p>ABP Commercial v10.6 RC adds a <strong>Generate Access Token</strong> action to OpenIddict application management pages across MVC, Blazor, MudBlazor, and Angular UIs.</p>
<p>Administrators can request a token for an OpenIddict application directly from the UI. The backend forwards a <code>client_credentials</code> request to <code>/connect/token</code> and returns the generated access token to the caller.</p>
<p>This is especially useful for testing integrations, validating scopes, and troubleshooting machine-to-machine authentication without leaving the admin UI.</p>
<h3>Dependency Updates</h3>
<p>ABP v10.6 RC includes several dependency and package updates:</p>
<ul>
<li>Angular packages upgraded to <strong>22.0.x</strong></li>
<li><code>Microsoft.*</code> and <code>System.*</code> packages upgraded to <strong>10.0.9</strong></li>
<li><code>Microsoft.Data.SqlClient</code> upgraded to <strong>7.0.2</strong></li>
<li><code>Swashbuckle.AspNetCore</code> upgraded to <strong>10.2.3</strong></li>
</ul>
<blockquote>
<p>Check the <a href="https://abp.io/docs/10.6/package-version-changes">Package Version Changes</a> document for all updates.</p>
</blockquote>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>Permission management</strong>: Skip dynamic permission initialization during migration runs to avoid noisy logs when the database is unavailable (<a href="https://github.com/abpframework/abp/pull/25743">#25743</a>).</li>
<li><strong>Security / principal access</strong>: <code>ThreadCurrentPrincipalAccessor</code> now returns an anonymous principal instead of <code>null</code> in non-web contexts (<a href="https://github.com/abpframework/abp/pull/25752">#25752</a>).</li>
<li><strong>Angular proxy generation</strong>: Array parameters are now generated as <code>readonly</code> in Angular proxies (<a href="https://github.com/abpframework/abp/pull/25687">#25687</a>).</li>
<li><strong>Date/time normalization</strong>: Removed misleading warnings when normalizing <code>Unspecified</code> <code>DateTime</code> values near range boundaries (<a href="https://github.com/abpframework/abp/pull/25703">#25703</a>).</li>
<li><strong>AI Management</strong>: Indexing is more resilient under memory pressure in the commercial module.</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/abp-10.5.0-expands-blazor-ui-options-with-mudblazor-support-03rzmlpm">ABP 10.5.0 Expands Blazor UI Options with MudBlazor Support</a> by <a href="https://abp.io/community/members/maliming">Liming Ma</a></li>
<li><a href="https://abp.io/community/articles/angular-22-state-management-signals-signalstore-or-ngrx-yq8zg0nw">Angular 22 State Management: Signals, SignalStore, or NgRx?</a> by <a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a></li>
<li><a href="https://abp.io/community/articles/working-with-dapr-workflows-in-the-abp-framework-6476or18">Working with Dapr Workflows in the ABP Framework</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/my-speakers-view-of-convex-summit-2026-ai-net-conference-3uk6ln1l">My Speaker's View of CONVEX Summit 2026</a> by <a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h3>ABP Summer Campaign: Get Up To 20% Off + $300 in AI Credits</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-07-07%20v10_6_Preview/summer-sale.png" alt="summer-sale" /></p>
<p>Summer is a great time to start building with ABP. From <strong>July 6 to July 20</strong>, we're offering exclusive summer savings on <strong>ABP licenses and renewals</strong>: <strong>20% off new licenses</strong>, <strong>10% off renewals</strong>, and <strong>up to $300 in AI credits</strong> for the <strong>ABP AI Agent</strong> in <strong>ABP Studio</strong>. Whether you're starting a new project or upgrading your development workflow, this limited-time offer helps you save on your license while accelerating development with AI.</p>
<blockquote>
<p>You can read the announcement here: <a href="https://abp.io/community/announcements/abp-summer-campaign-get-up-to-20-off-300-in-ai-credits-r5lqtpg9">ABP Summer Campaign: Get Up To 20% Off + $300 in AI Credits</a>.</p>
</blockquote>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.6/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.6 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a22526b-f406-0b67-5c8f-b03f951dda8e" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a22526b-f406-0b67-5c8f-b03f951dda8e" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.5-final-has-been-released-2u589bsc</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.5-final-has-been-released-2u589bsc</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>abp</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP.IO Platform 10.5 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.5 stable version has been released. Read this post to learn out what's new in ABP 10.5...</description>
      <pubDate>Tue, 30 Jun 2026 11:45:46 Z</pubDate>
      <a10:updated>2026-09-25T21:20:01Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.5 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.5 stable version has been released.</p>
<h2>What's New With Version 10.5?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-10-5-release-candidate-k6oxdfle">10.5 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 10.5</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-06-30%20v10_5_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are no explicitly marked breaking changes in this version. However, there are still some important migration notes for specific scenarios. Please read the migration guide carefully, if you are upgrading from v10.4 or earlier versions: <a href="https://abp.io/docs/10.5/release-info/migration-guides/abp-10-5">ABP Version 10.5 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/angular-22-state-management-signals-signalstore-or-ngrx-yq8zg0nw">Angular 22 State Management: Signals, SignalStore, or NgRx?</a></li>
<li><a href="https://abp.io/community/articles/customizing-the-abp-framework-a-developers-guide-to-nklweri3">Customizing the ABP Framework: A Developer's Guide to LeptonX Theme Overrides in Angular and the Transition to React UI</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/working-with-dapr-workflows-in-the-abp-framework-6476or18">Working with Dapr Workflows in the ABP Framework</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/members/alper">Alper Ebicoglu</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/my-speakers-view-of-convex-summit-2026-ai-net-conference-3uk6ln1l">My Speaker's View of CONVEX Summit 2026</a></li>
<li><a href="https://abp.io/community/articles/ai-isnt-replacing-developers-its-changing-what-good-2016q6ng">AI Isn't Replacing Developers - It's Changing What Good Developers Spend Time On</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/deep-dive-on-abp-ai-agent-the-complete-series-f7jute7n">Deep Dive on ABP AI Agent: The Complete Series</a> by <a href="https://abp.io/community/members/berkansasmaz">Berkan Sasmaz</a>
<ul>
<li>We have created a deep-dive series for ABP Studio's AI Coding Agent. You can read this series to learn the main features of the AI Coding Agent and how it can help you while developing ABP-based solutions.</li>
</ul>
</li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.6. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a222a86-573e-7a30-e909-c002138eb268" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a222a86-573e-7a30-e909-c002138eb268" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/working-with-dapr-workflows-in-the-abp-framework-6476or18</guid>
      <link>https://abp.io/community/posts/working-with-dapr-workflows-in-the-abp-framework-6476or18</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>workflow</category>
      <category>abp-framework</category>
      <category>tutorial</category>
      <title>Working with Dapr Workflows in the ABP Framework</title>
      <description>In this article, we'll build a small Dapr Workflow inside a fresh ABP project and run it end to end. By the time you reach the bottom, you should be able to copy the code, run it, and watch a workflow march through its steps.</description>
      <pubDate>Mon, 29 Jun 2026 17:39:42 Z</pubDate>
      <a10:updated>2026-09-26T00:25:18Z</a10:updated>
      <content:encoded><![CDATA[<h1>Working with Dapr Workflows in the ABP Framework</h1>
<p>Most real business processes don't finish in a single request.</p>
<p>An order gets placed, inventory gets checked, a payment gets charged, and the customer gets notified. Each step can fail, time out, or need a retry. And the whole thing has to survive a process restart without losing its place or charging someone twice.</p>
<p>We usually solve this with a pile of queues, a state table, and a lot of defensive code to track where each process is. It works, but the business logic ends up scattered across handlers and database rows, and nobody can read the flow top to bottom anymore.</p>
<p><a href="https://abp.io/community/search?tag=elsa">I covered <strong>Elsa</strong> in two earlier articles</a> as one way to handle workflows in ABP. <strong>Dapr Workflow</strong> takes a different path: instead of an in-app engine, the workflow engine runs in the <a href="https://docs.dapr.io/concepts/dapr-services/sidecar/"><strong>Dapr sidecar</strong></a>, and you write the process as ordinary C# code that Dapr makes durable. If the host crashes halfway through, the workflow picks up right where it left off.</p>
<p>In this article, we'll build a small Dapr Workflow inside a fresh ABP project and run it end to end. By the time you reach the bottom, you should be able to copy the code, run it, and watch a workflow march through its steps.</p>
<blockquote>
<p><strong>Note:</strong> Versions matter here, because both ABP and Dapr move fast. This article is written in June 2026 against <strong>ABP 10.4</strong> (.NET 10), <strong>Dapr 1.18</strong>, and the <strong><code>Dapr.Workflow</code> 1.18.x</strong> package. The <code>Dapr.Workflow</code> package was rewritten in Dapr 1.17, so older tutorials you find online may use a different API.</p>
</blockquote>
<h2>What Dapr Workflow Actually Is?</h2>
<p>You define a <a href="https://docs.dapr.io/developing-applications/building-blocks/workflow/"><strong>workflow</strong></a> that orchestrates a process, and <a href="https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/#workflows-and-activities"><strong>activities</strong></a> that do the actual work (call a database, hit an API, send an email).</p>
<blockquote>
<p><strong>This is orchestration rather than choreography:</strong> one place drives the process, instead of services reacting to each other's events. The definitions live in your app, but the engine that executes them runs in the Dapr sidecar next to it.</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-28-working-with-dapr-workflows/mermaid1.png" alt="Dapr workflow execution architecture diagram" /></p>
<p>The key idea is <strong>durable execution</strong>. Dapr records every step to a state store, so the workflow can be replayed from history at any time. A crash, a deployment, or a scale-out event doesn't lose progress, and a workflow can run for seconds or for months.</p>
<blockquote>
<p>⚠️ One rule follows from this: <strong>workflow code must be deterministic</strong>. No <code>DateTime.Now</code>, no random values, no direct I/O. Anything non-deterministic goes into an activity. Even logging is affected, so inside a workflow you use <code>context.CreateReplaySafeLogger&lt;T&gt;()</code> instead of a normal logger, otherwise every replay repeats your log lines.</p>
</blockquote>
<p>Under the hood, this all runs on <a href="https://docs.dapr.io/developing-applications/building-blocks/actors/actors-overview/"><strong>Dapr actors</strong></a>, which is why the state store has to support actors. The good news is that the default local setup already handles this, as you'll see in a moment.</p>
<hr />
<h2>A Quick Note on ABP and Dapr</h2>
<p>ABP already ships a set of Dapr integration packages: <code>Volo.Abp.Dapr</code> (the core package), <code>Volo.Abp.EventBus.Dapr</code> and <code>Volo.Abp.AspNetCore.Mvc.Dapr.EventBus</code> (distributed event bus over Dapr pub/sub), <code>Volo.Abp.Http.Client.Dapr</code> (service invocation), and <code>Volo.Abp.DistributedLocking.Dapr</code> (distributed locking). You can read all about them in the <a href="https://abp.io/docs/latest/framework/dapr">ABP Dapr integration documentation</a>.</p>
<p>These cover pub/sub, service-to-service calls, and locking. <strong>Workflows are not part of ABP's Dapr integration</strong>, and that's fine. Dapr Workflow has its own first-class .NET SDK (<code>Dapr.Workflow</code>), and you plug it straight into your ABP app like any other .NET library. So in this article we use the Dapr SDK directly, inside an ABP startup template.</p>
<blockquote>
<p><strong>Note:</strong> If you'd like to see deeper Dapr integration in ABP, or you'd like us to build a dedicated piece around Dapr Workflow, feel free to open a new issue on the <a href="https://github.com/abpframework/abp/issues">ABP GitHub repository</a>. Telling us what you need is the best way to help us prioritize it.</p>
</blockquote>
<hr />
<h2>What We'll Build</h2>
<p>To keep this concrete, we'll build a small <strong>order processing</strong> workflow, the classic example for this kind of thing.</p>
<p>The workflow takes an order, checks inventory, charges the customer, then notifies them. If the item is out of stock, it stops early and returns a rejected result. Nothing fancy on the business side, but it's enough to show the parts that matter: how a workflow chains activities, how state survives across steps, and how you start and track an instance.</p>
<p>Here's the flow we're aiming for:</p>
<ul>
<li>An order comes in with a product, a quantity, and a price</li>
<li><strong>Check inventory</strong>: if there isn't enough stock, reject the order and stop</li>
<li><strong>Process payment</strong>: charge the customer</li>
<li><strong>Notify the customer</strong>: let them know the order went through</li>
<li>Return a final result</li>
</ul>
<p>Each of those steps will be an <strong>activity</strong>, and the workflow is the code that orchestrates them. Let's set up the project and build it.</p>
<h2>Prerequisites</h2>
<p>Before we start, make sure you have these installed:</p>
<ul>
<li><strong>.NET 10 SDK</strong></li>
<li><strong>ABP CLI</strong> (the current Studio CLI). Install it with <code>dotnet tool install -g Volo.Abp.Studio.Cli</code> (or update with <code>dotnet tool update -g Volo.Abp.Studio.Cli</code>)</li>
<li><strong>Docker</strong>, running on your machine</li>
<li><a href="https://docs.dapr.io/getting-started/"><strong>Dapr CLI</strong>, initialized once with <code>dapr init</code></a></li>
</ul>
<p>That last step matters. When you run <code>dapr init</code> in self-hosted mode, Dapr pulls a few containers (including Redis) and writes a default <code>statestore.yaml</code> component. That default state store already has <code>actorStateStore: &quot;true&quot;</code> set, which is exactly what Dapr Workflow needs. So once <code>dapr init</code> finishes, you can run workflows locally with zero extra configuration.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-28-working-with-dapr-workflows/dapr-init-run-result.png" alt="dapr-init-run-result" /></p>
<blockquote>
<p><strong>Pro Tip:</strong> If you ever swap the default Redis store for your own component, double-check that it sets <code>actorStateStore: &quot;true&quot;</code>. Without it, workflows silently fail to start, and it's the line people forget most often.</p>
</blockquote>
<h2>Create the Project</h2>
<p>In this article I'll create a new layered solution with <strong>EF Core</strong> as the database provider, using the ABP CLI.</p>
<blockquote>
<p>If you already have an ABP project, you don't need a new one. You can apply the following steps to your existing solution and skip this section.</p>
</blockquote>
<p>Create a new solution named <code>DaprWorkflowDemo</code> (or whatever you want):</p>
<pre><code class="language-bash">abp new DaprWorkflowDemo
</code></pre>
<p>Once the download finishes, your project boilerplate is ready. Open the solution in your IDE and run the <code>DaprWorkflowDemo.Web</code> project once to confirm the app starts and the UI works.</p>
<blockquote>
<p>Since, we have created the solution via ABP Studio CLI, it automatically runs the initial-tasks, which init database, seed initial data and run <code>abp install-libs</code> command, so, no need run the *<em>DbMigrator</em> project.</p>
</blockquote>
<blockquote>
<p>Default admin username is <strong>admin</strong> and the password is <strong>1q2w3E</strong>*. You can use these credentials to login...</p>
</blockquote>
<p>We'll do all the workflow work inside the <code>DaprWorkflowDemo.Web</code> project, since that's the running host where the workflow engine connects to the sidecar.</p>
<h2>Install the Dapr.Workflow Package</h2>
<p>Open a terminal in the <code>DaprWorkflowDemo.Web</code> project folder and add the package:</p>
<pre><code class="language-bash">dotnet add package Dapr.Workflow
</code></pre>
<p>-&gt; <strong>This single package gives you everything:</strong> the base <code>Workflow&lt;TInput, TOutput&gt;</code> and <code>WorkflowActivity&lt;TInput, TOutput&gt;</code> types, the <code>AddDaprWorkflow</code> registration helper, and the <code>DaprWorkflowClient</code> you use to start and query workflows from code.</p>
<h2>Define the Workflow and Its Activities</h2>
<p>Now let's write the order processing flow we sketched out earlier.</p>
<p>First, create a <code>Workflows</code> folder in the <code>DaprWorkflowDemo.Web</code> project. We'll keep everything there for simplicity.</p>
<p>Every input and output in a workflow gets serialized to the state store, so the types you pass around should be simple, JSON-friendly records (<strong><em>ensure they are serializable!</em></strong>). Let's define them:</p>
<pre><code class="language-csharp">namespace DaprWorkflowDemo.Web.Workflows;

public record OrderPayload(string OrderId, string ProductName, int Quantity, decimal TotalPrice);

public record InventoryResult(bool InStock);

public record OrderResult(string OrderId, string Status);
</code></pre>
<p>Now the workflow itself. A workflow derives from <code>Workflow&lt;TInput, TOutput&gt;</code> and reads top to bottom like a normal method, even though every step is durably persisted:</p>
<pre><code class="language-csharp">using Dapr.Workflow;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace DaprWorkflowDemo.Web.Workflows;

public class OrderProcessingWorkflow : Workflow&lt;OrderPayload, OrderResult&gt;
{
    public override async Task&lt;OrderResult&gt; RunAsync(WorkflowContext context, OrderPayload order)
    {
        var logger = context.CreateReplaySafeLogger&lt;OrderProcessingWorkflow&gt;();
        logger.LogInformation(&quot;Starting order {OrderId}: {Quantity} x {ProductName}&quot;,
            order.OrderId, order.Quantity, order.ProductName);

        // 1. Check inventory
        var inventory = await context.CallActivityAsync&lt;InventoryResult&gt;(
            nameof(CheckInventoryActivity), order);

        if (!inventory.InStock)
        {
            logger.LogWarning(&quot;Order {OrderId} rejected: out of stock&quot;, order.OrderId);
            return new OrderResult(order.OrderId, &quot;Rejected: out of stock&quot;);
        }

        // 2. Process the payment
        await context.CallActivityAsync(nameof(ProcessPaymentActivity), order);

        // 3. Notify the customer
        await context.CallActivityAsync(nameof(NotifyCustomerActivity), order);

        logger.LogInformation(&quot;Order {OrderId} completed&quot;, order.OrderId);
        return new OrderResult(order.OrderId, &quot;Completed&quot;);
    }
}
</code></pre>
<p>A couple of things worth pointing out here.</p>
<ul>
<li><code>CallActivityAsync</code> does not invoke the activity directly. It schedules the work with the workflow engine, which records the result once the activity completes. If the process dies right after the payment step, Dapr replays the workflow, feeds it the already-recorded results for the completed steps, and resumes at the notification step. The customer never gets charged twice. This is the <strong>task chaining</strong> pattern.</li>
<li>Notice the replay-safe logger too. Because the engine replays the workflow to rebuild its state, a normal logger would print the same lines over and over. <code>context.CreateReplaySafeLogger&lt;T&gt;()</code> logs only on the first real pass.</li>
<li>Now the activities. An activity is where the real work happens, and the only place you're allowed to be non-deterministic. It derives from <code>WorkflowActivity&lt;TInput, TOutput&gt;</code> and supports constructor injection, so you can pull in your ABP services, repositories, or any registered dependency:</li>
</ul>
<pre><code class="language-csharp">using Dapr.Workflow;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace DaprWorkflowDemo.Web.Workflows;

public class CheckInventoryActivity : WorkflowActivity&lt;OrderPayload, InventoryResult&gt;
{
    private readonly ILogger&lt;CheckInventoryActivity&gt; _logger;

    public CheckInventoryActivity(ILogger&lt;CheckInventoryActivity&gt; logger)
    {
        _logger = logger;
    }

    public override Task&lt;InventoryResult&gt; RunAsync(WorkflowActivityContext context, OrderPayload order)
    {
        _logger.LogInformation(&quot;Checking inventory for {ProductName}&quot;, order.ProductName);

        // Pretend we queried a stock service or a repository here.
        var inStock = order.Quantity &lt;= 100;

        return Task.FromResult(new InventoryResult(inStock));
    }
}

public class ProcessPaymentActivity : WorkflowActivity&lt;OrderPayload, object?&gt;
{
    private readonly ILogger&lt;ProcessPaymentActivity&gt; _logger;

    public ProcessPaymentActivity(ILogger&lt;ProcessPaymentActivity&gt; logger)
    {
        _logger = logger;
    }

    public override Task&lt;object?&gt; RunAsync(WorkflowActivityContext context, OrderPayload order)
    {
        _logger.LogInformation(&quot;Charging {TotalPrice:C} for order {OrderId}&quot;,
            order.TotalPrice, order.OrderId);

        // Call your real payment provider here.
        return Task.FromResult&lt;object?&gt;(null);
    }
}

public class NotifyCustomerActivity : WorkflowActivity&lt;OrderPayload, object?&gt;
{
    private readonly ILogger&lt;NotifyCustomerActivity&gt; _logger;

    public NotifyCustomerActivity(ILogger&lt;NotifyCustomerActivity&gt; logger)
    {
        _logger = logger;
    }

    public override Task&lt;object?&gt; RunAsync(WorkflowActivityContext context, OrderPayload order)
    {
        _logger.LogInformation(&quot;Notifying customer about order {OrderId}&quot;, order.OrderId);

        // Send an email, push a notification, publish an event, etc.
        return Task.FromResult&lt;object?&gt;(null);
    }
}
</code></pre>
<p>Each activity is isolated, so Dapr can retry a failed one without re-running the whole workflow. The two activities that don't return anything useful use <code>object?</code> as their output type and return <code>null</code>. That's why the workflow calls them with the non-generic <code>CallActivityAsync</code>, which ignores the result.</p>
<p>Here's the shape of the process we just wrote:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-28-working-with-dapr-workflows/mermaid2.png" alt="Order processing workflow flowchart" /></p>
<h2>Register the Workflow</h2>
<p>Workflows and activities need to be registered so the engine knows about them. Open your <code>DaprWorkflowDemoWebModule</code> class and register them in <code>ConfigureServices</code>. Most of the existing code is abbreviated for simplicity:</p>
<pre><code class="language-csharp">using DaprWorkflowDemo.Web.Workflows;
using Dapr.Workflow;

public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    // ... existing ABP configuration ...

    //Configure Dapr Workflows...
    context.Services.AddDaprWorkflow(options =&gt;
    {
        options.RegisterWorkflow&lt;OrderProcessingWorkflow&gt;();

        options.RegisterActivity&lt;CheckInventoryActivity&gt;();
        options.RegisterActivity&lt;ProcessPaymentActivity&gt;();
        options.RegisterActivity&lt;NotifyCustomerActivity&gt;();
    });
}
</code></pre>
<blockquote>
<p><code>AddDaprWorkflow</code> does two things for us. It registers a background worker that connects to the sidecar's workflow engine and hosts your workflow definitions, and it registers a <code>DaprWorkflowClient</code> in the dependency injection container so you can start and query workflows from your own code later.</p>
</blockquote>
<p>That's all the wiring. There's no component YAML to write, because Dapr ships a built-in workflow component named <code>dapr</code> that runs on top of the actor state store we already have.</p>
<h2>Run It With the Dapr Sidecar</h2>
<p>Here's the part that's different from a normal <code>dotnet run</code>. The workflow engine lives in the Dapr sidecar, so the app has to run <strong>alongside</strong> a sidecar. The Dapr CLI handles that for us.</p>
<blockquote>
<p>In this section, I assume that you already run <code>dapr init</code> command before, as explained above. If you haven't run it yet, please first run it and then follow the instructions/commands below.</p>
</blockquote>
<p>First, make sure your database is migrated (run <code>DaprWorkflowDemo.DbMigrator</code> if you haven't). Then, from the <code>DaprWorkflowDemo.Web</code> project folder, start the app with Dapr:</p>
<pre><code class="language-bash">dapr run --app-id dapr-workflow-demo --dapr-http-port 3500 -- dotnet run
</code></pre>
<p>A few notes on this command:</p>
<ul>
<li><code>--app-id</code> is the identity of your app within Dapr. We'll use it nowhere else in this example, but Dapr needs it.</li>
<li><code>--dapr-http-port 3500</code> pins the sidecar's HTTP port so we know where to send requests. You can leave it out and let Dapr pick one, but pinning it keeps the next step simple.</li>
<li>Everything after <code>--</code> is the command Dapr runs for your app. <code>dapr run</code> injects the sidecar's connection details (like the gRPC port) as environment variables, and the <code>Dapr.Workflow</code> worker reads them automatically to connect to the engine.</li>
</ul>
<p>Notice we don't pass <code>--app-port</code> here. That flag is only needed when Dapr has to call <strong>into</strong> your app (for pub/sub or service invocation). For workflows, your app connects <strong>out</strong> to the sidecar over gRPC, so we don't need it for this scenario.</p>
<p>Once it's running, you'll see both the ABP app logs and the Dapr sidecar logs in the same terminal.</p>
<h2>Does It Actually Work?</h2>
<p>The quickest way to test is to talk to the sidecar's <strong>Workflow management HTTP API</strong> directly. This hits Dapr, not your app, which makes it a clean smoke test with no extra endpoint code.</p>
<p>Start a workflow instance. The component name is <code>dapr</code> (the built-in one), the workflow name is the class name, and we pass our own instance ID so it's easy to query:</p>
<pre><code class="language-bash">curl -i -X POST &quot;http://localhost:3500/v1.0/workflows/dapr/OrderProcessingWorkflow/start?instanceID=order-001&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;OrderId&quot;:&quot;order-001&quot;,&quot;ProductName&quot;:&quot;Mechanical Keyboard&quot;,&quot;Quantity&quot;:2,&quot;TotalPrice&quot;:59.90}'
</code></pre>
<p>The request body is the workflow input, and Dapr passes it straight through to your <code>OrderPayload</code>. You should get a <code>202 Accepted</code> back with the instance ID:</p>
<pre><code class="language-json">{ &quot;instanceID&quot;: &quot;order-001&quot; }
</code></pre>
<p>Now query the status of that instance:</p>
<pre><code class="language-bash">curl &quot;http://localhost:3500/v1.0/workflows/dapr/order-001&quot;
</code></pre>
<p>After the workflow finishes, you'll see a <code>COMPLETED</code> status along with the serialized output:</p>
<pre><code class="language-json">{
  &quot;instanceID&quot;: &quot;order-001&quot;,
  &quot;workflowName&quot;: &quot;OrderProcessingWorkflow&quot;,
  &quot;createdAt&quot;: &quot;2026-06-29T15:30:15.038490Z&quot;,
  &quot;lastUpdatedAt&quot;: &quot;2026-06-29T15:30:15.360885500Z&quot;,
  &quot;runtimeStatus&quot;: &quot;COMPLETED&quot;,
  &quot;properties&quot;: {
    &quot;dapr.workflow.input&quot;: &quot;{\&quot;ProductName\&quot;:\&quot;Mechanical Keyboard\&quot;,\&quot;Quantity\&quot;:2,\&quot;OrderId\&quot;:\&quot;order-001\&quot;,\&quot;TotalPrice\&quot;:59.9}&quot;,
    &quot;dapr.workflow.output&quot;: &quot;{\&quot;orderId\&quot;:\&quot;order-001\&quot;,\&quot;status\&quot;:\&quot;Completed\&quot;}&quot;
  }
}
</code></pre>
<p>If you check the terminal, you'll also see the log lines from the workflow and each activity in order:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-28-working-with-dapr-workflows/dapr-workflow-response.png" alt="dapr-workflow-response" /></p>
<p>The same management API also lets you <code>terminate</code>, <code>pause</code>, <code>resume</code>, and <code>purge</code> instances, and <code>raiseEvent</code> to send external events into a waiting workflow. For example:</p>
<pre><code class="language-bash"># Permanently delete a finished workflow's state
curl -X POST &quot;http://localhost:3500/v1.0/workflows/dapr/order-001/purge&quot;
</code></pre>
<p>The <code>DaprWorkflowClient</code> exposes the same operations in code (terminating, suspending and resuming, purging, and raising external events on an instance), which is the way to go for anything beyond a quick manual test.</p>
<h2>Triggering Workflows From Your ABP Code</h2>
<p>Hitting the sidecar API by hand is great for a quick check, but in a real app you'll start workflows from your own code, and this is the recommended path. That's what the <code>DaprWorkflowClient</code> is for, and <code>AddDaprWorkflow</code> already registered it for you.</p>
<p>You can inject it anywhere, for example into a controller or an application service. Here's a minimal controller in the <code>DaprWorkflowDemo.Web</code> project that starts an order and reads its status:</p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using DaprWorkflowDemo.Web.Workflows;
using Dapr.Workflow;
using Microsoft.AspNetCore.Mvc;

namespace DaprWorkflowDemo.Web.Controllers;

[ApiController]
[Route(&quot;api/orders&quot;)]
public class OrderController : ControllerBase
{
    private readonly DaprWorkflowClient _workflowClient;

    public OrderController(DaprWorkflowClient workflowClient)
    {
        _workflowClient = workflowClient;
    }

    [HttpPost]
    public async Task&lt;IActionResult&gt; StartAsync(OrderPayload order)
    {
        var instanceId = await _workflowClient.ScheduleNewWorkflowAsync(
            name: nameof(OrderProcessingWorkflow),
            instanceId: order.OrderId,
            input: order);

        return Accepted($&quot;/api/orders/{instanceId}&quot;, new { instanceId });
    }

    [HttpGet(&quot;{instanceId}&quot;)]
    public async Task&lt;IActionResult&gt; GetStatusAsync(string instanceId)
    {
        var state = await _workflowClient.GetWorkflowStateAsync(instanceId);

        if (state is null || !state.Exists)
        {
            return NotFound();
        }

        return Ok(new
        {
            RuntimeStatus = state.RuntimeStatus.ToString(),
            Output = state.ReadOutputAs&lt;OrderResult&gt;()
        });
    }
}
</code></pre>
<p><code>ScheduleNewWorkflowAsync</code> returns immediately and the workflow runs in the background, so this fits the asynchronous request pattern nicely: return <code>202 Accepted</code> and let the client poll the status endpoint.</p>
<blockquote>
<p>One ABP-specific thing to keep in mind: ABP enforces antiforgery validation for unsafe HTTP methods on cookie-authenticated requests. Server-to-server or <code>curl</code> calls without an auth cookie usually pass straight through, but if you call the <code>POST</code> endpoint from a logged-in browser session and get a <code>400</code> antiforgery error, you can relax the auto validation for this controller through <code>AbpAntiForgeryOptions</code>, the same way the Elsa articles did for the Elsa endpoints.</p>
</blockquote>
<h2>Going Further</h2>
<p>We built a simple linear flow, but <strong>Dapr Workflow</strong> supports the patterns you'll actually need in production, all in plain C#:</p>
<ul>
<li><strong>Fan-out / fan-in</strong>: schedule many activities in parallel and aggregate the results (it's just <code>Select</code> plus <code>Task.WhenAll</code>).</li>
<li><strong>External events</strong>: pause a workflow until a human approves something or another system calls back. This is great for approval flows.</li>
<li><strong>Timers</strong>: durably wait for minutes, days, or months without holding a thread.</li>
<li><strong>Child workflows</strong>: break a big process into smaller workflows with their own history and status.</li>
<li><strong>Retry policies</strong>: give an activity an exponential backoff policy so transient failures recover on their own.</li>
</ul>
<h2>Conclusion</h2>
<p><strong>Dapr Workflow</strong> gives you durable execution for long-running processes without bolting a heavy orchestration engine into your code. The process is plain C# that reads top to bottom, Dapr makes it fault-tolerant by replaying from the state store, and the orchestration stays deterministic while the side effects live in activities.</p>
<p>The nice part for us is that none of this fights with ABP. You create a normal ABP solution, add the <code>Dapr.Workflow</code> package, register your workflows in a module, and run with <code>dapr run</code>. ABP's own Dapr packages still cover pub/sub, service invocation, and locking, so you can mix all of these in the same solution when you need them.</p>
<p>All the code in this article is self-contained, so you can copy it into a fresh ABP project and follow along from top to bottom.</p>
<p>Thanks for reading, see you in the next one!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a2226a4-0583-2f63-1030-93a38b1c6155" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a2226a4-0583-2f63-1030-93a38b1c6155" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/deep-dive-on-abp-ai-agent-9-workflows-7jo1adb1</guid>
      <link>https://abp.io/community/posts/deep-dive-on-abp-ai-agent-9-workflows-7jo1adb1</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>workflow</category>
      <category>logging</category>
      <category>migrations</category>
      <category>abp-studio</category>
      <category>ai</category>
      <title>Deep Dive on ABP AI Agent #9: Workflows</title>
      <description>The code change is often the smallest part of development. The real work is everything around it: builds, migrations, containers, validations, and logs. Instead of repeating these steps in every prompt, I want AI agents to know the workflow. That's the idea behind ABP Studio AI Agent Workflows.</description>
      <pubDate>Fri, 19 Jun 2026 06:39:24 Z</pubDate>
      <a10:updated>2026-09-26T01:46:58Z</a10:updated>
      <content:encoded><![CDATA[<h1>Deep Dive on ABP AI Agent #9: Workflows</h1>
<p>There is a pattern I see in almost every real development session.</p>
<p>The interesting part is the code change, but the repeated part is everything around it:</p>
<ul>
<li>start the containers,</li>
<li>build the affected packages,</li>
<li>add a migration if the model changed,</li>
<li>regenerate proxies if the API contract changed,</li>
<li>restart the application,</li>
<li>run the validation task,</li>
<li>check the logs when something fails...</li>
</ul>
<p>When I work alone, I can do those steps manually. When I work with an AI coding agent, I do not want to keep pasting the same checklist into every prompt. I want the tool to understand that this solution has a normal way of preparing, validating, and recovering after changes.</p>
<p>That is the point of <strong>ABP Studio AI Agent Workflows</strong>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow.jpg" alt="ABP Studio AI Agent Workflows overview" /></p>
<p>Workflows let me define repeatable actions around an agent run. The model can focus on the ambiguous part, understanding the requirement and changing the code, while ABP Studio handles the deterministic parts that should happen before or after the work.</p>
<p>That combination is one of the clearest differences between <strong>ABP AI Coding Agent</strong> and a generic coding assistant. It is not only an editor with a chat panel. It is an agent inside a platform that <strong>already knows how to build, run, migrate, generate proxies, manage containers, execute tasks, and inspect runtime signals.</strong></p>
<h2>Why Workflows Matter?</h2>
<p>LLMs are powerful because they can reason through unclear requirements and modify code across files. But many development steps should not be creative.</p>
<p>If the team always builds a package after an application service change, that should be predictable. If API contract changes require proxy generation, that should not depend on whether I remembered to mention it. If a local run needs containers before the application starts, that setup should not be reinvented in every prompt.</p>
<p>Workflows give ABP Studio a place to encode those repeatable steps.</p>
<p><em><strong>Choose a Workflow and Scope before sending your prompt:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflows-openning.png" alt="Opening the workflow settings panel in ABP Studio" /></p>
<p><em><strong>The selected workflow can be configured through Workflow Settings, where you can create, edit, and manage reusable workflows for common development tasks:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-settings.png" alt="ABP AI Agent workflow settings" /></p>
<p>For me, the value is not only automation. It is also consistency.</p>
<p>Instead of asking:</p>
<pre><code class="language-text">Please implement this, and then build, and maybe regenerate proxies,
and restart the app, and also add a migration if needed.
</code></pre>
<p>I can configure the workflow once and let the agent session carry that context.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/sample-workflow-1.png" alt="Sample ABP AI Agent workflow configuration" /></p>
<p>That makes the prompt cleaner:</p>
<pre><code class="language-text">Add the missing status filter to the order list.
Use the selected workflow for validation after the change.
</code></pre>
<p>The workflow becomes part of the development environment, not a long instruction I repeat manually.</p>
<h2>Before And After The Agent Works</h2>
<p>An AI Agent workflow has two sides:</p>
<ul>
<li><strong>Before steps</strong> prepare the environment before the agent starts coding.</li>
<li><strong>After steps</strong> guide the validation and follow-up work after the main task is complete.</li>
</ul>
<p>Before steps run automatically in <strong>Agent</strong> mode. They are useful for setup actions that should happen before the model receives control, such as starting containers or running a preparation task.</p>
<p>Plan and Ask modes are read-only, so they do not execute before steps. That separation is important. If I am only asking a question or asking for a plan, I do not want Studio to start applications or run mutation-oriented tooling.</p>
<p>After steps are injected into the agent instructions as post-task guidance. The agent is expected to run the relevant post-steps after completing the work, but it can skip actions that do not apply.</p>
<p>For example, if my workflow includes &quot;Add Migration&quot; but the change does not touch the EF Core model, the agent should not create an empty migration just because the workflow exists. The workflow gives deterministic options, but the agent still uses the actual change to decide what is relevant.</p>
<h2>What A Workflow Can Do?</h2>
<p>Workflow actions are built around the things ABP developers already do in ABP Studio.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/workflow-actions.png" alt="ABP AI Agent workflow actions" /></p>
<p>The supported actions include:</p>
<ul>
<li><strong>Build</strong> the solution, selected modules, selected packages, or configured targets.</li>
<li><strong>Start Application</strong> for selected applications, folders, or all runnable applications.</li>
<li><strong>Stop Application</strong> when validation needs a clean state.</li>
<li><strong>Restart Application</strong> after code changes.</li>
<li><strong>Start Containers</strong> for databases, caches, message brokers, or other dependencies.</li>
<li><strong>Stop Containers</strong> when the workflow needs to clean up.</li>
<li><strong>Run Task</strong> for configured ABP Studio tasks.</li>
<li><strong>Add Migration</strong> when entity changes require a database migration.</li>
<li><strong>Generate C# Proxies</strong> after contract changes.</li>
<li><strong>Generate Angular Proxies</strong> after API changes consumed by Angular clients.</li>
</ul>
<p>That list is very ABP-specific.</p>
<p>A generic coding tool can run shell commands, and that is useful. But ABP Studio workflows know about ABP Studio concepts: applications, containers, run profile tasks, packages, modules, migrations, and proxy generation. The agent can use those as first-class actions instead of trying to infer everything from terminal commands.</p>
<h2>Personal And Shared Workflows</h2>
<p>Workflows can be personal or shared.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/shared-with-team.png" alt="Sharing an ABP AI Agent workflow with the team via run profile" /></p>
<ul>
<li>A <strong>personal workflow</strong> is stored locally under the solution workspace. It is useful for my own development habits. Maybe I like restarting a specific app after each agent turn. Maybe I have a local task that only makes sense on my machine.</li>
<li>A <strong>shared workflow</strong> is stored with the active run profile. That makes it suitable for source control and team usage.</li>
</ul>
<p>This is where workflows become more than a convenience feature. A team can encode its normal AI-agent validation path into the solution itself.</p>
<p>For example:</p>
<pre><code class="language-text">Before:
- Start required containers
- Run the prepare-local-environment task

After:
- Build affected packages
- Generate Angular proxies when contracts changed
- Restart the Web and API applications
- Run the smoke-test task
</code></pre>
<p>Every developer using that run profile can work with the same repeatable loop. The workflow does not replace code review or testing, but it raises the baseline for what happens after the agent touches code.</p>
<h2>Workflows Make AI More Deterministic Where It Should Be</h2>
<ul>
<li>I do not want the model to creatively decide whether my team usually runs proxy generation. I want the workflow to encode that.</li>
<li>I do not want every prompt to include a long validation checklist. I want the workflow to carry it.</li>
<li>I do not want an agent to guess which applications belong to the local run. I want ABP Studio's run profile to provide that context.</li>
</ul>
<p>This is why workflows are such a strong fit for ABP Studio AI Coding Agent. The model stays flexible where flexibility helps, and the platform stays deterministic where determinism matters.</p>
<p>The result is a cleaner division of responsibility:</p>
<p>| Responsibility | Best handled by |
| --- | --- |
| Understanding the requirement | AI Agent |
| Finding and editing relevant code | AI Agent |
| Starting known containers | Workflow |
| Running known tasks | Workflow |
| Adding migrations when needed | Agent using workflow action |
| Generating proxies when contracts change | Agent using workflow action |
| Building affected packages | Workflow / Studio tools |
| Investigating runtime failures | Agent using monitoring tools |</p>
<h2>Workflows And Scopes Together</h2>
<p>Workflows are even better when combined with AI Scopes.</p>
<p>Scopes define where the agent can work. Workflows define what repeatable actions should happen around that work.</p>
<p>For example, I can select a <code>Catalog</code> scope and a workflow that:</p>
<ul>
<li>builds the <code>Catalog</code> package,</li>
<li>regenerates proxies if contracts changed,</li>
<li>restarts the public web app,</li>
<li>checks recent exceptions after restart.</li>
</ul>
<p>That is a focused agent loop. The agent does not need the whole repository, and the validation path does not need to be invented from scratch.</p>
<p>This is the kind of full flow that makes ABP AI Coding Agent feel different from tools that only operate at the file-and-terminal level.</p>
<h2>Why This Is Different From Generic Coding Agents</h2>
<p>Generic coding agents can be excellent: Cursor, Claude Code, Codex, Windsurf, and similar tools can read code, edit files, run shell commands, and help across many kinds of projects.</p>
<p>But ABP Studio AI Coding Agent is built for a different experience: <strong>It works inside ABP Studio, where the solution already has run profiles, applications, containers, tasks, modules, packages, migrations, proxy generation, monitoring, Git integration, and ABP-aware analysis.</strong></p>
<p>Workflows use that platform context.</p>
<p>Instead of saying:</p>
<pre><code class="language-text">Run whatever commands seem appropriate.
</code></pre>
<p>I can say:</p>
<pre><code class="language-text">Use the selected ABP Studio workflow.
</code></pre>
<p>That is a very different contract. The workflow is <em>visible, configurable, repeatable, and tied to the solution</em>.</p>
<p>For ABP teams, this matters because the development process is not only code generation. It is code generation plus build, migration, proxy generation, application restart, runtime observation, and review.</p>
<p>ABP Studio AI Coding Agent is designed for that full loop.</p>
<h2>Conclusion</h2>
<p>Workflows make ABP Studio AI Coding Agent more practical for real development.</p>
<p>They let me move repeated setup and validation steps out of my prompt and into the platform:</p>
<ul>
<li>start what needs to be running,</li>
<li>build what needs to be built,</li>
<li>generate what needs to be regenerated,</li>
<li>migrate when a model change requires it,</li>
<li>restart the relevant apps,</li>
<li>and continue the debugging loop with runtime evidence.</li>
</ul>
<p>That is why I see workflows as one of the features that makes ABP AI Coding Agent feel complete.</p>
<p>The agent is not isolated from the development environment. It works inside ABP Studio, with the same solution structure, run profile, tools, and team workflow that I already use.</p>
<p>That is the difference: <strong>not just AI-generated code, but an AI-assisted ABP development flow from change to validation.</strong></p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a21f0c7-e8c6-c478-dd6f-1f2c41c052b2" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a21f0c7-e8c6-c478-dd6f-1f2c41c052b2" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/deep-dive-on-abp-ai-agent-7-scopes-tfqtkdzu</guid>
      <link>https://abp.io/community/posts/deep-dive-on-abp-ai-agent-7-scopes-tfqtkdzu</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>modular</category>
      <category>abp</category>
      <category>abp-studio</category>
      <category>ai</category>
      <title>Deep Dive on ABP AI Agent #7: Scopes</title>
      <description>If I am working on the public side of a modular application, I do not want the agent to redesign the admin side. If I am changing a Catalog module, I do not want it to spend half the session reasoning about Identity, SaaS, or Payment code. If I am fixing one microservice, I do not want the agent to treat the whole platform as editable surface area.

That is where AI Scopes become one of the most important control features in ABP Studio AI Coding Agent.</description>
      <pubDate>Wed, 17 Jun 2026 06:35:09 Z</pubDate>
      <a10:updated>2026-09-26T01:33:28Z</a10:updated>
      <content:encoded><![CDATA[<h1>Deep Dive on ABP AI Agent #7: Scopes</h1>
<p>When I use an AI coding agent in a real ABP solution, I do not always want it to see everything.</p>
<p>That may sound strange at first. More context usually feels better. But in a large solution, more context can also mean more noise, more unrelated files, and more chances for the agent to drift into an area that is not part of the task.</p>
<p>If I am working on the public side of a modular application, I do not want the agent to redesign the admin side. If I am changing a Catalog module, I do not want it to spend half the session reasoning about Identity, SaaS, or Payment code. If I am fixing one microservice, I do not want the agent to treat the whole platform as editable surface area.</p>
<p>That is where <strong>AI Scopes</strong> become one of the most important control features in <strong>ABP Studio AI Coding Agent</strong>.</p>
<h2>Why Scopes Matter?</h2>
<p>Most AI coding agents are very good at reading a folder and making changes. That is useful, but an ABP solution is rarely just a folder.</p>
<p>An ABP solution can contain modules, packages, applications, gateways, background workers, database projects, shared contracts, UI projects, and infrastructure configuration. In a microservice solution, the repository may contain multiple independently meaningful services. In a modular monolith, a single solution may still have clear business boundaries.</p>
<p>-&gt; In those situations, the question is not only: <strong>Can the agent understand the solution?</strong> ❌</p>
<p>-&gt; The better question is: <strong>Which part of the solution should the agent be allowed to work with for this task?</strong> ✅</p>
<p>AI Scopes answer that question directly.</p>
<p>They let me choose the accessible area before the session starts. The agent can then focus on the relevant module, package, solution area, or external folder instead of treating the entire repository as equally relevant.</p>
<p>For me, that changes the feeling of using an AI agent. It is no longer &quot;here is my whole codebase, please be careful.&quot; It becomes &quot;here is the part of the system this task belongs to, work inside that boundary.&quot;</p>
<h2>What An AI Scope Controls?</h2>
<p>An AI Scope <strong>restricts which directories the agent can access during a session</strong>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/auth-identity-scope.png" alt="Auth and Identity scope configuration in ABP Studio" /></p>
<p>Depending on the task, a scope can include:</p>
<ul>
<li>the whole solution,</li>
<li>selected modules,</li>
<li>selected packages,</li>
<li>selected external folders,</li>
<li>or a focused combination of these.</li>
</ul>
<p>The important part is that this is not only a prompt suggestion. It is part of the session context and file access boundary. File paths used by the agent are validated against the resolved scope. If a file is outside the accessible directories, the agent should not treat it as part of the editable workspace.</p>
<p>Scopes also work together with <code>.abpignore</code>. Even if a file is under an accessible directory, files excluded by <code>.abpignore</code> remain blocked. That gives teams two useful layers:</p>
<ul>
<li><strong>Scopes</strong> decide which solution areas are relevant to the task.</li>
<li><strong><code>.abpignore</code></strong> protects files that should stay inaccessible, such as secrets, certificates, environment files, or other sensitive local data.</li>
</ul>
<p>This is a practical control model. I can narrow the agent's working area without pretending that the repository is smaller than it really is.</p>
<h2>Scope Is Locked To The Session</h2>
<p>Another detail I like is that scope belongs to the AI Agent session.</p>
<p>The first message of a session locks the configuration that affects the system prompt, including the active AI Scope. If a background session continues running and I change the foreground scope later, that running session does not silently change its context.</p>
<p>That matters when multiple sessions are active.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/selected-scope.png" alt="Selected AI scope shown in the agent session panel" /></p>
<p>Imagine I have one session working on a Catalog module and another session answering questions about the whole solution. Those sessions should not accidentally share a changing boundary. Each one should keep the scope it started with.</p>
<p>This makes scopes more predictable. I can choose the scope intentionally at the beginning of the work and trust that the session is tied to that decision.</p>
<h2>Focused Autonomy</h2>
<p>Scopes are not about making the agent weaker. They are about making autonomy more focused.</p>
<p>When I narrow the scope, I am not saying the agent is less capable. I am saying the task has a boundary.</p>
<p>For example:</p>
<pre><code class="language-text">Use the Public AI Scope.
Add a small validation improvement to the public product search flow.
Do not inspect or change the Admin side unless you find a direct contract dependency.
</code></pre>
<p>That kind of prompt becomes much stronger when the selected scope already matches the instruction. The agent receives both the natural-language task and the platform-level boundary.</p>
<p>This is especially useful for ABP because ABP applications are built around clear concepts: modules, layers, packages, application services, repositories, DTOs, permissions, localization resources, DbContexts, and run profiles. A scope can follow those boundaries instead of relying only on a long prompt.</p>
<h2>What Scopes Help Prevent?</h2>
<p>Scopes help reduce a few common AI-agent failure modes.</p>
<ul>
<li>First, they reduce <strong>unrelated exploration</strong>. The agent does not need to spend time discovering files that have nothing to do with the task.</li>
<li>Second, they reduce <strong>accidental edits</strong>. When a task belongs to one module, the agent should not casually change another module just because it found a similar type there.</li>
<li>Third, they improve <strong>reviewability</strong>. If I scoped the task to <code>Catalog</code>, and the diff changes <code>Identity</code>, that is immediately suspicious. The boundary makes the review easier.</li>
<li>Fourth, they support <strong>parallel work</strong>. Different sessions can be scoped to different areas, which is useful when independent tasks are running in the same solution.</li>
</ul>
<p>This is one of the places where ABP AI Coding Agent feels different from a generic coding tool. The feature is not only &quot;the model can read fewer files.&quot; It is integrated into ABP Studio's understanding of the solution.</p>
<h2>Scopes And ABP Solution Architecture</h2>
<p>ABP already encourages clear boundaries.</p>
<p>In a layered module, the Domain layer should not depend on the Application layer. HTTP API projects should depend on contracts, not implementation projects. Entity Framework Core and MongoDB integrations should stay behind the domain abstractions. A reusable module should be understandable as a module, not only as a set of files.</p>
<p>AI Scopes fit naturally into that mindset.</p>
<p>If I am working on a Domain change, I can keep the scope close to the module and its required shared contracts. If I am working on UI behavior, I can include the UI package and the related contract package. If I am working on a microservice, I can scope the agent to that service and only add external folders when they are truly required.</p>
<p>That means the agent's working area can follow the same mental model I already use as an ABP developer:</p>
<pre><code class="language-text">What bounded area owns this change?
Which packages are needed to make it safely?
Which parts of the system should stay out of this session?
</code></pre>
<h2>Scopes And Workflows Work Better Together</h2>
<ul>
<li>Scopes define <strong>where</strong> the agent can work.</li>
<li>Workflows define <strong>what deterministic actions</strong> should happen around that work.</li>
</ul>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-7-scopes/scopes-openning.png" alt="ABP AI Coding Agent panel showing scopes and workflows combined" /></p>
<p>That combination is powerful. For example, I can scope the agent to the <code>Catalog</code> module and use a workflow that builds the affected package, regenerates proxies if contracts changed, and restarts the related application.</p>
<p>The scope keeps the coding session focused. The workflow keeps the verification loop repeatable.</p>
<p>This is the larger ABP Studio AI story. It is not only an AI chat window. It is an agent inside a platform that already understands ABP solutions, run profiles, tools, workflows, Git state, and runtime signals.</p>
<h2>Why This Is Different From Generic Coding Agents?</h2>
<p>Tools like Cursor, Claude Code, Codex, and Windsurf are strong general-purpose coding tools. They can read files, edit code, run shell commands, and help with many projects.</p>
<p><strong>ABP AI Coding Agent is different because it is built around ABP Studio's view of an ABP solution.</strong></p>
<p>Scopes are a good example of that difference.</p>
<p>In a generic tool, I can try to simulate scope with a prompt:</p>
<pre><code class="language-text">Only work in this folder.
</code></pre>
<p>That is helpful, but it is still mostly an instruction. In ABP Studio, scope is part of the agent session and file access model. It can be selected intentionally before the work starts, stored with the session, and combined with <code>.abpignore</code>, workflows, tools, plans, and run profile context.</p>
<p>For professional ABP teams, that matters. The goal is not to give an AI agent unlimited access and hope the prompt is clear enough. The goal is to create a controlled development loop where the agent understands the system, works in the right area, uses the right tools, and produces a diff that is easier to trust.</p>
<h2>Conclusion</h2>
<p>AI Scopes make ABP Studio AI Coding Agent feel more deliberate.</p>
<p>They let me say:</p>
<ul>
<li>this is the part of the solution that matters,</li>
<li>this is the boundary for the current session,</li>
<li>this is the context the agent should focus on,</li>
<li>and everything else should stay outside unless we intentionally expand the scope.</li>
</ul>
<p>That is exactly the kind of control I want when using AI in real ABP solutions.</p>
<p>The agent can still be powerful. It can still plan, edit, build, run tools, and iterate. But with scopes, that power is pointed at the right part of the system.</p>
<p>That is the real value: <strong>not just more AI autonomy, but better-shaped AI autonomy for ABP development.</strong></p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a21e677-4a07-b271-5b1f-4482b32b993f" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a21e677-4a07-b271-5b1f-4482b32b993f" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-be2xa2om</guid>
      <link>https://abp.io/community/posts/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-be2xa2om</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-framework</category>
      <category>abp-studio</category>
      <category>tool</category>
      <category>ai</category>
      <title>Deep Dive on ABP AI Agent #4: Integrated ABP Studio Tools</title>
      <description>AI coding agents can read code, but real debugging needs more: exceptions, logs, requests, running apps, containers, tasks, and build results. 

ABP AI Coding Agent is different because it connects directly to ABP Studio. With access to your solution, run profiles, monitoring data, and build tools, it works with the same development context you do without manually copying logs or errors into chat.</description>
      <pubDate>Fri, 12 Jun 2026 05:36:58 Z</pubDate>
      <a10:updated>2026-09-26T01:48:07Z</a10:updated>
      <content:encoded><![CDATA[<h1>Deep Dive on ABP AI Agent #4: Integrated ABP Studio Tools</h1>
<p>When I use an AI coding agent, there is a point where plain code awareness is not enough.</p>
<p>The agent may understand the project structure. It may read the failing method. It may even guess the most likely cause of an error. But in a real development session, I usually need more than a guess. I need the latest exception, the request that caused it, the logs around it, the application that is running, the containers it depends on, the tasks I can execute, and the build result after a fix.</p>
<p>That is where <strong>ABP AI Coding Agent</strong> becomes different from a generic coding assistant. It is not only connected to files. It is connected to <strong>ABP Studio</strong>.</p>
<p>ABP Studio already knows the solution, run profiles, runnable applications, containers, tasks, monitoring data, and build actions. ABP AI Coding Agent can use that context through integrated tools when those tools are enabled. So instead of copying exception details, terminal output, container names, or build logs into the chat manually, I can let the agent work with the same ABP Studio environment I am using.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/tools-with-agent.png" alt="ABP AI Coding Agent with tools" /></p>
<blockquote>
<p><strong>Note:</strong> ABP AI Coding Agent is available directly to ABP license holders. License holders have predefined credits so they can try it without setting up a separate AI workflow first. When those credits run out, they can buy more and continue using the same integrated experience.</p>
</blockquote>
<h2>Why Integrated Tools Matter</h2>
<p>Most coding agents start from the same place: <strong>source code</strong>.</p>
<p>That is useful, but ABP development is not only source code. A running ABP solution has applications, modules, services, containers, database connections, migrations, logs, exceptions, requests, tasks, and build steps. When these pieces are outside the agent's reach, the developer becomes the bridge:</p>
<ul>
<li>Copy this exception.</li>
<li>Paste that log.</li>
<li>Run this build.</li>
<li>Check that container.</li>
<li>Explain which application is currently running.</li>
<li>Tell the agent what failed after the last change.</li>
</ul>
<p>Integrated ABP Studio tools reduce that manual work. They let the agent ask ABP Studio for runtime and solution information directly, within the permission boundary I choose.</p>
<p>The important detail is that this access is still explicit. Tools can be enabled or disabled. If I do not want the agent to use a tool, I can keep it disabled. If I want the agent to troubleshoot with runtime information, I can enable the relevant tools and ask for a more complete investigation.</p>
<p>That gives me a practical balance: the productivity of automation, with a visible boundary around what the agent can use.</p>
<h2>Tool Access: What The Agent Is Allowed To Use</h2>
<p>The tools view is the control point.</p>
<p>This is where ABP Studio shows the integrated tools that can be used by ABP AI Coding Agent. Some tools are for reading runtime information. Some are for interacting with applications. Some are for containers, tasks, or build actions.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-tools-overview.png" alt="ABP AI Coding Agent tools overview" /></p>
<blockquote>
<p>The names are intentionally direct. A monitoring tool that gets exceptions is about exceptions. A build tool is about build validation. A task tool is about ABP Studio tasks. That makes the tool list easy to understand even before using it in a real prompt.</p>
</blockquote>
<p>For me, the key idea is not the individual button names. It is the permission model:</p>
<ul>
<li>If a tool is disabled, the agent should not act as if it has that information.</li>
<li>If a tool is enabled, the agent can use it as part of the current session.</li>
<li>If a task needs runtime evidence, I can enable only the tools needed for that task.</li>
</ul>
<p>This makes ABP AI Coding Agent feel more intentional than a black box. I can decide when it should stay in code reasoning and when it should use ABP Studio's runtime view of the solution.</p>
<h2>Monitoring Tools</h2>
<p>Monitoring tools are the first group I reach for when something fails at runtime.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-monitoring-tools.png" alt="ABP AI Coding Agent monitoring tools" /></p>
<p>These tools help the agent inspect what happened while the application was running. In practice, this means information like <strong>exceptions</strong>, <strong>logs</strong>, <strong>events</strong>, and <strong>request details</strong>.</p>
<p>This is a big difference from a generic coding agent. Without monitoring tools, the agent can read the code and make a reasonable guess. With monitoring tools, it can work from the actual failure.</p>
<p>For example, if a page throws an exception, I can ask:</p>
<pre><code class="language-text">Get the latest exception from ABP Studio Monitoring and explain what failed.
</code></pre>
<p>If the exception tool is enabled, the agent can use that runtime signal. It can look at the exception message, stack trace, request context, and related code. Then it can connect the runtime failure to the implementation.</p>
<p>That changes the debugging loop:</p>
<ol>
<li>Trigger the problem.</li>
<li>Ask the agent to inspect the runtime evidence.</li>
<li>Let it find the related code.</li>
<li>Apply the fix.</li>
<li>Validate again.</li>
</ol>
<p>The developer no longer needs to manually copy the exception from one place and paste it into another. ABP Studio becomes part of the agent's working context and give it <em><strong>harness</strong></em>!</p>
<h2>Application Tools</h2>
<p>Application tools connect the agent to the applications defined in the active ABP Studio run profile.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-application-tools.png" alt="ABP AI Coding Agent application tools" /></p>
<p>This matters because ABP solutions often contain more than one runnable application. A layered solution may have a web application, an API host, a DbMigrator, and other executable projects. A microservice solution may have several services with different roles.</p>
<p>ABP Studio already understands these applications through the solution and run profile. When application tools are available, the agent does not need to rediscover everything from file names or ask me which project is running. It can use ABP Studio's view of the solution.</p>
<p>That is useful for prompts like:</p>
<pre><code class="language-text">Check which application is running and use the relevant runtime information to investigate the problem.
</code></pre>
<p>The benefit is not only convenience. It also reduces mistakes. The agent can reason from the same run profile that I use in ABP Studio, instead of guessing from the repository structure alone.</p>
<h2>Container Tools</h2>
<p>Many ABP applications depend on infrastructure services while running locally.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-container-tools.png" alt="ABP AI Coding Agent container tools" /></p>
<p>A solution may need SQL Server, PostgreSQL, Redis, RabbitMQ, OpenIddict-related services, or other containers depending on the template and modules. When something fails, the cause is not always in application code. Sometimes a required container is not running. Sometimes the application cannot reach a dependency. Sometimes the runtime error is only a symptom of an infrastructure problem.</p>
<p>Container tools give the agent a way to include that part of the environment in the investigation.</p>
<p>Instead of asking the agent to guess why a database connection fails, I can let it check the container context that ABP Studio already has. The agent can then distinguish between:</p>
<ul>
<li>a code problem,</li>
<li>a configuration problem,</li>
<li>a missing or stopped container,</li>
<li>or a dependency that is running but unhealthy.</li>
</ul>
<p>This is one of the places where ABP Studio integration is especially valuable. General coding agents can help with Docker files or connection strings, but they usually do not know the current ABP Studio container state unless I copy it into the prompt. ABP AI Coding Agent can work closer to the actual local development environment.</p>
<h2>Task Tools</h2>
<p>ABP Studio tasks are another part of the development workflow that should not live outside the agent.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-task-tools.png" alt="ABP AI Coding Agent task tools" /></p>
<p>Tasks can represent common solution actions. They may run commands, scripts, or workflow steps that are already configured for the solution. If the team uses ABP Studio tasks to standardize local development, the agent should be able to understand and use that same layer.</p>
<p>That means I can ask for a workflow instead of a raw command:</p>
<pre><code class="language-text">Use the available ABP Studio tasks to validate this change.
</code></pre>
<p>The agent can work with the task names and outputs rather than asking me to remember the exact command. This is helpful in larger solutions where the correct validation step is not obvious from a single project file.</p>
<p>It also keeps the agent aligned with the team's development path. If ABP Studio has the task, the agent can follow that path instead of inventing a one-off command.</p>
<h2>Build Tools</h2>
<p>Build tools close the loop after an implementation or fix.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/abp-agent-build-tools.png" alt="ABP AI Coding Agent build tools" /></p>
<p>The agent should not only change files. It should also help verify that the change still builds.</p>
<p>In a generic coding agent flow, validation often depends on shell access and manually chosen commands. That can work, but it leaves more room for guessing. Which project should be built? Which solution file should be used? Is there an ABP Studio-specific build action already configured?</p>
<p>With ABP Studio build tools, the agent can use the build context exposed by the platform. That makes prompts like this more natural:</p>
<pre><code class="language-text">Apply the fix and run the available build validation.
</code></pre>
<p>For small changes, this may simply confirm that the project compiles. For larger changes, it can become part of a broader loop with tasks, application checks, and monitoring tools.</p>
<p>The important part is that the agent can move from implementation to validation without requiring me to manually transfer output between tools.</p>
<h2>A Practical Tool Access Walkthrough</h2>
<p>Let me make this more concrete with a small debugging scenario.</p>
<p>In the sample application, I deliberately added a runtime exception to the <code>UpdateAsync</code> method of <code>BookAppService.cs</code>:</p>
<pre><code class="language-csharp">throw new Exception(&quot;Sample exception for demonstrating integrated tools!&quot;);
</code></pre>
<p>Then I started the application from ABP Studio and triggered the related request from the browser. The only purpose of this setup is to create a real runtime failure that ABP Studio Monitoring can capture.</p>
<p>The interesting part is not the exception itself. The interesting part is how the agent behaves when the monitoring tool is disabled, and how that changes when the tool is enabled.</p>
<h3>First, Without The Exception Tool</h3>
<p>For the first run, I kept the monitoring tool that retrieves exceptions disabled.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/disabled-monitoring-tools.png" alt="ABP AI Coding Agent with exception monitoring tool disabled" /></p>
<p>Then I asked:</p>
<pre><code class="language-text">Can you get the latest exception from ABP Studio Monitoring and explain what failed?
</code></pre>
<p>At this point, the agent did not have direct access to the exception tool. So it tried to reason around the problem in other ways. It looked for available information, tried to inspect logs from files, and checked the codebase to understand what might be happening.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/without-tool-result.png" alt="ABP AI Coding Agent result without exception tool access" /></p>
<p>That is still useful in some cases, but it is not the best debugging flow. The agent is spending time and context trying to reconstruct a runtime failure indirectly. It may eventually find the suspicious code, but it is working from weaker evidence.</p>
<p>This is exactly why tool access matters. Without the monitoring tool, the agent can reason from files. With the monitoring tool, it can inspect the actual runtime failure.</p>
<h3>Then, With The Exception Tool Enabled</h3>
<p>For the second run, I enabled the monitoring tool that can retrieve exceptions, such as <code>get_exceptions</code>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/enabled-monitoring-tools.png" alt="ABP AI Coding Agent with exception monitoring tool enabled" /></p>
<p>Then I asked:</p>
<pre><code class="language-text">Now the get_exceptions tool is enabled. Please get the latest exception from ABP Studio Monitoring, identify the failing code path, and suggest the smallest fix.
</code></pre>
<p>This time, the behavior was very different. The agent directly used the integrated exception tool, retrieved the exception details from ABP Studio Monitoring, and connected the runtime error to the failing code path.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/with-tool-result-1.png" alt="ABP AI Coding Agent using get_exceptions result" /></p>
<p>It also used the enabled logging tool to verify the surrounding context instead of guessing from the source code alone.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/with-tool-result-2.png" alt="ABP AI Coding Agent correlating exception with logs" /></p>
<p>That is the workflow I want from an integrated coding agent. It does not only say &quot;this code looks suspicious.&quot; It checks the exception, follows the evidence, finds the source of the problem, and proposes the smallest fix.</p>
<p>It is also faster and more efficient. The agent does not need to spend as many tokens searching for indirect clues because ABP Studio can provide the runtime signal directly.</p>
<h3>Adding Logs And Requests</h3>
<p>After that, I asked the agent to use the available monitoring tools together:</p>
<pre><code class="language-text">Use the available monitoring tools to check the related logs and recent requests for this failure. Tell me whether they confirm the same root cause.
</code></pre>
<p>At this point, the agent used the enabled tools for exceptions, requests, and logs.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/step-3-1.png" alt="ABP AI Coding Agent checking monitoring tools together" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/step-3-2.png" alt="ABP AI Coding Agent reviewing exception, request, and log details" /></p>
<p>This is where the ABP Studio integration becomes even more valuable. A runtime problem is rarely just one line of code. There is usually a request, a log entry, an exception, and a running application context around it.</p>
<p>When these tools are available together, the agent can correlate them. It can say, &quot;this request caused this exception, the logs confirm it, and this code path is responsible.&quot;</p>
<p>That is much better than asking the developer to copy each piece manually into the chat.</p>
<h3>Validating The Fix</h3>
<p>Finally, I asked the agent to validate the result:</p>
<pre><code class="language-text">Run the available build or application validation tools and confirm that the problem is fixed.
</code></pre>
<p>The agent used ABP Studio tools to stop the application, build it, and run it again.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-06-18-deep-dive-4-tools/validate-the-fix.png" alt="ABP AI Coding Agent validating the fix with ABP Studio tools" /></p>
<p>This completes the loop. The agent did not only identify the problem. It used the integrated tools to move through the full flow:</p>
<ol>
<li>Read the runtime exception.</li>
<li>Correlate it with logs and requests.</li>
<li>Find the failing code path.</li>
<li>Apply or suggest the focused fix.</li>
<li>Validate the application again.</li>
</ol>
<p>That is the difference I want to highlight in this article. ABP AI Coding Agent is not only a model that can edit files. When ABP Studio tools are enabled, it can participate in the same development workflow I use: observing the running application, understanding the failure, fixing the code, and validating the result.</p>
<h2>Why This Is Different From Generic Coding Agents</h2>
<p>Tools like Cursor, Claude Code, and Codex are powerful. They can read code, edit files, run commands, and help with many software projects.</p>
<p><strong>ABP AI Coding Agent has a different advantage: it is built for the ABP development experience.</strong></p>
<p>It is aware of ABP concepts, ABP solution structure, ABP Studio run profiles, application metadata, containers, monitoring, tasks, and build actions. It is also backed by the ABP Platform: ABP Framework, ABP Commercial, ABP Suite, ABP Studio, and the workflows that connect them.</p>
<p>That platform context matters. When I am building an ABP solution, I do not only want a model that can write C# or TypeScript. I want an assistant that understands the way ABP applications are structured and the way ABP Studio runs them.</p>
<p><strong>That makes the starting point simple:</strong> <em>open the ABP solution, use ABP Studio, <a href="https://abp.io/community/articles/deep-dive-on-abp-ai-agent-1-agent-plan-and-ask-modes-62wteg9t">choose the right mode</a>, enable the tools needed for the task, and work with the agent inside the platform.</em></p>
<h2>Conclusion</h2>
<p>Integrated tools make ABP AI Coding Agent more than a chat window next to the code. They give it a controlled way to work with the same ABP Studio context I already use: <strong>applications</strong>, <strong>containers</strong>, <strong>monitoring</strong>, <strong>tasks</strong>, and <strong>build actions</strong>.</p>
<p>-&gt; <strong>That helps the agent move from &quot;I think this might be the problem&quot; to &quot;I checked the runtime evidence, found the related code, applied the fix, and validated it.&quot;</strong></p>
<p>That is the real value of this part of ABP Studio AI. It brings the coding agent closer to the full development experience, from understanding the solution to running it, observing it, fixing it, and checking the result.</p>
<p>As ABP Studio evolves, more tools can be added to this workflow. That means the agent can become more useful over time without changing the basic idea: <em>ABP AI Coding Agent works best when it is not isolated from the platform, but integrated into it.</em></p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a21cc82-3a76-5b59-ac3c-95b51cced139" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a21cc82-3a76-5b59-ac3c-95b51cced139" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.5-rc-has-been-released-k6oxdfle</guid>
      <link>https://abp.io/community/posts/abp-platform-10.5-rc-has-been-released-k6oxdfle</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>new features</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP Platform 10.5 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.5 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v10.5! Thanks to you in advance.</description>
      <pubDate>Wed, 03 Jun 2026 12:09:35 Z</pubDate>
      <a10:updated>2026-09-26T00:03:35Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.5 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.5 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.5! Thanks to you in advance.</p>
<h2>Get Started with the 10.5 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-06-03%20v10_5_Preview/studio-switch-to-preview.png" alt="studio-switch-to-preview" /></p>
<h2>Migration Guide</h2>
<p>There are no explicitly marked breaking changes in this version. However, there are still some important migration notes for specific scenarios. Please check the migration guide if you are upgrading from v10.4 or earlier: <a href="https://abp.io/docs/10.5/release-info/migration-guides/abp-10-5">ABP Version 10.5 Migration Guide</a>.</p>
<h2>What's New with ABP v10.5?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>S3-Compatible Blob Storage Support</li>
<li>OpenIddict: Default Scope Fallback Options</li>
<li>Dynamic Background Worker Capability Markers</li>
<li>Account: Single-Active Token Provider Improvements</li>
<li>CMS Kit: CodeMirror 6 Update</li>
<li>Shared User Accounts: Remove Users from Tenants</li>
<li>Dependency Updates</li>
</ul>
<h3>S3-Compatible Blob Storage Support</h3>
<p>ABP v10.5 improves the AWS Blob Storing provider so it can work with S3-compatible storage services such as Cloudflare R2, MinIO, Backblaze B2, Wasabi, and DigitalOcean Spaces.</p>
<p>Two new AWS blob provider configuration options are available:</p>
<ul>
<li><code>ServiceURL</code>: Sets the custom S3-compatible service endpoint.</li>
<li><code>DisablePayloadSigning</code>: Sends <code>UNSIGNED-PAYLOAD</code> instead of streaming chunked signing when the target provider does not support AWS SDK v4's default payload signing behavior.</li>
</ul>
<p>Example configuration:</p>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.ConfigureDefault(container =&gt;
    {
        container.UseAws(aws =&gt;
        {
            aws.AccessKeyId = &quot;your-access-key&quot;;
            aws.SecretAccessKey = &quot;your-secret-key&quot;;
            aws.ServiceURL = &quot;https://&lt;account-id&gt;.r2.cloudflarestorage.com&quot;;
            aws.ContainerName = &quot;my-container&quot;;
            aws.DisablePayloadSigning = true;
        });
    });
});
</code></pre>
<p>This is especially useful if you want to keep ABP's blob storing abstraction while using an S3-compatible provider instead of AWS S3 itself.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/22962">#22962</a> for details.</p>
</blockquote>
<h3>OpenIddict: Default Scope Fallback Options</h3>
<p>ABP v10.5 adds opt-in default scope fallback options for OpenIddict token grants.</p>
<p>For <code>client_credentials</code>, <code>password</code>, and token-exchange grants, you can now configure ABP to use the scopes registered on the client application when the token request does not include a <code>scope</code> parameter.</p>
<p>The new options are disabled by default:</p>
<pre><code class="language-csharp">Configure&lt;AbpOpenIddictAspNetCoreOptions&gt;(options =&gt;
{
    options.UseDefaultScopesForClientCredentials = true;
    options.UseDefaultScopesForPassword = true;
    options.UseDefaultScopesForTokenExchange = true;
});
</code></pre>
<p>This gives applications more flexibility for machine-to-machine and integration scenarios while keeping the existing behavior unchanged unless you explicitly enable it.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25356">#25356</a> for details.</p>
</blockquote>
<h3>Dynamic Background Worker Capability Markers</h3>
<p>ABP v10.5 improves the dynamic background worker infrastructure with provider capability markers.</p>
<p>Consumers can now detect whether the active <code>IDynamicBackgroundWorkerManager</code> supports runtime registration and cron scheduling by checking marker interfaces:</p>
<ul>
<li><code>ISupportsRuntimeRegistration</code></li>
<li><code>ISupportsCronScheduling</code></li>
</ul>
<p>Hangfire and Quartz dynamic worker managers support both runtime registration and cron scheduling. The default in-memory manager supports runtime registration only, and now rejects cron expressions with a clearer error message. TickerQ's dynamic worker manager does not expose runtime dynamic scheduling support.</p>
<p>This is useful for modules and tools that need to adapt their UI or behavior based on the active background worker provider.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25397">#25397</a> for details.</p>
</blockquote>
<h3>Account: Single-Active Token Provider Improvements</h3>
<p>ABP continues improving token security in the <a href="https://abp.io/modules/account-pro">Account PRO module</a>.</p>
<p>In v10.5, the link-user token provider now uses ABP's single-active token infrastructure. Only the latest generated link-user token remains valid, and applications can configure the token lifetime through dedicated options.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-06-03%20v10_5_Preview/link-account-demo.mp4" alt="link account demo" /></p>
<p>The default ASP.NET Core Identity token provider used by ABP has also been replaced with an ABP single-active variant. Password-flow challenge tokens, such as two-factor and password-change challenge flows, are now single-active per user and purpose with a short default lifetime.</p>
<p>These changes help reduce the risk of old tokens remaining usable after a newer token has been issued.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25450">#25450</a> and <a href="https://github.com/abpframework/abp/pull/25525">#25525</a> for details.</p>
</blockquote>
<h3>CMS Kit: CodeMirror 6 Update</h3>
<p>ABP v10.5 updates the <code>@abp/codemirror</code> package to CodeMirror 6.</p>
<p>The package keeps compatibility with existing ABP and CMS Kit integrations through a <code>window.CodeMirror.fromTextArea(...)</code> adapter while serving the updated bundled CodeMirror assets from the ABP package.</p>
<p>This modernizes the editor infrastructure used by CMS Kit and related UI features without requiring typical applications to change their CMS Kit usage.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25358">#25358</a> for details.</p>
</blockquote>
<h3>Shared User Accounts: Remove Users from Tenants</h3>
<p>ABP Commercial v10.5 RC improves shared user account administration with a new tenant-side removal action.</p>
<p>Administrators can now remove a shared user from the current tenant directly from the user management UI. This provides an admin-managed counterpart to the self-service leave flow and keeps shared-account administration easier to handle in multi-tenant systems.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-06-03%20v10_5_Preview/remove-from-tenants.png" alt="remove shared user from tenant" /></p>
<h3>Dependency Updates</h3>
<p>ABP v10.5 RC includes several dependency and package updates:</p>
<ul>
<li>Blazorise packages upgraded to <strong>2.1.3</strong></li>
<li>MongoDB.Driver upgraded to <strong>3.9.0</strong></li>
<li>CodeMirror updated to <strong>6.0.2</strong> through <code>@abp/codemirror</code></li>
</ul>
<blockquote>
<p>Check the <a href="https://abp.io/docs/10.5/package-version-changes">Package Version Changes</a> document for all updates.</p>
</blockquote>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>Permission Management + MySQL</strong>: Fixed the <code>ResourcePermissionGrant</code> index length problem that could cause MySQL initial migration failures (<a href="https://github.com/abpframework/abp/pull/25495">#25495</a>).</li>
<li><strong>Distributed locking</strong>: Removed a redundant cancellation-token fallback call in <code>MedallionAbpDistributedLock</code> (<a href="https://github.com/abpframework/abp/pull/25497">#25497</a>).</li>
<li><strong>Documentation and tooling</strong>: Added a docs syntax check workflow for <code>docs/en</code> Markdown files (<a href="https://github.com/abpframework/abp/pull/25415">#25415</a>).</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/fahrigedik">Fahri Gedik</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/new-abp-modern-react-native-template-rxjiyrpb">New Look for ABP React Native: NativeWind, Modernization &amp; Two Sample Apps</a></li>
<li><a href="https://abp.io/community/articles/the-antidote-to-vibe-architecting-abp-studio-ai-agent-mpdeh3gr">The Antidote to Vibe Architecting: ABP Studio AI Agent</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/template-in-product-out-building-hanova-with-the-abp-ai-hcntpk3j">Template In, Product Out: Building Hanova with the ABP AI Agent</a> by <a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a></li>
<li><a href="https://abp.io/community/articles/abp-framework-ai-agent-skills-qccn87tu">Empowering AI Agents with ABP Framework: A Comprehensive Skill Collection</a> by <a href="https://abp.io/community/members/burakdemir">Burak Demir</a></li>
<li><a href="https://abp.io/community/articles/google-pomelli-how-to-market-your-app-1hu48pda">Google Pomelli: How to Market Your App Without Being a Designer</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/devdays-2026-conference-from-a-speakers-view-39d007hs">DevDays 2026 Conf From a Speaker's View</a> by <a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.5/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.5 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a219f90-7299-f0af-a376-3ae3b39f9140" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a219f90-7299-f0af-a376-3ae3b39f9140" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/google-pomelli-how-to-market-your-app-without-being-a-designer-1hu48pda</guid>
      <link>https://abp.io/community/posts/google-pomelli-how-to-market-your-app-without-being-a-designer-1hu48pda</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>google</category>
      <category>design</category>
      <category>marketing</category>
      <category>dailytalking</category>
      <title>Google Pomelli: How to Market Your App Without Being a Designer</title>
      <description>You shipped your app. The product is solid, the landing page is live, and users are starting to find it. Then comes the part nobody warned you about: marketing it consistently.

Creating social media visuals, writing campaign copy, generating product photography. None of that is in the developer job description. And at a small team or on a side project, you’re usually the one doing all of it anyway.

That’s where Google Pomelli comes in.</description>
      <pubDate>Thu, 28 May 2026 12:39:48 Z</pubDate>
      <a10:updated>2026-05-28T13:29:12Z</a10:updated>
      <content:encoded><![CDATA[You shipped your app. The product is solid, the landing page is live, and users are starting to find it. Then comes the part nobody warned you about: marketing it consistently.

Creating social media visuals, writing campaign copy, generating product photography. None of that is in the developer job description. And at a small team or on a side project, you’re usually the one doing all of it anyway.

That’s where Google Pomelli comes in.<br \><a href="https://engincanveske.substack.com/p/google-pomelli-create-campaigns-for-your-products" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a2180c5-f5fc-4d0b-b12d-fec0026890e5" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a2180c5-f5fc-4d0b-b12d-fec0026890e5" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.4-final-has-been-released-e0u81o2z</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.4-final-has-been-released-e0u81o2z</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>version</category>
      <category>updates</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp.io</category>
      <title>ABP.IO Platform 10.4 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.4 stable version has been released. Read this announcement post to learn what's new.</description>
      <pubDate>Thu, 14 May 2026 12:23:58 Z</pubDate>
      <a10:updated>2026-09-26T00:04:31Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.4 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.4 stable version has been released.</p>
<h2>What's New With Version 10.4?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-10-4-release-candidate-7ukyudm0">10.4 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 10.4</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-05-14%20v10_4_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are no explicitly marked breaking changes in this version. However, there are still some important migration notes for specific scenarios. Please read the migration guide carefully, if you are upgrading from v10.3 or earlier versions: <a href="https://abp.io/docs/10.4/release-info/migration-guides/abp-10-4">ABP Version 10.4 Migration Guide</a></p>
<h2>Community News</h2>
<h3>Highlights from the ABP Community</h3>
<p>There have been some important announcements for the ABP community recently. Here are two highlights you may want to check out:</p>
<h4>React UI for ABP Framework Is Finally Here</h4>
<p><img src="https://abp.io/api/posts/cover-picture-source/3a2114d0-5518-38a8-d9b3-ab5100b587a4?v=20260508112328" alt="React UI for ABP Framework Is Finally Here" /> </p>
<p>React support has been one of the most requested topics in the ABP community, and with ABP 10.4, it becomes a first-class UI option in the modern template system. The new React UI is designed for teams that want ABP on the backend and React on the frontend while keeping ABP's built-in application features such as authentication, authorization, localization, multi-tenancy, modularity, runtime configuration, and deployment.</p>
<p>Modern React solutions include your own React application as real source code in the solution, plus the ABP Admin Console for standard module administration screens. This means your product UI stays fully under your control, while ABP still provides a consistent and upgradeable administration experience.</p>
<p>The React stack is built with familiar modern tools, including Vite, TypeScript, TanStack Router, TanStack Query, Axios, Zod, React Hook Form, Tailwind CSS, shadcn/ui, and Vitest. You can create a React UI solution with the <code>--modern</code> flag or by selecting the modern template flow in ABP Studio. You can read the announcement here: <a href="https://abp.io/community/announcements/react-ui-for-abp-framework-is-finally-here-7rfmgb2v">React UI for ABP Framework Is Finally Here</a>.</p>
<h4>Introducing ABP Studio AI Agent</h4>
<p><img src="https://abp.io/api/posts/cover-picture-source/3a212ebc-06c1-e10f-f83c-a90079f988c1?v=20260508112328" alt="Introducing ABP Studio AI Agent" /> </p>
<p>ABP Studio now introduces ABP Agent, a deeply integrated AI coding assistant that understands ABP solutions as complete systems, not just as files in folders. It is aware of ABP concepts such as modules, layers, aggregate roots, repositories, application services, DTOs, permissions, localization, event bus, distributed cache, background jobs, and module dependencies.</p>
<p>ABP Agent works in three modes: Agent mode for implementation, Plan mode for read-only investigation and planning, and Ask mode for Q&amp;A and explanations. It can use ABP Studio's analysis engine to understand the solution structure, build affected projects, start or restart applications, run tasks, generate proxies, add migrations, and inspect runtime feedback such as exceptions, logs, HTTP requests, and distributed events.</p>
<p>The announcement also highlights the broader development loop around ABP Agent: solution runner integration, custom workflows, task runner support, Git and GitHub integration, AI-generated commit messages, and ABP-aware AI code review. You can read the announcement here: <a href="https://abp.io/community/announcements/introducing-abp-studio-ai-agent-o1ni0toc">Introducing ABP Studio AI Agent</a>.</p>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/abp-in-the-ai-era-surviving-evolving-and-staying-relevant-6gyfjfpe">ABP in the AI Era: Surviving, Evolving, and Staying Relevant</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/stop-sprinkling-requiresfeature-everywhere-a-centralized-7znie818">Stop Sprinkling [RequiresFeature] Everywhere — A Centralized Feature Gate for ABP.IO</a> by <a href="https://abp.io/community/members/Mohammad97Dev">Mohammad AlMohammad AlMahmoud</a></li>
<li><a href="https://abp.io/community/articles/top-ai-coding-models-in-2026-which-one-should-developers-use-rivh8x15">Top AI Coding Models in 2026: Which One Should Developers Actually Use?</a> by <a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.5. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a21389e-6d83-fe32-954b-2249f6f4c7a9" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a21389e-6d83-fe32-954b-2249f6f4c7a9" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-in-the-ai-era-surviving-evolving-and-staying-relevant-6gyfjfpe</guid>
      <link>https://abp.io/community/posts/abp-in-the-ai-era-surviving-evolving-and-staying-relevant-6gyfjfpe</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-framework</category>
      <category>ai</category>
      <category>abp-platform</category>
      <category>open source</category>
      <title>ABP in the AI Era: Surviving, Evolving, and Staying Relevant</title>
      <description>How an open-source framework team adapts when AI changes everything — the tradeoffs, the honest challenges, and why a complete platform still beats a general-purpose coding agent.</description>
      <pubDate>Tue, 12 May 2026 16:20:06 Z</pubDate>
      <a10:updated>2026-05-18T03:00:01Z</a10:updated>
      <content:encoded><![CDATA[How an open-source framework team adapts when AI changes everything — the tradeoffs, the honest challenges, and why a complete platform still beats a general-purpose coding agent.<br \><a href="https://engincanveske.substack.com/p/abp-framework-in-the-ai-era-surviving-evolving" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a212f29-e56b-a033-c4dc-44a73d3fe718" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a212f29-e56b-a033-c4dc-44a73d3fe718" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.4-rc-has-been-released-7ukyudm0</guid>
      <link>https://abp.io/community/posts/abp-platform-10.4-rc-has-been-released-7ukyudm0</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>updates</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP Platform 10.4 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.4 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v10.4! Thanks to you in advance.</description>
      <pubDate>Wed, 29 Apr 2026 12:49:02 Z</pubDate>
      <a10:updated>2026-09-26T02:39:15Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.4 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.4 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.4! Thanks to you in advance.</p>
<h2>Get Started with the 10.4 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<blockquote>
<p>The v10.4 RC versions of ABP Studio and the ABP CLI are still being tested and will be released shortly.</p>
</blockquote>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/studio-switch-to-preview.png" alt="studio-switch-to-preview" /></p>
<h2>Migration Guide</h2>
<p>There are no explicitly marked breaking changes in this version. However, there are still some important migration notes for specific scenarios. Please check the migration guide if you are upgrading from v10.3 or earlier: <a href="https://abp.io/docs/10.4/release-info/migration-guides/abp-10-4">ABP Version 10.4 Migration Guide</a>.</p>
<h2>What's New with ABP v10.4?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>URL-Based Localization</li>
<li>Localization File Splitting</li>
<li>Blazor UI: MudBlazor Support</li>
<li>Identity: Single-Use Email/SMS 2FA Token Providers</li>
<li>Account Pro: Passwordless Email Login</li>
<li>AI Management: MCP Server Enhancements</li>
<li>LeptonX: URL-Based Localization and Theme Improvements</li>
<li>Dependency and Security Updates</li>
</ul>
<h3>URL-Based Localization</h3>
<p>ABP v10.4 introduces URL-based localization support. You can now embed the culture directly in the URL path, such as <code>/tr/products</code> or <code>/en/about</code>.</p>
<p>This is especially useful for public websites, documentation sites, e-commerce applications, and any application that needs SEO-friendly and shareable localized URLs. Instead of relying only on query string, cookie, or browser language detection, the selected culture can be part of the URL itself.</p>
<p>You can enable it with a single configuration:</p>
<pre><code class="language-csharp">Configure&lt;AbpRequestLocalizationOptions&gt;(options =&gt;
{
    options.UseRouteBasedCulture = true;
});
</code></pre>
<p>When enabled, ABP automatically handles route registration, URL generation, menu links, and language switching for MVC/Razor Pages, Blazor, and Angular UIs.</p>
<p>For Angular applications, route trees can be wrapped with <code>withOptionalRouteCulturePrefix</code> so the same route configuration can handle both <code>/identity/users</code> and <code>/en/identity/users</code>:</p>
<pre><code class="language-typescript">import { Routes } from '@angular/router';
import { withOptionalRouteCulturePrefix } from '@abp/ng.core';

const appRoutesCore: Routes = [
  // ... your routes
];

export const appRoutes = withOptionalRouteCulturePrefix(appRoutesCore);
</code></pre>
<p>For Blazor applications, ABP built-in module pages already include culture-aware route variants. If you have your own Blazor pages, add culture route variants manually:</p>
<pre><code class="language-razor">@page &quot;/Products&quot;
@page &quot;/{culture}/Products&quot;
</code></pre>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.4/framework/fundamentals/url-based-localization">URL-Based Localization</a> documentation and <a href="https://github.com/abpframework/abp/pull/25174">#25174</a> for details.</p>
</blockquote>
<h3>Localization File Splitting</h3>
<p>ABP localization resources can now use multiple JSON files for the same culture. This is useful for large modules or applications where keeping all localization texts in a single <code>en.json</code> file becomes difficult to maintain.</p>
<p>For example, you can split a resource by feature:</p>
<pre><code class="language-text">Localization/
+-- MyResource/
    +-- en.json
    +-- en_Authors.json
    +-- en_Books.json
    +-- en_Users.json
</code></pre>
<p>ABP merges these files into the same localization dictionary. Files are sorted by name before merging, and if the same key exists in multiple files, the value from the last file wins.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.4/framework/fundamentals/localization">Localization</a> documentation and <a href="https://github.com/abpframework/abp/pull/25227">#25227</a> for details.</p>
</blockquote>
<h3>Blazor UI: MudBlazor Support</h3>
<p>ABP v10.4 starts the <a href="https://mudblazor.com/">MudBlazor</a> integration work for the Blazor UI stack.</p>
<p>This release adds MudBlazor-based package infrastructure, template integration, and module/theme support needed to build ABP Blazor applications with MudBlazor. Blazorise and MudBlazor are now supported side by side, the LeptonX theme works with both UI libraries, and when creating a new Blazor project you can pick which UI library to use.</p>
<p>This is a major UI foundation change, so we especially encourage Blazor users to try the RC and share feedback before the stable release.</p>
<p><em><strong>Selecting the UI library when creating a new Blazor project in ABP Studio:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/mud-studio.png" alt="mud-studio" /></p>
<p><em><strong>MudBlazor-based application home page:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/mud-index.png" alt="mud-index" /></p>
<p><em><strong>MudBlazor-based Identity management page:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/mud-identity.png" alt="mud-identity" /></p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25235">#25235</a> for details.</p>
</blockquote>
<h3>Identity: Single-Use Email/SMS 2FA Token Providers</h3>
<p>ABP v10.4 improves the security model for email and SMS two-factor authentication codes.</p>
<p>Email and phone verification codes now use ABP's single-use token providers. Generated codes are encrypted, stored with an absolute expiration time, and consumed after successful validation. Generating a new code invalidates the previous one.</p>
<p>You can configure token lifetime and code length:</p>
<pre><code class="language-csharp">Configure&lt;AbpEmailTwoFactorTokenProviderOptions&gt;(options =&gt;
{
    options.TokenLifespan = TimeSpan.FromMinutes(5);
    options.CodeLength = 8;
});

Configure&lt;AbpPhoneNumberTwoFactorTokenProviderOptions&gt;(options =&gt;
{
    options.TokenLifespan = TimeSpan.FromMinutes(2);
});
</code></pre>
<p>The authenticator app provider is not affected and continues to use the standard TOTP approach.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.4/modules/identity/two-factor-authentication">Two Factor Authentication</a> documentation and <a href="https://github.com/abpframework/abp/pull/25316">#25316</a> for details.</p>
</blockquote>
<h3>Account Pro: Passwordless Email Login</h3>
<p>ABP Commercial v10.4 RC introduces passwordless email login for the Account Pro module.</p>
<p>Users can sign in by receiving an email login link and/or a one-time password (OTP), depending on the configured login type. Administrators can enable the feature, choose the login mode, and configure token lifetime from the account settings.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/account-settings.png" alt="account-settings" /></p>
<p>The feature is designed with security in mind:</p>
<ul>
<li>Login links and OTPs are single-use.</li>
<li>Resending a login email invalidates previous tokens.</li>
<li>Token operations respect the current tenant context.</li>
<li>Rate limiting helps protect against brute-force and email spam scenarios.</li>
<li>Email enumeration behavior follows the existing account security setting.</li>
</ul>
<p>This feature is especially useful for applications that want a smoother sign-in experience without removing the tenant-aware and security-focused account flow of ABP.</p>
<p><em><strong>&quot;Login via email&quot;:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/login-via-email.png" alt="login-via-email" /></p>
<p><em><strong>Type the One-time Password (OTP) to login:</strong></em></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-29%20v10_4_Preview/login-via-email2.png" alt="login-via-email2" /></p>
<h3>AI Management: MCP Server Enhancements</h3>
<p>The <a href="https://abp.io/docs/latest/modules/ai-management">AI Management module</a> continues to improve its MCP (Model Context Protocol) support.</p>
<p>In this release, MCP server configuration has been enhanced for <code>stdio</code> transport scenarios and workspace relationships. This makes it easier to connect local or process-based MCP servers to AI workspaces and use their tools from the chat playground.</p>
<h3>LeptonX: URL-Based Localization and Theme Improvements</h3>
<p>LeptonX has been updated to work with the new URL-based localization flow across UI types, including Angular language switching and culture-aware navigation.</p>
<p>This release also includes several theme improvements and fixes, such as PathBase-safe menu links, improved custom select synchronization, sidebar menu re-binding after async rendering, and MudBlazor-related theme support.</p>
<h3>Dependency and Security Updates</h3>
<p>ABP v10.4 RC includes several dependency updates and security-related package bumps:</p>
<ul>
<li>OpenIddict upgraded to <strong>7.5.0</strong></li>
<li>MongoDB.Driver upgraded to <strong>3.8.0</strong></li>
<li>Microsoft/System package updates for CVE-2026-40372</li>
<li><code>System.Security.Cryptography.Xml</code> upgraded to <strong>10.0.6</strong></li>
<li><code>@abp/lodash</code> lodash dependency updated</li>
</ul>
<blockquote>
<p>Check <a href="https://abp.io/docs/10.4/package-version-changes">Package Version Changes</a> document for all updates.</p>
</blockquote>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>Virtual File System</strong>: <code>ReplaceEmbeddedByPhysical</code> can now receive exclusion filters, which gives developers more control over included/excluded physical files during development (<a href="https://github.com/abpframework/abp/pull/25284">#25284</a>).</li>
<li><strong>Exception logging</strong>: Complex objects in exception data are now serialized more clearly in logs (<a href="https://github.com/abpframework/abp/pull/25267">#25267</a>).</li>
<li><strong>Feature management</strong>: Improved batch state checker performance and added <code>RequireFeaturesSimpleBatchStateChecker</code> (<a href="https://github.com/abpframework/abp/pull/25276">#25276</a>).</li>
<li><strong>RabbitMQ</strong>: Fixed a potential hang while acquiring a closed channel after RabbitMQ restart (<a href="https://github.com/abpframework/abp/pull/25311">#25311</a>).</li>
<li><strong>Shared user accounts</strong>: Improved shared-user lookup and two-factor authentication flows for shared user scenarios.</li>
<li><strong>Account and SaaS modules</strong>: Improved shared-user invitation and account-page flows in tenant user sharing scenarios.</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/stop-sprinkling-requiresfeature-everywhere-a-centralized-7znie818">Stop Sprinkling [RequiresFeature] Everywhere — A Centralized Feature Gate for ABP.IO</a> by <a href="https://abp.io/community/members/Mohammad97Dev">Mohammad AlMohammad AlMahmoud</a></li>
<li><a href="https://abp.io/community/articles/top-ai-coding-models-in-2026-which-one-should-developers-use-rivh8x15">Top AI Coding Models in 2026: Which One Should Developers Actually Use?</a> by <a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.4/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.4 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a20eb75-fbd9-aef2-7c92-25e32fa6459b" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a20eb75-fbd9-aef2-7c92-25e32fa6459b" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.3-final-has-been-released-aryi10am</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.3-final-has-been-released-aryi10am</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>new-features</category>
      <category>release</category>
      <category>abp-platform</category>
      <category>abp.io</category>
      <title>ABP.IO Platform 10.3 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.3 stable version has been released. Read the blog post to learn what's new with 10.3 stable version.</description>
      <pubDate>Fri, 17 Apr 2026 08:32:25 Z</pubDate>
      <a10:updated>2026-09-26T00:04:00Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.3 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.3 stable version has been released.</p>
<h2>What's New With Version 10.3?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-10-3-release-candidate-hgnpr9jq">10.3 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 10.3</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/rel-10.3/docs/en/Blog-Posts/2026-04-15%20v10_3_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are some important changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v10.2 or earlier versions: <a href="https://abp.io/docs/10.3/release-info/migration-guides/abp-10-3">ABP Version 10.3 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/maliming">Liming Ma</a> has published 6 new posts:
<ul>
<li><a href="https://abp.io/community/articles/dynamic-events-in-abp-dukq95m1">Dynamic Events in ABP</a></li>
<li><a href="https://abp.io/community/articles/dynamic-background-jobs-and-workers-in-abp-wfdkdsq9">Dynamic Background Jobs and Workers in ABP</a></li>
<li><a href="https://abp.io/community/articles/shared-user-accounts-in-abp-multitenancy-mf3bkg79">Shared User Accounts in ABP Multi-Tenancy</a></li>
<li><a href="https://abp.io/community/articles/secure-client-authentication-with-privatekeyjwt-in-abp-b2rf18bc">Secure Client Authentication with private_key_jwt in ABP 10.3</a></li>
<li><a href="https://abp.io/community/articles/operation-rate-limiting-in-abp-framework-f4jtd6sn">Operation Rate Limiting in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/resourcebased-authorization-in-abp-framework-choku1sn">Resource-Based Authorization in ABP Framework</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/turning-abp-workspaces-into-openai-compatible-endpoints-u3ls1gp4">One Endpoint, Many AI Clients: Turning ABP Workspaces into OpenAI-Compatible Models</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/automatically-validate-your-documentation-m3ozgkhv">Automatically Validate Your Documentation: How We Built a Tutorial Validator</a> by <a href="https://abp.io/community/members/mansur.besleney">Mansur Besleney</a></li>
<li><a href="https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3">Automate Localhost Access for Expo: A Guide to Dynamic Cloudflare Tunnels &amp; Dev Builds</a> by <a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.4. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a20acbe-bd63-c4b5-dea1-946a01c6e274" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a20acbe-bd63-c4b5-dea1-946a01c6e274" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.3-rc-has-been-released-hgnpr9jq</guid>
      <link>https://abp.io/community/posts/abp-platform-10.3-rc-has-been-released-hgnpr9jq</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>update</category>
      <category>abp</category>
      <category>new-features</category>
      <category>release</category>
      <category>abp-platform</category>
      <title>ABP Platform 10.3 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.3 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v10.3! Thanks to you in advance.</description>
      <pubDate>Thu, 02 Apr 2026 06:40:41 Z</pubDate>
      <a10:updated>2026-09-26T00:04:35Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.3 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.3 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.3! Thanks to you in advance.</p>
<h2>Get Started with the 10.3 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-01%20v10_3_Preview/studio-switch-to-preview.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-04-01%20v10_3_Preview/studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<p>There are no explicitly marked breaking changes in this version. However, there are still some important migration notes for specific scenarios. Please check the migration guide if you are upgrading from v10.2 or earlier: <a href="https://abp.io/docs/10.3/release-info/migration-guides/abp-10-3">ABP Version 10.3 Migration Guide</a>.</p>
<h2>What's New with ABP v10.3?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>OpenIddict: <code>private_key_jwt</code> Client Authentication + <code>abp generate-jwks</code></li>
<li>Event Bus: String-Based Event Publishing with Dynamic Payload</li>
<li>Background Jobs/Workers: String-Based Publishing with Dynamic Payload</li>
<li>API Definition Endpoint: Descriptions and Documentation Support</li>
<li>Entity Cache: New Batch APIs (<code>FindMany*</code> / <code>GetMany*</code>)</li>
<li>Angular: User/Tenant Sharing and Tenant Switch Experience</li>
<li>Angular: Upgrade to 21.2 + TypeScript 5.9</li>
<li>Introducing the <code>Volo.Abp.LuckyPenny.AutoMapper</code> Provider</li>
<li>Security Improvements (Account Pro Module)</li>
</ul>
<h3>OpenIddict: <code>private_key_jwt</code> Client Authentication + <code>abp generate-jwks</code></h3>
<p>ABP v10.3 introduces end-to-end support for OpenIddict <code>private_key_jwt</code> client authentication.<br />
Instead of using a shared <code>client_secret</code>, clients can now authenticate with an asymmetric key pair: keep the private key on the client, and register the public key (JWKS) on the authorization server.</p>
<p>On the open-source side, ABP CLI now includes the <code>abp generate-jwks</code> command (and the OpenIddict demo was updated accordingly). On the Pro side, OpenIddict application management now supports storing and validating JWKS for confidential applications.</p>
<p>This is especially useful for machine-to-machine and compliance-focused environments where shared secrets are not preferred.</p>
<p><strong>Example - Generate a JWKS with ABP CLI:</strong></p>
<pre><code class="language-bash">abp generate-jwks --alg RS256 --key-size 2048 -o ./keys -f my-client
</code></pre>
<blockquote>
<p>See the community article <a href="https://abp.io/community/articles/secure-client-authentication-with-privatekeyjwt-in-abp-b2rf18bc">Secure Client Authentication with private_key_jwt in ABP 10.3</a> for a full walkthrough.
This approach is especially useful for Pro solutions that manage confidential clients in the administration UI.</p>
</blockquote>
<h3>Event Bus: String-Based Event Publishing with Dynamic Payload</h3>
<p>ABP v10.3 adds string-based publishing and subscription APIs for event-driven integrations.</p>
<p>When you do not know event types at compile time, you can now publish and handle events by name without introducing extra wrapper contracts up front. This is especially useful for plugin ecosystems, partner integrations, and metadata-driven application flows.</p>
<p>This is not a separate eventing model. Dynamic events run through the same ABP infrastructure (including outbox/inbox when configured), can be handled through <code>DynamicEventData</code>, and can coexist with typed handlers for the same event name. Distributed providers support this approach except Dapr, which requires startup-time topic declarations.</p>
<p><strong>Example - Publish by event name:</strong></p>
<pre><code class="language-csharp">await _distributedEventBus.PublishAsync(
    &quot;OrderPlaced&quot;,
    new { OrderId = input.Id, CustomerEmail = input.Email }
);
</code></pre>
<p><strong>Example - Subscribe dynamically at runtime:</strong></p>
<pre><code class="language-csharp">eventBus.Subscribe(&quot;PartnerOrderReceived&quot;,
    new PartnerOrderHandler(context.ServiceProvider));

public class PartnerOrderHandler : IDistributedEventHandler&lt;DynamicEventData&gt;
{
    public Task HandleEventAsync(DynamicEventData eventData)
    {
        // eventData.EventName + eventData.Data
        return Task.CompletedTask;
    }
}
</code></pre>
<blockquote>
<p>See the community article <a href="https://abp.io/community/articles/dynamic-events-in-abp-dukq95m1">Dynamic Events in ABP</a> for details.</p>
</blockquote>
<h3>Background Jobs/Workers: String-Based Publishing with Dynamic Payload</h3>
<p>ABP v10.3 introduces <strong>Dynamic Background Jobs</strong> (<code>IDynamicBackgroundJobManager</code>) and <strong>Dynamic Background Workers</strong> (<code>IDynamicBackgroundWorkerManager</code>) for runtime registration and execution by name.</p>
<p>With these APIs, you can enqueue jobs with dynamic payloads, register handler delegates at startup, and add/update/remove recurring workers at runtime. This is especially useful for plugin architectures, metadata-driven workflows, and tenant-specific scheduling scenarios where task types are not known at compile time.</p>
<p>Dynamic background jobs work through ABP's existing typed job pipeline (including provider integrations), while dynamic workers support runtime schedule management (period/cron depending on provider).</p>
<p><strong>Example - Enqueue a job by name with dynamic payload:</strong></p>
<pre><code class="language-csharp">await _dynamicBackgroundJobManager.EnqueueAsync(&quot;emails&quot;, new
{
    EmailAddress = input.CustomerEmail,
    Subject = &quot;Order Confirmed&quot;,
    Body = $&quot;Your order {input.OrderId} has been placed.&quot;
});
</code></pre>
<p><strong>Example - Update worker schedule at runtime:</strong></p>
<pre><code class="language-csharp">await workerManager.UpdateScheduleAsync(
    &quot;InventorySyncWorker&quot;,
    new DynamicBackgroundWorkerSchedule { Period = 10000 } // 10s
);
</code></pre>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25059">#25059</a> and the community article <a href="https://abp.io/community/articles/dynamic-background-jobs-and-workers-in-abp-wfdkdsq9">Dynamic Background Jobs and Workers in ABP</a> for details.</p>
</blockquote>
<h3>API Definition Endpoint: Descriptions and Documentation Support</h3>
<p>The API definition endpoint can now optionally return richer metadata such as summary/description fields for controllers, actions, and parameters.</p>
<p>This is particularly useful for dynamic client generation, API explorers, and tooling that consumes ABP API metadata directly without requiring OpenAPI parsing.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25022">#25022</a> for details.</p>
</blockquote>
<h3>Entity Cache: New Batch APIs (<code>FindMany*</code> / <code>GetMany*</code>)</h3>
<p>ABP v10.3 extends <code>IEntityCache</code> with batch retrieval APIs so you can resolve multiple entities in a single cache/database flow instead of looping over <code>FindAsync</code>/<code>GetAsync</code>.</p>
<p>It includes both list-based APIs (<code>FindManyAsync</code> / <code>GetManyAsync</code>) and dictionary-based APIs (<code>FindManyAsDictionaryAsync</code> / <code>GetManyAsDictionaryAsync</code>) so you can choose the shape that best matches your access pattern.</p>
<p><strong>Example - List-based batch retrieval (preserves input order):</strong></p>
<pre><code class="language-csharp">var ids = new List&lt;Guid&gt; { id1, id2, id1 };

var products = await _productCache.GetManyAsync(ids);      // throws if any ID is missing
var productsOrNull = await _productCache.FindManyAsync(ids); // null for missing IDs
</code></pre>
<p><strong>Example - Dictionary-based batch retrieval (fast lookup by ID):</strong></p>
<pre><code class="language-csharp">var productsById = await _productCache.GetManyAsDictionaryAsync(ids);
var nullableProductsById = await _productCache.FindManyAsDictionaryAsync(ids);

if (nullableProductsById.TryGetValue(id1, out var product) &amp;&amp; product != null)
{
    // use product
}
</code></pre>
<p>All of these methods are optimized for bulk scenarios by internally batching cache misses via distributed cache multi-get/multi-add operations.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25088">#25088</a> and <a href="https://github.com/abpframework/abp/pull/25090">#25090</a> for details.</p>
</blockquote>
<h3>Angular: User/Tenant Sharing and Tenant Switch Experience</h3>
<p>ABP v10.3 enhances Angular UX for shared-user multi-tenancy scenarios, including invitation flows, tenant switch UX, and related identity/account integrations.</p>
<p>This improves the out-of-the-box experience for applications using tenant user sharing.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25051">#25051</a> for details.</p>
</blockquote>
<h3>Angular: Upgrade to 21.2 + TypeScript 5.9</h3>
<p>ABP v10.3 upgrades Angular to <strong>21.2</strong> and TypeScript to <strong>5.9</strong>, bringing the Angular UI stack to the latest ABP-supported frontend baseline.</p>
<p>This helps you stay current with the modern Angular and TypeScript ecosystem while benefiting from newer compiler/tooling improvements and maintaining compatibility with the ABP Angular packages in this release.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/25072">#25072</a> for details.</p>
</blockquote>
<h3>Introducing the <code>Volo.Abp.LuckyPenny.AutoMapper</code> Provider</h3>
<p>ABP v10.3 introduces <code>Volo.Abp.LuckyPenny.AutoMapper</code> as a new optional provider integration for projects that want to use the LuckyPenny-maintained AutoMapper package.</p>
<p>The existing <code>Volo.Abp.AutoMapper</code> package remains unchanged, and migration is straightforward: replace <code>AbpAutoMapperModule</code> with <code>AbpLuckyPennyAutoMapperModule</code> in your module dependencies while keeping the same ABP-facing namespaces and APIs.</p>
<p>This update also addresses the AutoMapper 14.x vulnerability context (<a href="https://github.com/advisories/GHSA-rvv3-g6hj-g44x">GHSA-rvv3-g6hj-g44x</a>), and ABP documentation was expanded with installation, usage, and migration guidance. For more information, see the documentation: <a href="https://abp.io/docs/10.3/framework/infrastructure/luckypenny-automapper">LuckyPenny AutoMapper Integration</a>.</p>
<h3>Security Improvements (Account Pro Module)</h3>
<p>ABP Commercial v10.3 RC also includes notable account security hardening:</p>
<ul>
<li>Optional CAPTCHA for forgot-password flow</li>
<li>Operation-based rate limiting policies for account confirmation/token operations (including updated/default policies for reset and token endpoints)</li>
<li>Session revocation after sensitive credential operations (password change/reset/admin reset)</li>
<li>Stronger profile picture upload validation (allowed extensions, max size, and magic-bytes checks)</li>
</ul>
<p>These changes are security-focused and are designed to be practical for real projects. Here are the key points and how you can tune them:</p>
<ul>
<li><strong>Forgot-password abuse protection</strong>: You can enable CAPTCHA for forgot-password flows to reduce automated reset attempts.</li>
<li><strong>Operation-level rate limiting</strong>: Token/confirmation/reset operations now rely on policy-based limits, so you can centralize and customize limits per operation.</li>
<li><strong>Safer session behavior</strong>: Password changes/resets now revoke sessions to reduce risk from stolen or long-lived sessions.</li>
<li><strong>Profile picture hardening</strong>: Uploads are checked by extension, size, and file signature (magic bytes), not only by client-provided metadata.</li>
</ul>
<p><strong>Example - Tune profile picture upload restrictions:</strong></p>
<pre><code class="language-csharp">Configure&lt;AbpProfilePictureOptions&gt;(options =&gt;
{
    options.AllowedFileExtensions = new[] { &quot;.jpg&quot;, &quot;.jpeg&quot;, &quot;.png&quot; };
    options.MaxFileSizeInBytes = 2 * 1024 * 1024; // 2 MB
});
</code></pre>
<p><strong>Example - Override account operation rate-limiting policies:</strong></p>
<pre><code class="language-csharp">Configure&lt;AbpOperationRateLimitingOptions&gt;(options =&gt;
{
    options.ConfigurePolicy(
        AbpAccountOperationRateLimitPolicies.SendPasswordResetCode,
        policy =&gt;
        {
            policy.ClearRules();
            policy.PerHour(5);
            policy.PerDay(20);
        });
});
</code></pre>
<blockquote>
<p>See the community article <a href="https://abp.io/community/articles/operation-rate-limiting-in-abp-framework-f4jtd6sn">Operation Rate Limiting in ABP Framework</a> for conceptual guidance.</p>
</blockquote>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>Permission integration endpoint update</strong>: <code>PermissionIntegrationController.IsGrantedAsync</code> now uses <code>HttpPost</code> for large payload scenarios (<a href="https://github.com/abpframework/abp/pull/25177">#25177</a>).</li>
<li><strong>OpenIddict dependency update</strong>: Upgraded to OpenIddict 7.3.0 (<a href="https://github.com/abpframework/abp/pull/25053">#25053</a>).</li>
<li><strong>Autofac integration update</strong>: Upgraded <code>Autofac.Extensions.DependencyInjection</code> to 11.0.0 (<a href="https://github.com/abpframework/abp/pull/25190">#25190</a>).</li>
<li><strong>MongoDB dependency update</strong>: Bumped MongoDB.Driver to 3.7.1 (<a href="https://github.com/abpframework/abp/pull/25114">#25114</a>).</li>
<li><strong>OIDC auth storage options for Angular UI (pro)</strong>: OIDC auth storage is now configurable.</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/maliming">Liming Ma</a> has published 6 new posts:
<ul>
<li><a href="https://abp.io/community/articles/dynamic-events-in-abp-dukq95m1">Dynamic Events in ABP</a></li>
<li><a href="https://abp.io/community/articles/dynamic-background-jobs-and-workers-in-abp-wfdkdsq9">Dynamic Background Jobs and Workers in ABP</a></li>
<li><a href="https://abp.io/community/articles/shared-user-accounts-in-abp-multitenancy-mf3bkg79">Shared User Accounts in ABP Multi-Tenancy</a></li>
<li><a href="https://abp.io/community/articles/secure-client-authentication-with-privatekeyjwt-in-abp-b2rf18bc">Secure Client Authentication with private_key_jwt in ABP 10.3</a></li>
<li><a href="https://abp.io/community/articles/operation-rate-limiting-in-abp-framework-f4jtd6sn">Operation Rate Limiting in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/resourcebased-authorization-in-abp-framework-choku1sn">Resource-Based Authorization in ABP Framework</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/turning-abp-workspaces-into-openai-compatible-endpoints-u3ls1gp4">One Endpoint, Many AI Clients: Turning ABP Workspaces into OpenAI-Compatible Models</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/automatically-validate-your-documentation-m3ozgkhv">Automatically Validate Your Documentation: How We Built a Tutorial Validator</a> by <a href="https://abp.io/community/members/mansur.besleney">Mansur Besleney</a></li>
<li><a href="https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3">Automate Localhost Access for Expo: A Guide to Dynamic Cloudflare Tunnels &amp; Dev Builds</a> by <a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.3/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.3 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a205f19-0a57-10d7-3536-e0ce67342e45" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a205f19-0a57-10d7-3536-e0ce67342e45" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.2-final-has-been-released-x47ytfww</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.2-final-has-been-released-x47ytfww</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>new-version</category>
      <category>release</category>
      <category>abp-io</category>
      <category>abpplatform</category>
      <title>ABP.IO Platform 10.2 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.2 stable version has been released. Read the announcement to see what's new with this version!</description>
      <pubDate>Tue, 31 Mar 2026 11:15:35 Z</pubDate>
      <a10:updated>2026-09-25T20:46:10Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.2 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.2 stable version has been released.</p>
<h2>What's New With Version 10.2?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-10-2-release-candidate-05zatjfq">10.2 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 10.2</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-03-31%20v10_2_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v10.1 or earlier versions: <a href="https://abp.io/docs/10.2/release-info/migration-guides/abp-10-2">ABP Version 10.2 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/maliming">Liming Ma</a> has published 6 new posts:
<ul>
<li><a href="https://abp.io/community/articles/dynamic-events-in-abp-dukq95m1">Dynamic Events in ABP</a></li>
<li><a href="https://abp.io/community/articles/dynamic-background-jobs-and-workers-in-abp-wfdkdsq9">Dynamic Background Jobs and Workers in ABP</a></li>
<li><a href="https://abp.io/community/articles/shared-user-accounts-in-abp-multitenancy-mf3bkg79">Shared User Accounts in ABP Multi-Tenancy</a></li>
<li><a href="https://abp.io/community/articles/secure-client-authentication-with-privatekeyjwt-in-abp-b2rf18bc">Secure Client Authentication with private_key_jwt in ABP 10.3</a></li>
<li><a href="https://abp.io/community/articles/operation-rate-limiting-in-abp-framework-f4jtd6sn">Operation Rate Limiting in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/resourcebased-authorization-in-abp-framework-choku1sn">Resource-Based Authorization in ABP Framework</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/turning-abp-workspaces-into-openai-compatible-endpoints-u3ls1gp4">One Endpoint, Many AI Clients: Turning ABP Workspaces into OpenAI-Compatible Models</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/automatically-validate-your-documentation-m3ozgkhv">Automatically Validate Your Documentation: How We Built a Tutorial Validator</a> by <a href="https://abp.io/community/members/mansur.besleney">Mansur Besleney</a></li>
<li><a href="https://abp.io/community/articles/automate-localhost-access-for-expo-a-guide-to-dynamic-7cblqtj3">Automate Localhost Access for Expo: A Guide to Dynamic Cloudflare Tunnels &amp; Dev Builds</a> by <a href="https://abp.io/community/members/sumeyye.kurtulus">Sumeyye Kurtulus</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.3. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a2055c8-0229-3af8-ebb3-17e80abb1b9b" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a2055c8-0229-3af8-ebb3-17e80abb1b9b" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/one-endpoint-many-ai-clients-turning-abp-workspaces-into-openaicompatible-models-u3ls1gp4</guid>
      <link>https://abp.io/community/posts/one-endpoint-many-ai-clients-turning-abp-workspaces-into-openaicompatible-models-u3ls1gp4</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-framework</category>
      <category>openapi</category>
      <category>abp</category>
      <category>api</category>
      <category>ai</category>
      <title>One Endpoint, Many AI Clients: Turning ABP Workspaces into OpenAI-Compatible Models</title>
      <description>With ABP v10.2, there is a major addition: you can now expose workspaces through OpenAI-compatible endpoints under "/v1" route.

That changes the integration story in a practical way. Instead of wiring every external tool directly to a provider, you can point those tools to ABP and keep runtime decisions centralized in one place.

In this post, we will walk through a practical setup with AnythingLLM and show why this pattern is useful in real projects.</description>
      <pubDate>Tue, 17 Mar 2026 10:28:00 Z</pubDate>
      <a10:updated>2026-09-26T00:09:48Z</a10:updated>
      <content:encoded><![CDATA[<h1>One Endpoint, Many AI Clients: Turning ABP Workspaces into OpenAI-Compatible Models</h1>
<p>ABP's AI Management module already makes it easy to define and manage AI workspaces (provider, model, API key/base URL, system prompt, permissions, MCP tools, RAG settings, and more). With <strong>ABP v10.2</strong>, there is a major addition: you can now expose those workspaces through <strong>OpenAI-compatible endpoints</strong> under <code>/v1</code>.</p>
<p>That changes the integration story in a practical way. Instead of wiring every external tool directly to a provider, you can point those tools to ABP and keep runtime decisions centralized in one place.</p>
<p>In this post, we will walk through a practical setup with <strong>AnythingLLM</strong> and show why this pattern is useful in real projects.</p>
<p>Before we get into the details, here's a quick look at the full flow in action:</p>
<h2>See It in Action: AnythingLLM + ABP</h2>
<p>The demo below shows the full flow: connecting an OpenAI-compatible client to ABP, selecting a workspace-backed model, and sending a successful chat request through <code>/v1</code>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-03-17-OpenAI-Compatible-Endpoints/openai-compatible-endpoints-demo.gif" alt="ABP AI Management OpenAI-compatible endpoints demo" /></p>
<h2>Why This Is a Big Deal</h2>
<p>Many teams end up with AI configuration spread across multiple clients and services. Updating providers, rotating keys, or changing model behavior can become operationally messy.</p>
<p>With ABP in front of your AI traffic:</p>
<ul>
<li>Clients keep speaking the familiar OpenAI contract.</li>
<li>ABP resolves the requested <code>model</code> to a workspace.</li>
<li>The workspace decides which provider/model settings are actually used.</li>
</ul>
<p>This gives you a clean split: standardized client integration outside, governed AI configuration inside.</p>
<h2>Key Concept: Workspace = Model</h2>
<p>OpenAI-compatible clients send a <code>model</code> value.
In ABP AI Management, that <code>model</code> maps to a <strong>workspace name</strong>.</p>
<p><strong>For example:</strong></p>
<ul>
<li>Workspace name: <code>SupportAgent</code></li>
<li>Client request model: <code>SupportAgent</code></li>
</ul>
<p>When the client calls <code>/v1/chat/completions</code> with <code>&quot;model&quot;: &quot;SupportAgent&quot;</code>, ABP routes the request to that workspace and applies that workspace's provider (OpenAI, Ollama etc.) and model configuration.</p>
<p>This is the main mental model to keep in mind while integrating any OpenAI-compatible tool with ABP.</p>
<h2>Endpoints Exposed by ABP v10.2</h2>
<p>The AI Management module exposes OpenAI-compatible REST endpoints at <code>/v1</code>.</p>
<p>| Endpoint                     | Method | Description                                    |
| ---------------------------- | ------ | ---------------------------------------------- |
| <code>/v1/chat/completions</code>       | POST   | Chat completions (streaming and non-streaming) |
| <code>/v1/completions</code>            | POST   | Legacy text completions                        |
| <code>/v1/models</code>                 | GET    | List available models (workspaces)             |
| <code>/v1/models/{modelId}</code>       | GET    | Get a single model (workspace)                 |
| <code>/v1/embeddings</code>             | POST   | Generate embeddings                            |
| <code>/v1/files</code>                  | GET    | List files                                     |
| <code>/v1/files</code>                  | POST   | Upload a file                                  |
| <code>/v1/files/{fileId}</code>         | GET    | Get file metadata                              |
| <code>/v1/files/{fileId}</code>         | DELETE | Delete a file                                  |
| <code>/v1/files/{fileId}/content</code> | GET    | Download file content                          |</p>
<p>All endpoints require <code>Authorization: Bearer &lt;token&gt;</code>.</p>
<h2>Quick Setup with AnythingLLM</h2>
<p>Before configuration, ensure:</p>
<ol>
<li>AI Management is installed and running in your ABP app.</li>
<li>At least one workspace is created and <strong>active</strong>.</li>
<li>You have a valid Bearer token for your ABP application.</li>
</ol>
<h3>1) Get an access token</h3>
<p>Use any valid token accepted by your app. In a demo-style setup, token retrieval can look like this:</p>
<pre><code class="language-bash">curl -X POST http://localhost:44337/connect/token \
  -d &quot;grant_type=password&amp;username=admin&amp;password=1q2w3E*&amp;client_id=DemoApp_API&amp;client_secret=1q2w3e*&amp;scope=DemoApp&quot;
</code></pre>
<p>Use the returned <code>access_token</code> as the API key value in your OpenAI-compatible client.</p>
<h3>2) Configure AnythingLLM as Generic OpenAI</h3>
<p>In <strong>AnythingLLM -&gt; Settings -&gt; LLM Preference</strong>, select <strong>Generic OpenAI</strong> and set:</p>
<p>| Setting              | Value                       |
| -------------------- | --------------------------- |
| Base URL             | <code>http://localhost:44337/v1</code> |
| API Key              | <code>&lt;access_token&gt;</code>            |
| Chat Model Selection | Select an active workspace  |</p>
<p>In most OpenAI-compatible UIs, the app adds <code>Bearer</code> automatically, so the API key field should contain only the raw token string.</p>
<h3>3) Optional: configure embeddings</h3>
<p>If you want RAG flows through ABP, go to <strong>Settings -&gt; Embedding Preference</strong> and use the same Base URL/API key values.
Then select a workspace that has embedder settings configured.</p>
<h2>Validate the Flow</h2>
<h3>List models (workspaces)</h3>
<pre><code class="language-bash">curl http://localhost:44337/v1/models \
  -H &quot;Authorization: Bearer &lt;your-token&gt;&quot;
</code></pre>
<h3>Chat completion</h3>
<pre><code class="language-bash">curl -X POST http://localhost:44337/v1/chat/completions \
  -H &quot;Authorization: Bearer &lt;your-token&gt;&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d '{
    &quot;model&quot;: &quot;MyWorkspace&quot;,
    &quot;messages&quot;: [
      { &quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Hello from ABP OpenAI-compatible endpoint!&quot; }
    ]
  }'
</code></pre>
<h3>Optional SDK check (Python)</h3>
<pre><code class="language-python">from openai import OpenAI

client = OpenAI(
    base_url=&quot;http://localhost:44337/v1&quot;,
    api_key=&quot;&lt;your-token&gt;&quot;
)

response = client.chat.completions.create(
    model=&quot;MyWorkspace&quot;,
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Hello!&quot;}]
)

print(response.choices[0].message.content)
</code></pre>
<h2>Where This Fits in Real Projects</h2>
<p>This approach is a strong fit when you want to:</p>
<ul>
<li>Keep ABP as the central control plane for AI workspaces.</li>
<li>Let client tools integrate through a standard OpenAI contract.</li>
<li>Switch providers or model settings without rewriting client-side integration.</li>
</ul>
<p>If your team uses multiple AI clients, this pattern keeps integration simple while preserving control where it matters.</p>
<h2>Learn More</h2>
<ul>
<li><a href="https://abp.io/docs/10.2/modules/ai-management">ABP AI Management Documentation</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a200d83-6b29-efe9-57f5-84eea8167c74" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a200d83-6b29-efe9-57f5-84eea8167c74" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.2-rc-has-been-released-05zatjfq</guid>
      <link>https://abp.io/community/posts/abp-platform-10.2-rc-has-been-released-05zatjfq</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>version</category>
      <category>new-features</category>
      <category>release</category>
      <category>abpplatform</category>
      <title>ABP Platform 10.2 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.2 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.</description>
      <pubDate>Wed, 25 Feb 2026 18:52:15 Z</pubDate>
      <a10:updated>2026-09-26T00:04:30Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.2 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.2 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.2! Thanks to you in advance.</p>
<h2>Get Started with the 10.2 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/studio-switch-to-preview.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v10.1 or earlier: <a href="https://abp.io/docs/10.2/release-info/migration-guides/abp-10-2">ABP Version 10.2 Migration Guide</a>.</p>
<h2>What's New with ABP v10.2?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>Multi-Tenant Account Usage: Shared User Accounts</li>
<li>Prevent Privilege Escalation: Assignment Restrictions for Roles and Permissions</li>
<li><code>ClientResourcePermissionValueProvider</code> for OAuth/OpenIddict</li>
<li>Angular: Hybrid Localization Support</li>
<li>Angular: Extensible Table Row Detail</li>
<li>Angular: CMS Kit Module Features</li>
<li>Blazor: Upgrade to Blazorise 2.0</li>
<li>Identity: Single Active Token Providers</li>
<li>TickerQ Package Upgrade to 10.1.1</li>
<li>AI Management: MCP (Model Context Protocol) Support</li>
<li>AI Management: RAG with File Upload</li>
<li>AI Management: OpenAI-Compatible Chat Endpoint</li>
<li>File Management: Resource-Based Authorization</li>
</ul>
<h3>Multi-Tenant Account Usage: Shared User Accounts</h3>
<p>ABP v10.2 introduces <strong>Shared User Accounts</strong>: a single user account can belong to multiple tenants, and the user can choose or switch the active tenant when signing in. This enables a &quot;one account, multiple tenants&quot; experience — for example, inviting the same email address into multiple tenants.</p>
<p>When you use Shared User Accounts:</p>
<ul>
<li>Username/email uniqueness becomes <strong>global</strong> (Host + all tenants)</li>
<li>Users are prompted to select the tenant at login if they belong to multiple tenants</li>
<li>Users can switch between tenants using the tenant switcher in the user menu</li>
<li>Tenant administrators can invite existing or new users to join a tenant</li>
</ul>
<p>Enable shared accounts by configuring <code>UserSharingStrategy</code>:</p>
<pre><code class="language-csharp">Configure&lt;AbpMultiTenancyOptions&gt;(options =&gt;
{
    options.IsEnabled = true;
    options.UserSharingStrategy = TenantUserSharingStrategy.Shared;
});
</code></pre>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.2/modules/account/shared-user-accounts">Shared User Accounts</a> documentation for details.</p>
</blockquote>
<h3>Prevent Privilege Escalation: Assignment Restrictions for Roles and Permissions</h3>
<p>ABP v10.2 implements a unified <strong>privilege escalation prevention</strong> model to address security vulnerabilities where users could assign themselves or others roles or permissions they do not possess.</p>
<p><strong>Role Assignment Restriction:</strong> Users can only assign or remove roles they currently have. Users cannot add new roles to themselves (removal only) and cannot assign or remove roles they do not possess.</p>
<p><strong>Permission Grant/Revoke Authorization:</strong> Users can only grant or revoke permissions they currently have. Validation applies to both grant and revoke operations.</p>
<p><strong>Incremental Permission Protection:</strong> When updating user or role permissions, permissions the current user does not have are treated as non-editable and are preserved as-is during updates.</p>
<p>Users with the <code>admin</code> role can assign any role and grant/revoke any permission. All validations are enforced on the backend — the UI is not a security boundary.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24775">#24775</a> for more details.</p>
</blockquote>
<h3><code>ClientResourcePermissionValueProvider</code> for OAuth/OpenIddict</h3>
<p>ABP v10.2 adds <strong>ClientResourcePermissionValueProvider</strong>, extending resource-based authorization to OAuth clients. When using IdentityServer or OpenIddict, clients can now have resource permissions aligned with the standard user and role permission model.</p>
<p>This allows you to control which OAuth clients can access which resources, providing fine-grained authorization for API consumers. The implementation integrates with ABP's existing resource permission infrastructure.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24515">#24515</a> for more details.</p>
</blockquote>
<h3>Angular: Hybrid Localization Support</h3>
<p>ABP v10.2 introduces <strong>Hybrid Localization</strong> for Angular applications, combining server-side and client-side localization strategies. This gives you flexibility in how translations are loaded and resolved — you can use server-provided localization, client-side fallbacks, or a mix of both.</p>
<p>This feature is useful when you want to reduce initial load time, support offline scenarios, or have environment-specific localization behavior. The Angular packages have been updated to support the hybrid approach seamlessly.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.2/framework/ui/angular/hybrid-localization">Hybrid Localization</a> documentation and <a href="https://github.com/abpframework/abp/pull/24731">#24731</a>.</p>
</blockquote>
<h3>Angular: Extensible Table Row Detail</h3>
<p>ABP v10.2 adds the <strong>ExtensibleTableRowDetailComponent</strong> for expandable row details in extensible tables. You can now display additional information for each row in a collapsible detail section.</p>
<p>The feature supports row detail templates via both direct input and content child component. It adds toggle logic and emits <code>rowDetailToggle</code> events, making it easy to customize the behavior and appearance of expandable rows in your data tables.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24636">#24636</a> for more details.</p>
</blockquote>
<h3>Angular: CMS Kit Module Features</h3>
<p>ABP v10.2 brings <strong>CMS Kit features to Angular</strong>, completing the cross-platform UI coverage for the CMS Kit module. The Angular implementation includes: Blogs, Blog Posts, Comments, Menus, Pages, Tags, Global Resources, and CMS Settings.</p>
<p>Together with the CMS Kit Pro Angular implementation (FAQ, Newsletters, Page Feedbacks, Polls, Url forwarding), ABP now provides full Angular UI coverage for both the open-source CMS Kit and CMS Kit Pro modules.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24234">#24234</a> for more details.</p>
</blockquote>
<h3>Blazor: Upgrade to Blazorise 2.0</h3>
<p>ABP v10.2 upgrades the <a href="https://blazorise.com/">Blazorise</a> library to <strong>version 2.0</strong> for Blazor UI. If you are upgrading your project to v10.2 RC, please ensure that all Blazorise-related packages are updated to v2.0 in your application.</p>
<p>Blazorise 2.0 includes various improvements and changes. Please refer to the <a href="https://blazorise.com/news/release-notes/200">Blazorise 2.0 Release Notes</a> and the <a href="https://abp.io/docs/10.2/release-info/migration-guides/blazorise-2-0-migration">ABP Blazorise 2.0 Migration Guide</a> for upgrade instructions.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24906">#24906</a> for more details.</p>
</blockquote>
<h3>Identity: Single Active Token Providers</h3>
<p>ABP v10.2 introduces a <strong>single active token</strong> policy for password reset, email confirmation, and change-email flows. Three new token providers are available: <code>AbpPasswordResetTokenProvider</code>, <code>AbpEmailConfirmationTokenProvider</code>, and <code>AbpChangeEmailTokenProvider</code>.</p>
<p>When a new token is generated, it invalidates any previously issued tokens for that purpose. This improves security by ensuring that only the most recently issued token is valid. Token lifespan can be customized via the respective options classes for each provider.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/24926">#24926</a> for more details.</p>
</blockquote>
<h3>TickerQ Package Upgrade to 10.1.1</h3>
<p><strong>If you are using the TickerQ integration packages</strong> (<code>Volo.Abp.TickerQ</code>, <code>Volo.Abp.BackgroundJobs.TickerQ</code>, or <code>Volo.Abp.BackgroundWorkers.TickerQ</code>), you need to apply breaking changes when upgrading to ABP 10.2. TickerQ has been upgraded from 2.5.3 to 10.1.1, which only targets .NET 10.0 and contains several API changes.</p>
<p>Key changes include:</p>
<ul>
<li><code>UseAbpTickerQ</code> moved from <code>IApplicationBuilder</code> to <code>IHost</code> — use <code>context.GetHost().UseAbpTickerQ()</code> in your module</li>
<li>Entity types renamed: <code>TimeTicker</code> → <code>TimeTickerEntity</code>, <code>CronTicker</code> → <code>CronTickerEntity</code></li>
<li>Scheduler and dashboard configuration APIs have changed</li>
<li>New helpers: <code>context.GetHost()</code>, <code>GetWebApplication()</code>, <code>GetEndpointRouteBuilder()</code></li>
</ul>
<blockquote>
<p><strong>Important:</strong> Do <strong>not</strong> resolve <code>IHost</code> from <code>context.ServiceProvider.GetRequiredService&lt;IHost&gt;()</code>. Always use <code>context.GetHost()</code>. See the <a href="https://abp.io/docs/10.2/release-info/migration-guides/abp-10-2">ABP Version 10.2 Migration Guide</a> for the complete list of changes.</p>
</blockquote>
<h3>AI Management: MCP (Model Context Protocol) Support</h3>
<p><em>This is a <strong>PRO</strong> feature available for ABP Commercial customers.</em></p>
<p>The <a href="https://abp.io/docs/10.2/modules/ai-management">AI Management Module</a> now supports <a href="https://modelcontextprotocol.io/">MCP (Model Context Protocol)</a>, enabling AI workspaces to use external MCP servers as tools. MCP allows AI models to interact with external services, databases, APIs, and more through a standardized protocol.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/mcp-servers.png" alt="mcp-servers" /></p>
<p>You can create and manage MCP servers via the AI Management UI. Each MCP server supports one of the following transport types: <strong>Stdio</strong> (runs a local command), <strong>SSE</strong> (Server-Sent Events), or <strong>StreamableHttp</strong>. For HTTP-based transports, you can configure authentication (API Key, Bearer token, or custom headers). Once MCP servers are defined, you can associate them with workspaces. When a workspace has MCP servers associated, the AI model can invoke tools from those servers during chat conversations — tool calls and results are displayed in the chat interface.</p>
<p>You can test the connection to an MCP server after creating it to verify connectivity and list available tools before use:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/test-connection.png" alt="test-connection" /></p>
<p>When a workspace has MCP servers associated, the AI model can invoke tools from those servers during chat conversations. Tool calls and results are displayed in the chat interface.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/chat-playground.png" alt="chat-playground" /></p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.2/modules/ai-management#mcp-servers">AI Management documentation</a> for details.</p>
</blockquote>
<h3>AI Management: RAG with File Upload</h3>
<p><em>This is a <strong>PRO</strong> feature available for ABP Commercial customers.</em></p>
<p>The AI Management module supports <strong>RAG (Retrieval-Augmented Generation)</strong> with file upload, which enables workspaces to answer questions based on the content of uploaded documents. When RAG is configured, the AI model searches the uploaded documents for relevant information before generating a response.</p>
<p>To enable RAG, configure an <strong>embedder</strong> (e.g., OpenAI, Ollama) and a <strong>vector store</strong> (e.g., PgVector) on the workspace:</p>
<p>| Embedder | Vector Store |
| --- | --- |
| <img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/rag-embedder.png" alt="rag-embedder" /> | <img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/rag-vector-store.png" alt="rag-vector-store" /> |</p>
<p>You can then upload documents (PDF, Markdown, or text files, max 10 MB) through the workspace management UI. Uploaded documents are automatically processed — their content is chunked, embedded, and stored in the configured vector store:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/rag-file-upload.png" alt="rag-file-upload" /></p>
<p>When you ask questions in the chat interface, the AI model uses the uploaded documents as context for accurate, grounded responses.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.2/modules/ai-management#rag-with-file-upload">AI Management — RAG with File Upload</a> documentation for configuration details.</p>
</blockquote>
<h3>AI Management: OpenAI-Compatible Chat Endpoint</h3>
<p><em>This is a <strong>PRO</strong> feature available for ABP Commercial customers.</em></p>
<p>The AI Management module exposes an <strong>OpenAI-compatible REST API</strong> at the <code>/v1</code> path. This allows any application or tool that supports the OpenAI API format — such as <a href="https://anythingllm.com/">AnythingLLM</a>, <a href="https://openwebui.com/">Open WebUI</a>, <a href="https://dify.ai/">Dify</a>, or custom scripts using the OpenAI SDK — to connect directly to your AI Management instance.</p>
<p><strong>Example configuration from AnythingLLM</strong>:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/ai-management-openai-anythingllm.png" alt="anythingllm" /></p>
<p>Each AI Management <strong>workspace</strong> appears as a selectable model in the client application. The workspace's configured AI provider handles the actual inference transparently. Available endpoints include <code>/v1/chat/completions</code>, <code>/v1/models</code>, <code>/v1/embeddings</code>, <code>/v1/files</code>, and more. All endpoints require authentication via a Bearer token in the <code>Authorization</code> header.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/10.2/modules/ai-management#openai-compatible-api">AI Management — OpenAI-Compatible API</a> documentation for usage examples.</p>
</blockquote>
<h3>File Management: Resource-Based Authorization</h3>
<p><em>This is a <strong>PRO</strong> feature available for ABP Commercial customers.</em></p>
<p>The <strong>File Management Module</strong> now supports <strong>resource-based authorization</strong>. You can control access to individual files and folders per user, role, or client. Permissions can be granted at the resource level via the UI, and the feature integrates with ABP's resource permission infrastructure.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-25%20v10_2_Preview/file-management-rba.png" alt="file-management-resource-based-authorization" /></p>
<p>This feature is <strong>implemented for all three supported UIs: MVC/Razor Pages, Blazor, and Angular</strong>, providing a consistent experience across your application regardless of the UI framework you use.</p>
<h3>Other Improvements and Enhancements</h3>
<ul>
<li><strong>Angular signal APIs</strong>: ABP Angular packages migrated to signal queries, output functions, and signal input functions for alignment with Angular 21 (<a href="https://github.com/abpframework/abp/pull/24765">#24765</a>, <a href="https://github.com/abpframework/abp/pull/24766">#24766</a>, <a href="https://github.com/abpframework/abp/pull/24777">#24777</a>).</li>
<li><strong>Angular Vitest</strong>: ABP Angular templates now use Vitest as the default testing framework instead of Karma/Jasmine (<a href="https://github.com/abpframework/abp/pull/24725">#24725</a>).</li>
<li><strong>Ambient auditing</strong>: Programmatic disable/enable of auditing via <code>IAuditingHelper.DisableAuditing()</code> and <code>IsAuditingEnabled()</code> (<a href="https://github.com/abpframework/abp/pull/24718">#24718</a>).</li>
<li><strong>Complex property auditing</strong>: Entity History and ModifierId now support EF Core complex properties (<a href="https://github.com/abpframework/abp/pull/24767">#24767</a>).</li>
<li><strong>RabbitMQ correlation ID</strong>: Correlation ID support added to RabbitMQ JobQueue for distributed tracing (<a href="https://github.com/abpframework/abp/pull/24755">#24755</a>).</li>
<li><strong>Concurrent config retrieval</strong>: <code>MvcCachedApplicationConfigurationClient</code> now fetches configuration and localization concurrently for faster startup (<a href="https://github.com/abpframework/abp/pull/24838">#24838</a>).</li>
<li><strong>Environment localization fallback</strong>: Angular can use <code>environment.defaultResourceName</code> when the backend does not provide it (<a href="https://github.com/abpframework/abp/pull/24589">#24589</a>).</li>
<li><strong>JS proxy namespace fix</strong>: Resolved namespace mismatch for multi-segment company names in generated proxies (<a href="https://github.com/abpframework/abp/pull/24877">#24877</a>).</li>
<li><strong>Audit Logging max length</strong>: Entity/property type full names increased to 512 characters to reduce truncation (<a href="https://github.com/abpframework/abp/pull/24846">#24846</a>).</li>
<li><strong>AI guidelines</strong>: Cursor and Copilot AI guideline documents added for ABP development (<a href="https://github.com/abpframework/abp/pull/24563">#24563</a>, <a href="https://github.com/abpframework/abp/pull/24593">#24593</a>).</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/enisn">Enis Necipoğlu</a> has published 2 new posts:
<ul>
<li><a href="https://abp.io/community/articles/hidden-magic-things-that-just-work-without-you-knowing-vw6osmyt">ABP Framework's Hidden Magic: Things That Just Work Without You Knowing</a></li>
<li><a href="https://abp.io/community/articles/implementing-multiple-global-query-filters-with-entity-ugnsmf6i">Implementing Multiple Global Query Filters with Entity Framework Core</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/suhaib-mousa">Suhaib Mousa</a> has published 2 new posts:
<ul>
<li><a href="https://abp.io/community/articles/dotnet-11-preview-1-highlights-hspp3o5x">.NET 11 Preview 1 Highlights: Faster Runtime, Smarter JIT, and AI-Ready Improvements</a></li>
<li><a href="https://abp.io/community/articles/toon-vs-json-b4rn2avd">TOON vs JSON for LLM Prompts in ABP: Token-Efficient Structured Context</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/fahrigedik">Fahri Gedik</a> has published 2 new posts:
<ul>
<li><a href="https://abp.io/community/articles/building-a-multiagent-ai-system-with-a2a-mcp-iefdehyx">Building a Multi-Agent AI System with A2A, MCP, and ADK in .NET</a></li>
<li><a href="https://abp.io/community/articles/async-chain-of-persistence-pattern-wzjuy4gl">Async Chain of Persistence Pattern: Designing for Failure in Event-Driven Systems</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a> has published 2 new posts:
<ul>
<li><a href="https://abp.io/community/articles/ndc-london-2026-a-.net-conf-from-a-developers-perspective-07wp50yl">NDC London 2026: From a Developer's Perspective and My Personal Notes about AI</a></li>
<li><a href="https://abp.io/community/articles/which-opensource-pdf-libraries-are-recently-popular-a-g68q78it">Which Open-Source PDF Libraries Are Recently Popular? A Data-Driven Look At PDF Topic</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/stop-spam-and-toxic-users-in-your-app-with-ai-3i0xxh0y">Stop Spam and Toxic Users in Your App with AI</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/how-ai-is-changing-developers-e8y4a85f">How AI Is Changing Developers</a> by <a href="https://abp.io/community/members/maliming">Liming Ma</a></li>
<li><a href="https://abp.io/community/articles/jetbrains-state-of-developer-ecosystem-report-2025-key-z0638q5e">JetBrains State of Developer Ecosystem Report 2025 — Key Insights</a> by <a href="https://abp.io/community/members/mtozdemir">Tarık Özdemir</a></li>
<li><a href="https://abp.io/community/articles/integrating-ai-into-abp.io-applications-the-complete-guide-jc9fbjq0">Integrating AI into ABP.IO Applications: The Complete Guide to Volo.Abp.AI and AI Management Module</a> by <a href="https://abp.io/community/members/adnanaldaim">Adnan Ali</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.2/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.2 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1fa851-e0a8-2372-1e75-345896864a6c" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1fa851-e0a8-2372-1e75-345896864a6c" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-10.1-final-has-been-released-z4xfn1me</guid>
      <link>https://abp.io/community/posts/abp.io-platform-10.1-final-has-been-released-z4xfn1me</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <category>abp-io</category>
      <category>abpframework</category>
      <category>abpplatform</category>
      <title>ABP.IO Platform 10.1 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 10.1 stable version has been released.  Read the blog post to learn out what's new with v10.1.</description>
      <pubDate>Mon, 23 Feb 2026 10:52:23 Z</pubDate>
      <a10:updated>2026-09-26T00:04:33Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 10.1 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 10.1 stable version has been released.</p>
<h2>What's New With Version 10.1?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-10-1-release-candidate-cyqui19d">10.1 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 10.1</h2>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-02-23%20v10_1_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v10.0 or earlier versions: <a href="https://abp.io/docs/latest/release-info/migration-guides/abp-10-1">ABP Version 10.1 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/enisn">Enis Necipoğlu</a>:
<ul>
<li><a href="https://abp.io/community/articles/hidden-magic-things-that-just-work-without-you-knowing-vw6osmyt">ABP Framework's Hidden Magic: Things That Just Work Without You Knowing</a></li>
<li><a href="https://abp.io/community/articles/implementing-multiple-global-query-filters-with-entity-ugnsmf6i">Implementing Multiple Global Query Filters with Entity Framework Core</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/suhaib-mousa">Suhaib Mousa</a>:
<ul>
<li><a href="https://abp.io/community/articles/dotnet-11-preview-1-highlights-hspp3o5x">.NET 11 Preview 1 Highlights: Faster Runtime, Smarter JIT, and AI-Ready Improvements</a></li>
<li><a href="https://abp.io/community/articles/toon-vs-json-b4rn2avd">TOON vs JSON for LLM Prompts in ABP: Token-Efficient Structured Context</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/fahrigedik">Fahri Gedik</a>:
<ul>
<li><a href="https://abp.io/community/articles/building-a-multiagent-ai-system-with-a2a-mcp-iefdehyx">Building a Multi-Agent AI System with A2A, MCP, and ADK in .NET</a></li>
<li><a href="https://abp.io/community/articles/async-chain-of-persistence-pattern-wzjuy4gl">Async Chain of Persistence Pattern: Designing for Failure in Event-Driven Systems</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a>:
<ul>
<li><a href="https://abp.io/community/articles/ndc-london-2026-a-.net-conf-from-a-developers-perspective-07wp50yl">NDC London 2026: From a Developer's Perspective and My Personal Notes about AI</a></li>
<li><a href="https://abp.io/community/articles/which-opensource-pdf-libraries-are-recently-popular-a-g68q78it">Which Open-Source PDF Libraries Are Recently Popular? A Data-Driven Look At PDF Topic</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/EngincanV">Engincan Veske</a>:
<ul>
<li><a href="https://abp.io/community/articles/stop-spam-and-toxic-users-in-your-app-with-ai-3i0xxh0y">Stop Spam and Toxic Users in Your App with AI</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/maliming">Liming Ma</a>:
<ul>
<li><a href="https://abp.io/community/articles/how-ai-is-changing-developers-e8y4a85f">How AI Is Changing Developers</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/mtozdemir">Tarık Özdemir</a>:
<ul>
<li><a href="https://abp.io/community/articles/jetbrains-state-of-developer-ecosystem-report-2025-key-z0638q5e">JetBrains State of Developer Ecosystem Report 2025 — Key Insights</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/adnanaldaim">Adnan Ali</a>:
<ul>
<li><a href="https://abp.io/community/articles/integrating-ai-into-abp.io-applications-the-complete-guide-jc9fbjq0">Integrating AI into ABP.IO Applications: The Complete Guide to Volo.Abp.AI and AI Management Module</a></li>
</ul>
</li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.2. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1f9c4d-d57f-c103-ea33-569030dd7cce" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1f9c4d-d57f-c103-ea33-569030dd7cce" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/stop-spam-and-toxic-users-in-your-app-with-ai-3i0xxh0y</guid>
      <link>https://abp.io/community/posts/stop-spam-and-toxic-users-in-your-app-with-ai-3i0xxh0y</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-commercial</category>
      <category>abp</category>
      <category>module-integration</category>
      <category>cms-kit</category>
      <category>ai</category>
      <title>Stop Spam and Toxic Users in Your App with AI</title>
      <description>In this article, I'll show you how to integrate the "omni-moderation" model into an ABP application using the AI Management Module. We'll wire it into the CMS Kit Module's Comment Feature so every comment is automatically screened before it's published. The AI Management Module handles the OpenAI configuration (API keys, model selection, etc.) through a runtime UI, so you won't need to hardcode any of that into your appsettings.json or redeploy when something changes.</description>
      <pubDate>Tue, 10 Feb 2026 13:24:10 Z</pubDate>
      <a10:updated>2026-09-25T21:16:40Z</a10:updated>
      <content:encoded><![CDATA[<h1>Using OpenAI's Moderation API in an ABP Application with the AI Management Module</h1>
<p>If your application accepts user-generated content (comments, reviews, forum posts) you likely need some form of content moderation. Building one from scratch typically means training ML models, maintaining datasets, and writing a lot of code. OpenAI's <code>omni-moderation-latest</code> model offers a practical shortcut: it's free, requires no training data, and covers 13+ harm categories across text and images in 40+ languages.</p>
<p>In this article, I'll show you how to integrate this model into an ABP application using the <a href="https://abp.io/docs/latest/modules/ai-management"><strong>AI Management Module</strong></a>. We'll wire it into the <a href="https://abp.io/docs/latest/modules/cms-kit/comments">CMS Kit Module's Comment Feature</a> so every comment is automatically screened before it's published. The <strong>AI Management Module</strong> handles the OpenAI configuration (API keys, model selection, etc.) through a runtime UI, so you won't need to hardcode any of that into your <code>appsettings.json</code> or redeploy when something changes.</p>
<p>By the end, you'll have a working content moderation pipeline you can adapt for any entity in your ABP project.</p>
<h2>Understanding OpenAI's Omni-Moderation Model</h2>
<p>Before diving into the implementation, let's understand what makes OpenAI's <code>omni-moderation-latest</code> model a game-changer for content moderation.</p>
<h3>What is it?</h3>
<p>OpenAI's <code>omni-moderation-latest</code> is a next-generation multimodal content moderation model built on the foundation of GPT-4o. Released in September 2024, this model represents a significant leap forward in automated content moderation capabilities.</p>
<p>The most remarkable aspect? <strong>It's completely free to use</strong> through OpenAI's Moderation API, there are no token costs, no usage limits for reasonable use cases, and no hidden fees.</p>
<h3>Key Capabilities</h3>
<p>The <strong>omni-moderation</strong> model offers several compelling features that make it ideal for production applications:</p>
<ul>
<li><strong>Multimodal Understanding</strong>: Unlike text-only moderation systems, this model <em>can process both text and image inputs</em>, making it suitable for applications where users can upload images alongside their comments or posts.</li>
<li><strong>High Accuracy</strong>: Built on GPT-4o's advanced understanding capabilities, the model achieves significantly higher accuracy in detecting nuanced harmful content compared to rule-based systems or simpler ML models.</li>
<li><strong>Multilingual Support</strong>: The model demonstrates enhanced performance across more than 40 languages, making it suitable for global applications without requiring separate moderation systems for each language.</li>
<li><strong>Comprehensive Category Coverage</strong>: Rather than just detecting &quot;spam&quot; or &quot;not spam,&quot; the model classifies content across 13+ distinct categories of potentially harmful content.</li>
</ul>
<h3>Content Categories</h3>
<p>The model evaluates content against the following categories, each designed to catch specific types of harmful content:</p>
<p>| Category | What It Detects |
|----------|-----------------|
| <code>harassment</code> | Content that expresses, incites, or promotes harassing language towards any individual or group |
| <code>harassment/threatening</code> | Harassment content that additionally includes threats of violence or serious harm |
| <code>hate</code> | Content that promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability, or caste |
| <code>hate/threatening</code> | Hateful content that includes threats of violence or serious harm towards the targeted group |
| <code>self-harm</code> | Content that promotes, encourages, or depicts acts of self-harm such as suicide, cutting, or eating disorders |
| <code>self-harm/intent</code> | Content where the speaker expresses intent to engage in self-harm |
| <code>self-harm/instructions</code> | Content that provides instructions or advice on how to commit acts of self-harm |
| <code>sexual</code> | Content meant to arouse sexual excitement, including descriptions of sexual activity or promotion of sexual services |
| <code>sexual/minors</code> | Sexual content that involves individuals under 18 years of age |
| <code>violence</code> | Content that depicts death, violence, or physical injury in graphic detail |
| <code>violence/graphic</code> | Content depicting violence or physical injury in extremely graphic, disturbing detail |
| <code>illicit</code> | Content that provides advice or instructions for committing illegal activities |
| <code>illicit/violent</code> | Illicit content that specifically involves violence or weapons |</p>
<h3>API Response Structure</h3>
<p>When you send content to the Moderation API (through model or directly to the API), you receive a structured response containing:</p>
<ul>
<li><strong><code>flagged</code></strong>: A boolean indicating whether the content violates any of OpenAI's usage policies. This is your primary indicator for whether to block content.</li>
<li><strong><code>categories</code></strong>: A dictionary containing boolean flags for each category, telling you exactly which policies were violated.</li>
<li><strong><code>category_scores</code></strong>: Confidence scores ranging from 0 to 1 for each category, allowing you to implement custom thresholds if needed.</li>
<li><strong><code>category_applied_input_types</code></strong>: A dictionary containing information on which input types were flagged for each category. For example, if both the image and text inputs to the model are flagged for &quot;violence/graphic&quot;, the <code>violence/graphic</code> property will be set to <code>[&quot;image&quot;, &quot;text&quot;]</code>. This is only available on omni models.</li>
</ul>
<blockquote>
<p>For more detailed information about the model's capabilities and best practices, refer to the <a href="https://platform.openai.com/docs/guides/moderation">OpenAI Moderation Guide</a>.</p>
</blockquote>
<h2>The AI Management Module: Your Dynamic AI Configuration Hub</h2>
<p>The <a href="https://abp.io/docs/latest/modules/ai-management">AI Management Module</a> is a powerful addition to the ABP Platform that transforms how you integrate and manage AI capabilities in your applications. Built on top of the <a href="https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence">ABP Framework's AI infrastructure</a>, it provides a complete solution for managing AI workspaces dynamically—without requiring code changes or application redeployment.</p>
<h3>Why Use the AI Management Module?</h3>
<p>Traditional AI integrations often suffer from several pain points:</p>
<ol>
<li><strong>Hardcoded Configuration</strong>: API keys, model names, and endpoints are typically stored in configuration files, requiring redeployment for any changes.</li>
<li><strong>No Runtime Flexibility</strong>: Switching between AI providers or models requires code changes.</li>
<li><strong>Security Concerns</strong>: Managing API keys across environments is cumbersome and error-prone.</li>
<li><strong>Limited Visibility</strong>: There's no easy way to see which AI configurations are active or test them without writing code.</li>
</ol>
<p>The AI Management Module addresses all these concerns by providing:</p>
<ul>
<li><strong>Dynamic Workspace Management</strong>: Create, configure, and update AI workspaces directly from a user-friendly administrative interface—no code changes required.</li>
<li><strong>Provider Flexibility</strong>: Seamlessly switch between different AI providers (OpenAI, Gemini, Antrophic, Azure OpenAI, Ollama, and custom providers) without modifying your application code.</li>
<li><strong>Built-in Testing</strong>: Test your AI configurations immediately using the included chat interface playground before deploying to production.</li>
<li><strong>Permission-Based Access Control</strong>: Define granular permissions to control who can manage AI workspaces and who can use specific AI features.</li>
<li><strong>Multi-Framework Support</strong>: Full support for MVC/Razor Pages, Blazor (Server &amp; WebAssembly), and Angular UI frameworks.</li>
</ul>
<h3>Built-in Provider Support</h3>
<p>The <strong>AI Management Module</strong> comes with built-in support for popular AI providers through dedicated NuGet packages:</p>
<ul>
<li><strong><code>Volo.AIManagement.OpenAI</code></strong>: Provides seamless integration with OpenAI's APIs, including GPT models and the <em>Moderation API</em>.</li>
<li>Custom providers can be added by implementing the <code>IChatClientFactory</code> interface. (If you configured the Ollama while creating your project, then you can see the example implementation for Ollama)</li>
</ul>
<h2>Building the Demo Application</h2>
<p>Now let's put theory into practice by building a complete content moderation system. We'll create an ABP application with the <strong>AI Management Module</strong>, configure OpenAI as our provider, set up the CMS Kit Comment Feature, and implement automatic content moderation for all user comments.</p>
<h3>Step 1: Creating an Application with AI Management Module</h3>
<blockquote>
<p>In this tutorial, I'll create a <strong>layered MVC application</strong> named <strong>ContentModeration</strong>. If you already have an existing solution, you can follow along by replacing the namespaces accordingly. Otherwise, feel free to follow the solution creation steps below.</p>
</blockquote>
<p>The most straightforward way to create an application with the AI Management Module is through <strong>ABP Studio</strong>. When you create a new project, you'll encounter an <strong>AI Integration</strong> step in the project creation wizard. This wizard allows you to:</p>
<ul>
<li>Enable the AI Management Module with a single checkbox</li>
<li>Configure your preferred AI provider (OpenAI and Ollama)</li>
<li>Set up initial workspace configurations</li>
<li>Automatically install all required NuGet packages</li>
</ul>
<blockquote>
<p><strong>Note:</strong> The AI Integration tab in ABP Studio currently only supports the <strong>MVC/Razor Pages</strong> UI. Support for <strong>Angular</strong> and <strong>Blazor</strong> UIs will be added in upcoming versions.</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-02-04-Omni-Moderation-in-AI-Management-Module/images/abp-studio-ai-management.png" alt="ABP Studio AI Management" /></p>
<p>During the wizard, select <strong>OpenAI</strong> as your AI provider, set the model name as <code>omni-moderation-latest</code> and provide your API key. The wizard will automatically:</p>
<ol>
<li>Install the <code>Volo.AIManagement.*</code> packages across your solution</li>
<li>Install the <code>Volo.AIManagement.OpenAI</code> package for OpenAI provider support (you can use any OpenAI compatible model here, including Gemini, Claude and GPT models)</li>
<li>Configure the necessary module dependencies</li>
<li>Set up initial database migrations</li>
</ol>
<p><strong>Alternative Installation Method:</strong></p>
<p>If you have an existing project or prefer manual installation, you can add the module using the ABP CLI:</p>
<pre><code class="language-bash">abp add-module Volo.AIManagement
</code></pre>
<p>Or through ABP Studio by right-clicking on your solution, selecting <strong>Import Module</strong>, and choosing <code>Volo.AIManagement</code> from the NuGet tab.</p>
<h3>Step 2: Understanding the OpenAI Workspace Configuration</h3>
<p>After creating your project and running the application for the first time, navigate to <strong>AI Management &gt; Workspaces</strong> in the admin menu. Here you'll find the workspace management interface where you can view, create, and modify AI workspaces.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-02-04-Omni-Moderation-in-AI-Management-Module/images/ai-management-workspaces.png" alt="AI Management Workspaces" /></p>
<p>If you configured OpenAI during the project creation wizard, you'll already have a workspace set up. Otherwise, you can create a new workspace with the following configuration:</p>
<p>| Property | Value | Description |
|----------|-------|-------------|
| <strong>Name</strong> | <code>OpenAIAssistant</code> | A unique identifier for this workspace (no spaces allowed) |
| <strong>Provider</strong> | <code>OpenAI</code> | The AI provider to use |
| <strong>Model</strong> | <code>omni-moderation-latest</code> | The specific model for content moderation |
| <strong>API Key</strong> | <code>&lt;Your-OpenAI-API-key&gt;</code> | Authentication credential for the OpenAI API |
| <strong>Description</strong> | <code>Workspace for content moderation</code> | A helpful description for administrators |</p>
<p>The beauty of this approach is that you can modify any of these settings at runtime through the UI. Need to rotate your API key? Just update it in the workspace configuration. Want to test a different model? Change it without touching your code.</p>
<h3>Step 3: Setting Up the CMS Kit Comment Feature</h3>
<p>Now let's add the CMS Kit Module to enable the Comment Feature. The CMS Kit provides a robust, production-ready commenting system that we'll enhance with our content moderation.</p>
<p><strong>Install the CMS Kit Module:</strong></p>
<p>Run the following command in your solution directory:</p>
<pre><code class="language-bash">abp add-module Volo.CmsKit --skip-db-migrations
</code></pre>
<blockquote>
<p>Also, you can add the related module through ABP Studio UI.</p>
</blockquote>
<p><strong>Enable the Comment Feature:</strong></p>
<p>By default, CMS Kit features are disabled to keep your application lean. Open the <code>GlobalFeatureConfigurator</code> class in your <code>*.Domain.Shared</code> project and enable the Comment Feature:</p>
<pre><code class="language-csharp">using Volo.Abp.GlobalFeatures;
using Volo.Abp.Threading;

namespace ContentModeration;

public static class ContentModerationGlobalFeatureConfigurator
{
    private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();

    public static void Configure()
    {
        OneTimeRunner.Run(() =&gt;
        {
            GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit =&gt;
            {
                //only enable the Comment Feature
                cmsKit.Comments.Enable();
            });
        });
    }
}
</code></pre>
<p><strong>Configure the Comment Entity Types:</strong></p>
<p>Open your <code>*DomainModule</code> class and configure which entity types can have comments. For our demo, we'll enable comments on &quot;Article&quot; entities:</p>
<pre><code class="language-csharp">using Volo.CmsKit.Comments;

// In your ConfigureServices method:
Configure&lt;CmsKitCommentOptions&gt;(options =&gt;
{
    options.EntityTypes.Add(new CommentEntityTypeDefinition(&quot;Article&quot;));
});
</code></pre>
<p><strong>Add the Comment Component to a Page:</strong></p>
<p>Finally, let's add the commenting interface to a page. Open the <code>Index.cshtml</code> file in your <code>*.Web</code> project and add the Comment component (replace with the following content):</p>
<pre><code class="language-html">@page
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting
@model ContentModeration.Web.Pages.IndexModel

&lt;div class=&quot;container mt-4&quot;&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;card-header&quot;&gt;
            &lt;h3&gt;Welcome to Our Community&lt;/h3&gt;
        &lt;/div&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;p class=&quot;lead&quot;&gt;
                Share your thoughts in the comments below. Our AI-powered moderation system
                automatically reviews all comments to ensure a safe and respectful environment
                for everyone.
            &lt;/p&gt;

            &lt;hr/&gt;

            &lt;h4&gt;Comments&lt;/h4&gt;
            @await Component.InvokeAsync(typeof(CommentingViewComponent), new
            {
                entityType = &quot;Article&quot;,
                entityId = &quot;welcome-article&quot;,
                isReadOnly = false
            })
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>At this point, you have a fully functional commenting system. Users can post comments, reply to existing comments, and interact with the community.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-02-04-Omni-Moderation-in-AI-Management-Module/images/example-comment.png" alt="" /></p>
<p>However, there's no content moderation yet and any content, including harmful content, would be accepted. Let's fix that!</p>
<h2>Implementing the Content Moderation Service</h2>
<p><strong>Now comes the exciting part:</strong> implementing the content moderation service that leverages OpenAI's <code>omni-moderation</code> model to automatically screen all comments before they're published.</p>
<h3>Understanding the Architecture</h3>
<p>Our implementation follows a clean, modular architecture:</p>
<ol>
<li><strong><code>IContentModerator</code> Interface</strong>: Defines the contract for content moderation, making our implementation testable and replaceable.</li>
<li><strong><code>ContentModerator</code> Service</strong>: The concrete implementation that calls OpenAI's Moderation API using the configuration from the AI Management Module.</li>
<li><strong><code>MyCommentAppService</code></strong>: An override of the CMS Kit's comment service that integrates our moderation logic.</li>
</ol>
<p>This separation of concerns ensures that:</p>
<ul>
<li>The moderation logic is isolated and can be unit tested independently</li>
<li>You can easily swap the moderation implementation (e.g., switch to a different provider)</li>
<li>The integration with CMS Kit is clean and maintainable</li>
</ul>
<h3>Creating the Content Moderator Interface</h3>
<p>First, let's define the interface in your <code>*.Application.Contracts</code> project. This interface is intentionally simple and it takes text input and throws an exception if the content is harmful:</p>
<pre><code class="language-csharp">using System.Threading.Tasks;

namespace ContentModeration.Moderation;

public interface IContentModerator
{
    Task CheckAsync(string text);
}
</code></pre>
<h3>Implementing the Content Moderator Service</h3>
<p>Now let's implement the service in your <code>*.Application</code> project. This implementation uses the <code>IWorkspaceConfigurationStore</code> from the AI Management Module to dynamically retrieve the OpenAI configuration:</p>
<pre><code class="language-csharp">using System.Collections.Generic;
using System.Threading.Tasks;
using OpenAI.Moderations;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.AIManagement.Workspaces.Configuration;

namespace ContentModeration.Moderation;

public class ContentModerator : IContentModerator, ITransientDependency
{
    private readonly IWorkspaceConfigurationStore _workspaceConfigurationStore;

    public ContentModerator(IWorkspaceConfigurationStore workspaceConfigurationStore)
    {
        _workspaceConfigurationStore = workspaceConfigurationStore;
    }

    public async Task CheckAsync(string text)
    {
        // Skip moderation for empty content
        if (string.IsNullOrWhiteSpace(text))
        {
            return;
        }

        // Retrieve the workspace configuration from AI Management Module
        // This allows runtime configuration changes without redeployment
        var config = await _workspaceConfigurationStore.GetOrNullAsync&lt;OpenAIAssistantWorkspace&gt;();

        if(config == null)
        {
            throw new UserFriendlyException(&quot;Could not find the 'OpenAIAssistant' workspace!&quot;);
        }

        var client = new ModerationClient(
            model: config.Model,
            apiKey: config.ApiKey
        );

        // Send the text to OpenAI's Moderation API
        var result = await client.ClassifyTextAsync(text);
        var moderationResult = result.Value;

        // If the content is flagged, throw a user-friendly exception
        if (moderationResult.Flagged)
        {
            var flaggedCategories = GetFlaggedCategories(moderationResult);
            
            throw new UserFriendlyException(
                $&quot;Your comment contains content that violates our community guidelines. &quot; +
                $&quot;Detected issues: {string.Join(&quot;, &quot;, flaggedCategories)}. &quot; +
                $&quot;Please revise your comment and try again.&quot;
            );
        }
    }

    private static List&lt;string&gt; GetFlaggedCategories(ModerationResult result)
    {
        var flaggedCategories = new List&lt;string&gt;();

        if (result.Harassment.Flagged)
        {
            flaggedCategories.Add(&quot;harassment&quot;);
        }
        if (result.HarassmentThreatening.Flagged) 
        {
            flaggedCategories.Add(&quot;threatening harassment&quot;);
        }
        
        //other categories...

        return flaggedCategories;
    }
}
</code></pre>
<blockquote>
<p><strong>Note</strong>: The <code>ModerationResult</code> class from the OpenAI .NET SDK provides properties for each moderation category (e.g., <code>Harassment</code>, <code>Violence</code>, <code>Sexual</code>), each with a <code>Flagged</code> boolean and a <code>Score</code> float (0-1). The exact property names may vary slightly between SDK versions, so check the <a href="https://github.com/openai/openai-dotnet">OpenAI .NET SDK documentation</a> for the latest API.</p>
</blockquote>
<h3>Integrating with CMS Kit Comments</h3>
<p>The final piece of the puzzle is integrating our moderation service with the CMS Kit's comment system. We'll override the <code>CommentPublicAppService</code> to intercept all comment creation and update requests:</p>
<pre><code class="language-csharp">using System;
using System.Threading.Tasks;
using ContentModeration.Moderation;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Public.Comments;
using Volo.CmsKit.Users;
using Volo.Abp.SettingManagement;

namespace ContentModeration.Comments;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ICommentPublicAppService), typeof(CommentPublicAppService), typeof(MyCommentAppService))]
public class MyCommentAppService : CommentPublicAppService
{
    protected IContentModerator ContentModerator { get; }

    public MyCommentAppService(
        ICommentRepository commentRepository,
        ICmsUserLookupService cmsUserLookupService,
        IDistributedEventBus distributedEventBus,
        CommentManager commentManager,
        IOptionsSnapshot&lt;CmsKitCommentOptions&gt; cmsCommentOptions,
        ISettingManager settingManager,
        IContentModerator contentModerator)
        : base(commentRepository, cmsUserLookupService, distributedEventBus, commentManager, cmsCommentOptions, settingManager)
    {
        ContentModerator = contentModerator;
    }

    public override async Task&lt;CommentDto&gt; CreateAsync(string entityType, string entityId, CreateCommentInput input)
    {
        // Check for harmful content BEFORE creating the comment
        // If harmful content is detected, an exception is thrown and the comment is not saved
        await ContentModerator.CheckAsync(input.Text);

        return await base.CreateAsync(entityType, entityId, input);
    }

    public override async Task&lt;CommentDto&gt; UpdateAsync(Guid id, UpdateCommentInput input)
    {
        // Check for harmful content BEFORE updating the comment
        // This prevents users from editing approved comments to add harmful content
        await ContentModerator.CheckAsync(input.Text);

        return await base.UpdateAsync(id, input);
    }
}
</code></pre>
<p><strong>How This Works:</strong></p>
<ol>
<li>When a user submits a new comment, the <code>CreateAsync</code> method is called.</li>
<li>Before the comment is saved to the database, we call <code>ContentModerator.CheckAsync()</code> with the comment text.</li>
<li>The moderation service sends the text to OpenAI's Moderation API.</li>
<li>If the content is flagged as harmful, a <code>UserFriendlyException</code> is thrown with a descriptive message.</li>
<li>The exception is caught by ABP's exception handling middleware and displayed to the user as a friendly error message.</li>
<li>If the content passes moderation, the comment is saved normally.</li>
</ol>
<p>The same flow applies to comment updates, ensuring users can't circumvent moderation by editing previously approved comments.</p>
<p>Here's the full flow in action — submitting a comment with harmful content and seeing the moderation kick in:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2026-02-04-Omni-Moderation-in-AI-Management-Module/demo.gif" alt="Content moderation demo" /></p>
<h2>The Power of Dynamic Configuration - What AI Management Module Provides to You?</h2>
<p>One of the most significant advantages of using the AI Management Module is the ability to manage your AI configurations dynamically. Let's explore what this means in practice.</p>
<h3>Runtime Configuration Changes</h3>
<p>With the AI Management Module, you can:</p>
<ul>
<li><strong>Rotate API Keys</strong>: Update your OpenAI API key through the admin UI without any downtime or redeployment. This is crucial for security compliance and key rotation policies.</li>
<li><strong>Switch Models</strong>: Want to test a newer moderation model? Simply update the model name in the workspace configuration. Your application will immediately start using the new model.</li>
<li><strong>Adjust Settings</strong>: Fine-tune settings like temperature or system prompts (for chat-based workspaces) without touching your codebase.</li>
<li><strong>Enable/Disable Workspaces</strong>: Temporarily disable a workspace for maintenance or testing without affecting other parts of your application.</li>
</ul>
<h3>Multi-Environment Management</h3>
<p>The dynamic configuration approach shines in multi-environment scenarios:</p>
<ul>
<li><strong>Development</strong>: Use a test API key with lower rate limits</li>
<li><strong>Staging</strong>: Use a separate API key for integration testing</li>
<li><strong>Production</strong>: Use your production API key with appropriate security measures</li>
</ul>
<p>All these configurations can be managed through the UI or via data seeding, without environment-specific code changes.</p>
<h3>Actively Maintained &amp; What's Coming Next</h3>
<p>The AI Management Module is <strong>actively maintained</strong> and continuously evolving. The team is working on exciting new capabilities that will further expand what you can do with AI in your ABP applications:</p>
<ul>
<li><strong>MCP (Model Context Protocol) Support</strong> — Coming in <strong>v10.2</strong>, MCP support will allow your AI workspaces to interact with external tools and data sources, enabling more sophisticated AI-powered workflows.</li>
<li><strong>RAG (Retrieval-Augmented Generation) System</strong> — Also planned for <strong>v10.2</strong>, the built-in RAG system will let you ground AI responses in your own data, making AI features more accurate and context-aware.</li>
<li><strong>And More</strong> — Additional features and improvements are on the roadmap to make AI integration even more seamless.</li>
</ul>
<p>Since the module is built on ABP's modular architecture, adopting these new capabilities will be straightforward — you can simply update the module and start using the new features without rewriting your existing AI integrations.</p>
<h3>Permission-Based Access Control</h3>
<p>The AI Management Module integrates with ABP's permission system, allowing you to:</p>
<ul>
<li>Restrict who can view AI workspace configurations</li>
<li>Control who can create or modify workspaces</li>
<li>Limit access to specific workspaces based on user roles</li>
</ul>
<p>This ensures that sensitive configurations like API keys are only accessible to authorized administrators.</p>
<h2>Conclusion</h2>
<p>In this comprehensive guide, we've built a production-ready content moderation system that combines the power of OpenAI's <code>omni-moderation-latest</code> model with the flexibility of ABP's AI Management Module. Let's recap what makes this approach powerful:</p>
<h3>Key Takeaways</h3>
<ol>
<li><strong>Zero Training Required</strong>: Unlike traditional ML approaches that require collecting datasets, training models, and ongoing maintenance, OpenAI's Moderation API works out of the box with state-of-the-art accuracy.</li>
<li><strong>Completely Free</strong>: OpenAI's Moderation API has no token costs, making it economically viable for applications of any scale.</li>
<li><strong>Comprehensive Detection</strong>: With 13+ categories of harmful content detection, you get protection against harassment, hate speech, violence, sexual content, self-harm, and more—all from a single API call.</li>
<li><strong>Dynamic Configuration</strong>: The AI Management Module allows you to manage API keys, switch providers, and adjust settings at runtime without code changes or redeployment.</li>
<li><strong>Clean Integration</strong>: By following ABP's service override pattern, we integrated moderation seamlessly into the existing CMS Kit comment system without modifying the original module.</li>
<li><strong>Production Ready</strong>: The implementation includes proper error handling, graceful degradation, and user-friendly error messages suitable for production use.</li>
</ol>
<h3>Resources</h3>
<ul>
<li><a href="https://abp.io/docs/latest/modules/ai-management">AI Management Module Documentation</a></li>
<li><a href="https://platform.openai.com/docs/guides/moderation">OpenAI Moderation Guide</a></li>
<li><a href="https://abp.io/docs/latest/modules/cms-kit/comments">CMS Kit Comments Feature</a></li>
<li><a href="https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence">ABP Framework AI Infrastructure</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1f59e6-1d0d-b8c0-85c4-20ccbea76d3e" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1f59e6-1d0d-b8c0-85c4-20ccbea76d3e" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-10.1-rc-has-been-released-cyqui19d</guid>
      <link>https://abp.io/community/posts/abp-platform-10.1-rc-has-been-released-cyqui19d</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>updates</category>
      <category>new-version</category>
      <category>release</category>
      <category>abpplatform</category>
      <title>ABP Platform 10.1 RC Has Been Released</title>
      <description>We are happy to release ABP version 10.1 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v10.1! Thanks to you in advance.</description>
      <pubDate>Mon, 12 Jan 2026 06:58:38 Z</pubDate>
      <a10:updated>2026-09-26T02:39:56Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 10.1 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>10.1 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v10.1! Thanks to you in advance.</p>
<h2>Get Started with the 10.1 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/studio-switch-to-preview.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v10.0 or earlier: <a href="https://abp.io/docs/10.1/release-info/migration-guides/abp-10-1">ABP Version 10.1 Migration Guide</a>.</p>
<h2>What's New with ABP v10.1?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>Resource-Based Authorization</li>
<li>Introducing the TickerQ Background Worker Provider</li>
<li>Angular UI: Improving Authentication Token Handling</li>
<li>Angular Version Upgrade to v21</li>
<li>File Management Module: Public File Sharing Support</li>
<li>Payment Module: Public Page Implementation for Blazor &amp; Angular UIs</li>
<li>AI Management Module: Blazor &amp; Angular UIs</li>
<li>Identity PRO Module: Password History Support</li>
<li>Account PRO Module: Introducing WebAuthn Passkeys</li>
</ul>
<h3>Resource-Based Authorization</h3>
<p>ABP v10.1 introduces <strong>Resource-Based Authorization</strong>, a powerful feature that enables fine-grained access control based on specific resource instances. This enhancement addresses a long-requested feature (<a href="https://github.com/abpframework/abp/issues/236">#236</a>) that allows you to implement authorization logic that depends on the resource being accessed, not just static roles or permissions.</p>
<p><strong>What is Resource-Based Authorization?</strong></p>
<p>Unlike traditional permission-based authorization where you check if a user has a general permission (like &quot;CanEditDocuments&quot;), resource-based authorization allows you to make authorization decisions based on the specific resource instance. For example:</p>
<ul>
<li>Allow users to edit only their own blog posts</li>
<li>Grant access to documents based on ownership or sharing settings</li>
<li>Implement complex authorization rules that depend on resource properties</li>
</ul>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/ai-management-demo.gif" alt="" /></p>
<h4>How It Works?</h4>
<p><strong>1. Define resource permissions (<code>AddResourcePermission</code>)</strong>:</p>
<pre><code class="language-csharp">public class MyPermissionDefinitionProvider : PermissionDefinitionProvider
{
    public override void Define(IPermissionDefinitionContext context)
    {
        //other permissions...

        context.AddResourcePermission(
            name: BookManagementPermissions.Manage.Resources.Consume,
            resourceName: BookManagementPermissions.Manage.Resources.Name,
            managementPermissionName: BookManagementPermissions.Manage.ManagePermissions,
            L(&quot;LocalizedPermissionDisplayName&quot;)
        );
    }
}
</code></pre>
<p><strong>2. Use <code>IResourcePermissionChecker.IsGrantedAsync</code> in your code to perform the resource permission check</strong>:</p>
<pre><code class="language-csharp">protected IResourcePermissionChecker ResourcePermissionChecker { get; }

public async Task MyService()
{
    if(await ResourcePermissionChecker.IsGrantedAsync(
        BookManagementPermissions.Manage.Resources.Consume, 
        BookManagementPermissions.Manage.Resources.Name,
        workspaceConfiguration.WorkspaceId!.Value.ToString()))
        {
            return;
        }

        //...
}
</code></pre>
<p><strong>3. Use the relevant <code>ResourcePermissionManagementModel</code> in your UI:</strong></p>
<blockquote>
<p>The following code block demonstrates its usage in the Blazor UI, but the same component is also implemented for MVC &amp; Angular UIs (however, component name might be different, please refer to the documentation before using the component).</p>
</blockquote>
<pre><code class="language-xml">&lt;ResourcePermissionManagementModal @ref=&quot;PermissionManagementModal&quot; /&gt;

@code {
    ResourcePermissionManagementModal PermissionManagementModal { get; set; } = null!;

    private Task OpenResourcePermissionModel()
    {
        await PermissionManagementModal.OpenAsync(
            resourceName: BookManagementPermissions.Manage.Resources.Name, 
            resourceKey: entity.Id.ToString(), 
            resourceDisplayName: entity.Name
        );
    }
}
</code></pre>
<p>This feature integrates perfectly with ABP's existing authorization infrastructure and provides a standard way to implement complex, context-aware authorization scenarios in your applications.</p>
<h3>Introducing the TickerQ Background Worker Provider</h3>
<p>ABP v10.1 now includes <strong><a href="https://tickerq.net/">TickerQ</a></strong> as a new background job and background worker provider option. TickerQ is a fast, reflection-free background task scheduler for .NET — built with source generators, EF Core integration, cron + time-based execution, and a real-time dashboard. It offers reliable job execution with built-in retry mechanisms, persistent job storage, and efficient resource usage.</p>
<p>To use TickerQ in your ABP-based solution, refer to the following documentation:</p>
<ul>
<li><a href="https://abp.io/docs/10.1/framework/infrastructure/background-jobs/tickerq">TickerQ Background Job Integration</a></li>
<li><a href="https://abp.io/docs/10.1/framework/infrastructure/background-workers/tickerq">TickerQ Background Worker Integration</a></li>
</ul>
<h3>Angular UI: Improving Authentication Token Handling</h3>
<p>ABP v10.1 brings significant improvements to <strong>Angular authentication token handling</strong>, making token refresh more reliable and providing better error handling for expired or invalid tokens.</p>
<h4>What's Improved?</h4>
<p>Prior to this version, access tokens issued by the auth-server were stored in localStorage, making them vulnerable to XSS attacks. We've made the following enhancements to improve safety and reduce security risks:</p>
<ul>
<li>Store sensitive tokens in memory</li>
<li>Use web-workers for state sharing between tabs</li>
</ul>
<p>These enhancements are automatically available in new Angular projects and can be applied to existing projects by updating ABP packages.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/issues/23930">#23930</a> for more details.</p>
</blockquote>
<h3>Angular Version Upgrade to v21</h3>
<p>ABP v10.1 <strong>upgrades Angular to version 21</strong>, bringing the latest improvements and features from the Angular ecosystem to your ABP applications. We've upgraded the relevant core Angular packages and 3rd party packages such as <strong>angular-oauth2-oidc</strong> and <strong>ng-bootstrap</strong>. We will also update the ABP Studio templates along with the stable v10.1 release.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/issues/24384">#24384</a> for the complete change list.</p>
</blockquote>
<h3>File Management Module: Public File Sharing Support</h3>
<p><em>This is a <strong>PRO</strong> feature available for ABP Commercial customers.</em></p>
<p>The <strong>File Management Module</strong> now supports <strong>public file sharing</strong> via shareable links, similar to popular cloud storage services like Google Drive or Dropbox. This feature enables you to generate public URLs for files that can be accessed without authentication.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/file-sharing.gif" alt="" /></p>
<p><strong>Example Share URL:</strong></p>
<pre><code class="language-text">https://abp.io/api/file-management/file-descriptor/share?shareToken=CfDJ8AK%2BOEpCD...
</code></pre>
<p><strong>Configuration:</strong></p>
<p>You can configure the public share domain through options:</p>
<pre><code class="language-csharp">Configure&lt;FileManagementWebOptions&gt;(options =&gt;
{
    options.FileDownloadRootUrl = &quot;https://files.yourdomain.com&quot;;
});
</code></pre>
<p>This feature is available for all supported UI types (MVC, Angular, Blazor) and integrates seamlessly with the existing <a href="https://abp.io/docs/latest/modules/file-management">File Management Module</a>.</p>
<h3>Payment Module: Public Page Implementation for Blazor &amp; Angular UIs</h3>
<p>The <strong>Payment Module</strong> now includes <strong>public page implementations for Angular and Blazor UIs</strong>, completing UI coverage across all ABP-supported frameworks. Previously, public payment pages (payment gateway selection, pre-payment, and post-payment pages) were only available for MVC/Razor Pages UI. With this version, both admin and public pages are now available for MVC, Angular, and Blazor UIs.</p>
<p>The public payment pages seamlessly integrate with ABP's <a href="https://abp.io/docs/latest/modules/payment">Payment Module</a> and support all configured payment gateways. The documentation will be updated soon with detailed integration guides and examples at <a href="https://abp.io/docs/latest/modules/payment">abp.io/docs/latest/modules/payment</a>.</p>
<h3>AI Management Module: Blazor &amp; Angular UIs</h3>
<p>With this version, Angular and Blazor UIs for the <a href="https://abp.io/docs/latest/modules/ai-management">AI Management module</a> have been implemented, completing the cross-platform support for this powerful AI integration module.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/ai-management-workspaces.png" alt="AI Management Workspaces" /></p>
<p>The AI Management Module builds on top of <a href="https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence">ABP's AI Infrastructure</a> and provides:</p>
<ul>
<li><strong>Multi-Provider Support</strong>: Integrate with OpenAI, Google Gemini, Anthropic Claude, and more from a unified API</li>
<li><strong>Workspace-Based Organization</strong>: Organize AI capabilities into separate workspaces for different use cases</li>
<li><strong>Built-In Chat Interface</strong>: Ready-to-use chat UI for conversational AI</li>
<li><strong>Chat Widget</strong>: Drop-in chat widget component for customer support or AI assistance</li>
<li><strong>Resource-Based Permissions</strong>: Control access to specific AI workspaces for users, roles, or clients</li>
</ul>
<p>Learn more about the AI Management Module in the <a href="https://abp.io/community/announcements/introducing-the-ai-management-module-nz9404a9">announcement post</a> and <a href="https://abp.io/docs/latest/modules/ai-management">official documentation</a>.</p>
<h3>Identity PRO Module: Password History Support</h3>
<p>The <a href="https://abp.io/docs/latest/modules/identity-pro"><strong>Identity PRO Module</strong></a> now includes <strong>Password History</strong> support, preventing users from reusing previous passwords. This security feature helps enforce stronger password policies and meet compliance requirements for your organization.</p>
<p>Administrators can enable password reuse prevention by toggling the related setting on the <em>Administration -&gt; Settings -&gt; Identity Management</em> page:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/password-history-settings.png" alt="Password History Settings" /></p>
<p>When changing a password, the system checks the specified number of previous passwords and displays an error message if the new password matches any of them:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/set-password-error-modal.png" alt="" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/reset-password-error-modal.png" alt="" /></p>
<h3>Account PRO Module: Introducing WebAuthn Passkeys</h3>
<p>ABP v10.1 introduces <strong>Passkey authentication</strong>, enabling passwordless sign-in using modern biometric authentication methods. Built on the <strong>WebAuthn standard (FIDO2)</strong>, this feature allows users to authenticate using Face ID, Touch ID, Windows Hello, Android biometrics, security keys, or other platform authenticators.</p>
<p><strong>What are Passkeys?</strong></p>
<p>Passkeys are a modern, phishing-resistant authentication method that replaces traditional passwords:</p>
<ul>
<li><strong>Passwordless</strong>: No passwords to remember, type, or manage</li>
<li><strong>Secure</strong>: Uses public/private key cryptography stored on the user's device</li>
<li><strong>Convenient</strong>: Sign in with a fingerprint, face scan, or device PIN</li>
<li><strong>Cross-Platform</strong>: Can sync across devices depending on platform support (Apple, Google, Microsoft)</li>
</ul>
<p><strong>How It Works:</strong></p>
<p><strong>1. Enable or disable the WebAuthn passkeys feature in the <em>Settings -&gt; Account -&gt; Passkeys</em> page:</strong></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/passkey-setting.png" alt="Passkey Setting" /></p>
<p><strong>2. Add your passkeys in the <em>Account/Manage</em> page:</strong></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/my-passkey.png" alt="My Passkeys" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/passkey-registration.png" alt="Passkey registration" /></p>
<p><strong>3. Use the <em>Passkey login</em> option for passwordless authentication the next time you log in:</strong></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/passkey-login.png" alt="Passkey Login" /></p>
<blockquote>
<p>For more information, refer to the <a href="https://abp.io/docs/10.1/modules/account/passkey">Web Authentication API (WebAuthn) passkeys</a> documentation.</p>
</blockquote>
<h2>Community News</h2>
<h3>Special Offer: Level Up Your ABP Skills with 33% Off Live Trainings!</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/live-training-discount.png" alt="ABP Live Training Discount" /></p>
<p>We're excited to announce a special limited-time offer for developers looking to master the ABP Platform! Get <strong>33% OFF</strong> on all ABP live training sessions and accelerate your learning journey with hands-on guidance from ABP experts.</p>
<p><strong>Why Join ABP Live Trainings?</strong></p>
<p>Our live training sessions provide an immersive learning experience where you can:</p>
<ul>
<li><strong>Learn from the Experts</strong>: Get direct instruction from ABP team members and experienced trainers who know the platform inside and out.</li>
<li><strong>Hands-On Practice</strong>: Work through real-world scenarios and build actual applications during the sessions.</li>
<li><strong>Interactive Q&amp;A</strong>: Ask questions in real-time and get immediate answers to your specific challenges.</li>
<li><strong>Comprehensive Coverage</strong>: From fundamentals to advanced topics, our trainings cover everything you need to build production-ready applications with ABP.</li>
<li><strong>Certificate of Completion</strong>: Receive a certificate upon completing the training to showcase your ABP expertise.</li>
</ul>
<p>Don't miss this opportunity to invest in your skills and career. Whether you're new to ABP or looking to advance your expertise, our live trainings provide the structured learning path you need to succeed.</p>
<blockquote>
<p>👉 <a href="https://abp.io/community/announcements/improve-your-abp-skills-with-33-off-live-trainings-hjnw57xu">Learn more and claim your discount here</a></p>
</blockquote>
<h3>Introducing the ABP Referral Program</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/referral-program.png" alt="ABP.IO Referral Program" /></p>
<p>We're thrilled to announce the launch of the <strong>ABP.IO Referral Program</strong>, a new way for our community members to earn rewards while helping others discover the ABP Platform!</p>
<p><strong>How It Works:</strong></p>
<p>ABP's Referral Program is simple and rewarding:</p>
<ol>
<li><strong>Get Your Unique Referral Link</strong>: Sign up for the program and receive your personalized referral link.</li>
<li><strong>Share with Your Network</strong>: Share your link with colleagues, friends, and fellow developers who could benefit from ABP.</li>
<li><strong>Earn Rewards</strong>: When someone purchases an ABP Commercial license through your referral link, <strong>you earn 5% commission</strong>!</li>
</ol>
<p>By joining the referral program, you're not just earning rewards and also you're helping other developers discover a platform that can significantly improve their productivity and project success.</p>
<blockquote>
<p>👉 <a href="https://abp.io/community/announcements/introducing-abp.io-referral-program-b59obhe7">Join the ABP.IO Referral Program</a></p>
</blockquote>
<h3>Announcing AI Management Module</h3>
<p>We are excited to announce the <a href="https://abp.io/docs/10.0/modules/ai-management">AI Management Module</a>, a powerful new module to the ABP Platform that makes managing AI capabilities in your applications easier than ever!</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/ai-management-workspaces.png" alt="ABP - AI Management Module Workspaces" /></p>
<p><strong>What is the AI Management Module?</strong></p>
<p>Built on top of the <a href="https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence">ABP Framework's AI infrastructure</a>, the <strong>AI Management Module</strong> allows you to manage AI workspaces dynamically without touching your code. Whether you're building a customer support chatbot, adding AI-powered search, or creating intelligent automation workflows, this module provides everything you need to manage AI integrations through a user-friendly interface.</p>
<p><strong>Key Features:</strong></p>
<ul>
<li><strong>Multi-Provider Support</strong>: Allows integrating with multiple AI providers including OpenAI, Google Gemini, Anthropic Claude, and more from a single unified API.</li>
<li><strong>Buit-In Chat Interface</strong></li>
<li><strong>Ready to Use Chat Widget</strong></li>
<li>and more... (RAG &amp; MCP supports are on the way!)</li>
</ul>
<p>👉 <a href="https://abp.io/community/announcements/introducing-the-ai-management-module-nz9404a9">Read the announcement post for more...</a></p>
<h3>We Were At .NET Conf China 2025!</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/dotnet-conf-china-2025.png" alt=".NET Conf China 2025" /></p>
<p>The ABP team participated in <strong>.NET Conf China 2025</strong> in Shanghai, celebrating the release of .NET 10 (LTS) and the achievements of the .NET community in China.</p>
<p><strong>Event Highlights:</strong></p>
<p>The conference brought together hundereds of developers and featured Scott Hanselman's opening keynote announcing .NET 10's availability, focused on four pillars: AI, cloud-native, cross-platform, and performance. The event covered three main themes: performance improvements, AI integration, and cross-platform development, with in-depth sessions on topics ranging from Avalonia and Blazor to AI agents and enterprise adoption.</p>
<p><strong>ABP's Participation:</strong></p>
<p>At the ABP booth, we showcased our developer platform with live demonstrations of modular architecture, multi-tenancy support, and built-in authentication systems. We hosted interactive raffles with prizes including ABP stickers, the <em>Mastering ABP Framework</em> book, and Bluetooth headphones. The booth was a hub for sharing experiences, impromptu code walkthroughs, and meaningful conversations with Chinese developers about ABP's future.</p>
<blockquote>
<p>👉 <a href="https://abp.io/community/announcements/.net-conf-china-2025-fz03gfge">Read the full event recap</a></p>
</blockquote>
<h3>Community Talks 2025.10: AI-Powered .NET Apps with ABP &amp; Microsoft Agent Framework</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2026-01-08%20v10_1_Preview/community-talk-2025-10-ai.png" alt="ABP Community Talks - AI-Powered .NET Apps" /></p>
<p>In our latest ABP Community Talks session, we dove deep into the world of <strong>Artificial Intelligence</strong> and its integration with the ABP Framework. This session explored Microsoft's cutting-edge AI libraries: <strong>Extensions AI</strong>, <strong>Semantic Kernel</strong>, and the <strong>Microsoft Agent Framework</strong>.</p>
<p><strong>What We Covered:</strong></p>
<p>We introduced the new <strong>AI Management Module</strong>, discussing its current status and roadmap. The session included practical demonstrations on building intelligent applications with the Microsoft Agent Framework within ABP projects, showing how these technologies empower developers to create AI-powered .NET applications.</p>
<blockquote>
<p>👉 <a href="https://www.youtube.com/live/tEcd2H6yXQk">Missed the live session? Click here to watch the full session</a></p>
</blockquote>
<h3>New ABP Community Articles</h3>
<p>There are exciting articles contributed by the ABP community as always. I will highlight some of them here:</p>
<ul>
<li><a href="https://github.com/salihozkara">Salih Özkara</a> has published 3 new articles:
<ul>
<li><a href="https://abp.io/community/articles/building-dynamic-xml-sitemaps-with-abp-framework-n3q6schd">Building Dynamic XML Sitemaps with ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/implement-automatic-methodlevel-caching-in-abp-framework-4uzd3wx8">Implement Automatic Method-Level Caching in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/building-production-ready-llm-applications-with-net-ya7qemfa">Building Production-Ready LLM Applications with .NET: A Practical Guide</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/adnanaldaim">Adnan Ali</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/integrating-ai-into-abp.io-applications-the-complete-guide-jc9fbjq0">Integrating AI into ABP.IO Applications: The Complete Guide to Volo.Abp.AI and AI Management Module</a></li>
<li><a href="https://abp.io/community/articles/how-abp.io-framework-cuts-your-mvp-development-time-by-60-8l7m3ugj">How ABP.IO Framework Cuts Your MVP Development Time by 60%</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/my-first-look-and-experience-with-google-antigravity-0hr4sjtf">My First Look and Experience with Google AntiGravity</a> by <a href="https://twitter.com/alperebicoglu">Alper Ebiçoğlu</a></li>
<li><a href="https://abp.io/community/articles/toon-vs-json-b4rn2avd">TOON vs JSON for LLM Prompts in ABP: Token-Efficient Structured Context</a> by <a href="https://abp.io/community/members/suhaib-mousa">Suhaib Mousa</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP-related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/10.1/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v10.1 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1ec32c-ba27-e6c6-0c09-8c3531b3fc30" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1ec32c-ba27-e6c6-0c09-8c3531b3fc30" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/where-and-how-to-store-your-blob-objects-in-.net-r2r1vjjd</guid>
      <link>https://abp.io/community/posts/where-and-how-to-store-your-blob-objects-in-.net-r2r1vjjd</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-framework</category>
      <category>blob-storing</category>
      <category>blob-storage</category>
      <category>.net</category>
      <title>Where and How to Store Your BLOB Objects in .NET?</title>
      <description>In this article, we'll explore different approaches to storing BLOBs in .NET applications and demonstrate how the ABP Framework simplifies this process with its flexible BLOB Storing infrastructure.</description>
      <pubDate>Tue, 30 Sep 2025 14:02:35 Z</pubDate>
      <a10:updated>2026-09-26T00:50:13Z</a10:updated>
      <content:encoded><![CDATA[<h1>Where and How to Store Your BLOB Objects in .NET?</h1>
<p>When building modern web applications, managing <a href="https://cloud.google.com/discover/what-is-binary-large-object-storage">BLOBs (Binary Large Objects)</a> such as images, videos, documents, or any other file types is a common requirement. Whether you're developing a CMS, an e-commerce platform, or almost any other kind of application, you'll eventually ask yourself: <strong>&quot;Where should I store these files?&quot;</strong></p>
<p>In this article, we'll explore different approaches to storing BLOBs in .NET applications and demonstrate how the ABP Framework simplifies this process with its flexible <a href="https://abp.io/docs/latest/framework/infrastructure/blob-storing">BLOB Storing infrastructure</a>.</p>
<p>ABP Provides <a href="https://abp.io/docs/latest/framework/infrastructure/blob-storing#blob-storage-providers">multiple storage providers</a> such as Azure, AWS, Google, Minio, Bunny etc. But for the simplicity of this article, we will only focus on the <strong>Database Provider</strong>, showing you how to store BLOBs in database tables step-by-step.</p>
<h2>Understanding BLOB Storage Options</h2>
<p>Before diving into implementation details, let's understand the common approaches for storing BLOBs in .NET applications. Mainly, there are three main approaches:</p>
<ol>
<li>Database Storage</li>
<li>File System Storage</li>
<li>Cloud Storage</li>
</ol>
<h3>1. Database Storage</h3>
<p>The first approach is to store BLOBs directly in the database alongside your relational data (<em>you can also store them separately</em>). This approach uses columns with types like <code>VARBINARY(MAX)</code> in SQL Server or <code>BYTEA</code> in PostgreSQL.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>✅ Transactional consistency between files and related data</li>
<li>✅ Simplified backup and restore operations (everything in one place)</li>
<li>✅ No additional file system permissions or management needed</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>❌ Database size can grow significantly with large files</li>
<li>❌ Potential performance impact on database operations</li>
<li>❌ May require additional database tuning and optimization</li>
<li>❌ Increased backup size and duration</li>
</ul>
<h3>2. File System Storage</h3>
<p>The second obvious approach is to store BLOBs as physical files in the server's file system. This approach is simple and easy to implement. Also, it's possible to use these two approaches together and keep the metadata and file references in the database.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>✅ Better performance for large files</li>
<li>✅ Reduced database size and improved database performance</li>
<li>✅ Easier to leverage CDNs and file servers</li>
<li>✅ Simple to implement file system-level operations (compression, deduplication)</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>❌ Requires separate backup strategy for files</li>
<li>❌ Need to manage file system permissions</li>
<li>❌ Potential synchronization issues in distributed environments</li>
<li>❌ More complex cleanup operations for orphaned files</li>
</ul>
<h3>3. Cloud Storage (Azure, AWS S3, etc.)</h3>
<p>The third approach can be using cloud storage services for scalability and global distribution. This approach is powerful and scalable. But it's also more complex to implement and manage.</p>
<p><strong>Best for:</strong></p>
<ul>
<li>Large-scale applications</li>
<li>Multi-region deployments</li>
<li>Content delivery requirements</li>
</ul>
<h2>ABP Framework's BLOB Storage Infrastructure</h2>
<p>The ABP Framework provides an abstraction layer over different storage providers, allowing you to switch between them with minimal code changes. This is achieved through the <strong>IBlobContainer</strong> (and <code>IBlobContainer&lt;TContainerType&gt;</code>) service and various provider implementations.</p>
<blockquote>
<p>ABP provides several built-in providers, which you can see the full list <a href="https://abp.io/docs/latest/framework/infrastructure/blob-storing#blob-storage-providers">here</a>.</p>
</blockquote>
<p>Let's see how to use the Database provider in your application step by step.</p>
<h3>Demo: Storing BLOBs in Database in an ABP-Based Application</h3>
<p>In this demo, we'll walk through a practical example of storing BLOBs in a database using ABP's BLOB Storing infrastructure. We'll focus on the backend implementation using the <code>IBlobContainer</code> service and examine the database structure that ABP creates automatically. The UI framework choice doesn't matter for this demonstration, as we're concentrating on the core BLOB storage functionality.</p>
<p>If you don't have an ABP application yet, create one using the ABP CLI:</p>
<pre><code class="language-bash">abp new BlobStoringDemo
</code></pre>
<p>This command generates a new ABP layered application named <code>BlobStoringDemo</code> with <strong>MVC</strong> as the default UI and <strong>SQL Server</strong> as the default database provider.</p>
<h4>Understanding the Database Provider Setup</h4>
<p>When you create a layered ABP application, it automatically includes the BLOB Storing infrastructure with the Database Provider pre-configured. You can verify this by examining the module dependencies in your <code>*Domain</code>, <code>*DomainShared</code>, and <code>*EntityFrameworkCore</code> modules:</p>
<pre><code class="language-csharp">[DependsOn(
    //...
    typeof(BlobStoringDatabaseDomainModule) // &lt;-- This is the Database Provider
    )]
public class BlobStoringDemoDomainModule : AbpModule
{
    //...
}
</code></pre>
<p>Since the Database Provider is already included through module dependencies, no additional configuration is required to start using it. The provider is ready to use out of the box.</p>
<p>However, if you're working with multiple BLOB storage providers or want to explicitly configure the Database Provider, you can add the following configuration to your <code>*EntityFrameworkCore</code> module's <code>ConfigureServices</code> method:</p>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.ConfigureDefault(container =&gt; 
    {
        container.UseDatabase();
    });
});
</code></pre>
<blockquote>
<p><strong>Note:</strong> This explicit configuration is optional when using only one BLOB provider (Database Provider in this case), but becomes necessary when managing multiple providers or custom container configurations.</p>
</blockquote>
<h4>Running Database Migrations</h4>
<p>Now, let's apply the database migrations to create the necessary BLOB storage tables. Run the <code>DbMigrator</code> project:</p>
<pre><code class="language-bash">cd src/BlobStoringDemo.DbMigrator
dotnet run
</code></pre>
<p>Once the migration completes successfully, open your database management tool and you'll see two new tables:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-09-30-Where-and-How-to-Store-Your-BLOB-Objects-in-dotnet/blob-tables.png" alt="" /></p>
<p><strong>Understanding the BLOB Storage Tables:</strong></p>
<ul>
<li><p><strong><code>AbpBlobContainers</code></strong>: Stores metadata about BLOB containers, including container names, tenant information, and any custom properties.</p>
</li>
<li><p><strong><code>AbpBlobs</code></strong>: Stores the actual BLOB content (the binary data) along with references to their parent containers. Each BLOB is associated with a container through a foreign key relationship.</p>
</li>
</ul>
<p>When you save a BLOB, ABP automatically handles the database operations: the binary content goes into <code>AbpBlobs</code>, while the container configuration and metadata are managed in <code>AbpBlobContainers</code>.</p>
<h4>Creating a File Management Service</h4>
<p>Let's implement a practical application service that demonstrates common BLOB operations. Create a new application service class:</p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;

namespace BlobStoringDemo
{
    public class FileAppService : ApplicationService, IFileAppService
    {
        private readonly IBlobContainer _blobContainer;

        public FileAppService(IBlobContainer blobContainer)
        {
            _blobContainer = blobContainer;
        }

        public async Task SaveFileAsync(string fileName, byte[] fileContent)
        {
            // Save the file
            await _blobContainer.SaveAsync(fileName, fileContent);
        }

        public async Task&lt;byte[]&gt; GetFileAsync(string fileName)
        {
            // Get the file
            return await _blobContainer.GetAllBytesAsync(fileName);
        }

        public async Task&lt;bool&gt; FileExistsAsync(string fileName)
        {
            // Check if file exists
            return await _blobContainer.ExistsAsync(fileName);
        }

        public async Task DeleteFileAsync(string fileName)
        {
            // Delete the file
            await _blobContainer.DeleteAsync(fileName);
        }
    }
}
</code></pre>
<p>Here, we are doing the followings:</p>
<ul>
<li>Injecting the <code>IBlobContainer</code> service.</li>
<li>Saving the BLOB data to the database with the <code>SaveAsync</code> method. (<em>it allows you to use byte arrays or streams</em>)</li>
<li>Retrieving the BLOB data from the database with the <code>GetAllBytesAsync</code> method.</li>
<li>Checking if the BLOB exists with the <code>ExistsAsync</code> method.</li>
<li>Deleting the BLOB data from the database with the <code>DeleteAsync</code> method.</li>
</ul>
<p>With this service in place, you can now manage BLOBs throughout your application without worrying about the underlying storage implementation. Simply inject <code>IFileAppService</code> wherever you need file operations, and ABP handles all the provider-specific details behind the scenes.</p>
<blockquote>
<p>Also, it's good to highlight that, the beauty of this approach is <strong>provider independence</strong>: you can start with database storage and later switch to Azure Blob Storage, AWS S3, or any other provider without modifying a single line of your application code. We'll explore this powerful feature in the next section.</p>
</blockquote>
<h3>Switching Between Providers</h3>
<p>One of the biggest advantages of using ABP's BLOB Storage system is the ability to switch providers without changing your application code.</p>
<p>For example, you might start with the <a href="https://abp.io/docs/latest/framework/infrastructure/blob-storing/file-system">File System provider</a> during development and switch to <a href="https://abp.io/docs/latest/framework/infrastructure/blob-storing/azure">Azure Blob Storage</a> for production:</p>
<p><strong>Development:</strong></p>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.ConfigureDefault(container =&gt;
    {
        container.UseFileSystem(fileSystem =&gt;
        {
            fileSystem.BasePath = Path.Combine(
                hostingEnvironment.ContentRootPath, 
                &quot;Documents&quot;
            );
        });
    });
});
</code></pre>
<p><strong>Production:</strong></p>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.ConfigureDefault(container =&gt;
    {
        container.UseAzure(azure =&gt;
        {
            azure.ConnectionString = &quot;your azure connection string&quot;;
            azure.ContainerName = &quot;your azure container name&quot;;
            azure.CreateContainerIfNotExists = true;
        });
    });
});
</code></pre>
<p><strong>Your application code remains unchanged!</strong> You just need to install the appropriate package and update the configuration. You can even use pragmas (for example: <code>#if !DEBUG</code>) to switch the provider at runtime (or use similar techniques).</p>
<h3>Using Named BLOB Containers</h3>
<p>ABP allows you to define multiple BLOB containers with different configurations. This is useful when you need to store different types of files using different providers. Here are the steps to implement it:</p>
<h4>Step 1: Define a BLOB Container</h4>
<pre><code class="language-csharp">[BlobContainerName(&quot;profile-pictures&quot;)]
public class ProfilePictureContainer
{
}

[BlobContainerName(&quot;documents&quot;)]
public class DocumentContainer
{
}
</code></pre>
<h4>Step 2: Configure Different Providers for Each Container</h4>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    // Profile pictures stored in database
    options.Containers.Configure&lt;ProfilePictureContainer&gt;(container =&gt;
    {
        container.UseDatabase();
    });

    // Documents stored in file system
    options.Containers.Configure&lt;DocumentContainer&gt;(container =&gt;
    {
        container.UseFileSystem(fileSystem =&gt;
        {
            fileSystem.BasePath = Path.Combine(
                hostingEnvironment.ContentRootPath, 
                &quot;Documents&quot;
            );
        });
    });
});
</code></pre>
<h4>Step 3: Use the Named Containers</h4>
<p>Once you have defined the BLOB Containers, you can use the <code>IBlobContainer&lt;TContainerType&gt;</code> service to access the BLOB containers:</p>
<pre><code class="language-csharp">public class ProfileService : ApplicationService
{
    private readonly IBlobContainer&lt;ProfilePictureContainer&gt; _profilePictureContainer;

    public ProfileService(IBlobContainer&lt;ProfilePictureContainer&gt; profilePictureContainer)
    {
        _profilePictureContainer = profilePictureContainer;
    }

    public async Task UpdateProfilePictureAsync(Guid userId, byte[] picture)
    {
        var blobName = $&quot;{userId}.jpg&quot;;
        await _profilePictureContainer.SaveAsync(blobName, picture);
    }
}
</code></pre>
<p>With this approach, your documents and profile pictures are stored in different containers and different providers. This is useful when you need to store different types of files using different providers and need scalability and performance.</p>
<h2>Conclusion</h2>
<p>Managing BLOBs effectively is crucial for modern applications, and choosing the right storage approach depends on your specific needs.</p>
<p>ABP's BLOB Storing infrastructure provides a powerful abstraction that lets you start with one provider and switch to another as your requirements evolve, all without changing your application code.</p>
<p>Whether you're storing files in a database, file system, or cloud storage, ABP's BLOB Storing system provides a flexible and powerful way to manage your files.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1cad1b-7e6a-3aa5-9ea9-ed133101032a" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1cad1b-7e6a-3aa5-9ea9-ed133101032a" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/building-a-permissionbased-authorization-system-for-asp.net-core-owyszy0b</guid>
      <link>https://abp.io/community/posts/building-a-permissionbased-authorization-system-for-asp.net-core-owyszy0b</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <title>Building a Permission-Based Authorization System for ASP.NET Core</title>
      <description>In this article, we'll explore different authorization approaches in ASP.NET Core and examine how ABP's permission-based authorization system works.

First, we'll look at some of the core authorization types that come with ASP.NET Core, such as role-based, claims-based, policy-based, and resource-based authorization. We'll briefly review the pros and cons of each approach.

Then, we'll dive into ABP's Permission-Based Authorization System.</description>
      <pubDate>Thu, 28 Aug 2025 06:25:18 Z</pubDate>
      <a10:updated>2026-09-26T01:58:11Z</a10:updated>
      <content:encoded><![CDATA[<h1>Building a Permission-Based Authorization System for ASP.NET Core</h1>
<p>In this article, we'll explore different authorization approaches in ASP.NET Core and examine how ABP's permission-based authorization system works.</p>
<p>First, we'll look at some of the core authorization types that come with ASP.NET Core, such as role-based, claims-based, policy-based, and resource-based authorization. We'll briefly review the pros and cons of each approach.</p>
<p>Then, we'll dive into <a href="https://abp.io/docs/latest/framework/fundamentals/authorization#permission-system">ABP's Permission-Based Authorization System</a>. This is a more advanced approach that gives you fine-grained control over what users can do in your application. We'll also explore ABP's Permission Management Module, which makes managing permissions through the UI easily.</p>
<h2>Understanding ASP.NET Core Authorization Types</h2>
<p>Before diving into permission-based authorization, let's examine some of the core authorization types available in ASP.NET Core:</p>
<ul>
<li><p><strong><a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-9.0">Role-Based Authorization</a></strong> checks if the current user belongs to specific roles (like <strong>&quot;Admin&quot;</strong> or <strong>&quot;User&quot;</strong>) and grants access based on these roles. (For example, only users in the <strong>&quot;Manager&quot;</strong> role can access the employee salary management page.)</p>
</li>
<li><p><strong><a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-9.0">Claims-Based Authorization</a></strong> uses key-value pairs (claims) that describe user attributes, such as age, department, or security clearance. (For example, only users with a <strong>&quot;Department=Finance&quot;</strong> claim can view financial reports.) This provides more granular control but requires careful claim management (such as grouping claims under policies).</p>
</li>
<li><p><strong><a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-9.0">Policy-Based Authorization</a></strong> combines multiple requirements (roles, claims, custom logic) into reusable policies. It offers flexibility and centralized management, and <strong>this is exactly why ABP's permission system is built on top of it!</strong> (We'll discuss this in more detail later.)</p>
</li>
<li><p><strong><a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-9.0">Resource-Based Authorization</a></strong> determines access by examining both the user and the specific item they want to access. (For example, a user can edit only their own blog posts, not others' posts.) Unlike policy-based authorization which applies the same rules everywhere, resource-based authorization makes decisions based on the actual data being accessed, requiring more complex implementation.</p>
</li>
</ul>
<p>Here's a quick comparison of these approaches:</p>
<p>| Authorization Type | Pros | Cons |
|-------------------|------|------|
| <strong>Role-Based</strong> | Simple implementation, easy to understand | Becomes inflexible with complex role hierarchies |
| <strong>Claims-Based</strong> | Granular control, flexible user attributes | Complex claim management, potential for claim explosion |
| <strong>Policy-Based</strong> | Centralized logic, combines multiple requirements | Can become complex with numerous policies |
| <strong>Resource-Based</strong> | Fine-grained per-resource control | Implementation complexity, resource-specific code |</p>
<h2>What is Permission-Based Authorization?</h2>
<p>Permission-based authorization takes a different approach from other authorization types by defining specific permissions (like <strong>&quot;CreateUser&quot;</strong>, <strong>&quot;DeleteOrder&quot;</strong>, <strong>&quot;ViewReports&quot;</strong>) that represent granular actions within your application. These permissions can be assigned to users directly or through roles, providing both flexibility and clear action-based access control.</p>
<p>ABP Framework's permission system is built on top of this approach and extends ASP.NET Core's policy-based authorization system, working seamlessly with it.</p>
<h2>ABP Framework's Permission System</h2>
<p>ABP extends <a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-9.0">ASP.NET Core Authorization</a> by adding <strong>permissions</strong> as automatic <a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-9.0">policies</a> and allows the authorization system to be used in application services as well.</p>
<p>This system provides a clean abstraction while maintaining full compatibility with ASP.NET Core's authorization infrastructure.</p>
<p>ABP also provides a <a href="https://abp.io/docs/latest/modules/permission-management">Permission Management Module</a> that offers a complete UI and API for managing permissions. This allows you to easily manage permissions in the UI, assign permissions to roles or users, and much more. (We'll see how to use it in the following sections.)</p>
<h3>Defining Permissions in ABP</h3>
<p>In ABP, permissions are defined in classes (typically under the <code>*.Application.Contracts</code> project) that inherit from the <code>PermissionDefinitionProvider</code> class. Here's how you can define permissions for a book management system:</p>
<pre><code class="language-csharp">public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
    public override void Define(IPermissionDefinitionContext context)
    {
        var bookStoreGroup = context.AddGroup(&quot;BookStore&quot;);

        var booksPermission = bookStoreGroup.AddPermission(&quot;BookStore.Books&quot;, L(&quot;Permission:Books&quot;));
        booksPermission.AddChild(&quot;BookStore.Books.Create&quot;, L(&quot;Permission:Books.Create&quot;));
        booksPermission.AddChild(&quot;BookStore.Books.Edit&quot;, L(&quot;Permission:Books.Edit&quot;));
        booksPermission.AddChild(&quot;BookStore.Books.Delete&quot;, L(&quot;Permission:Books.Delete&quot;));
    }

    private static LocalizableString L(string name)
    {
        return LocalizableString.Create&lt;BookStoreResource&gt;(name);
    }
}
</code></pre>
<p>ABP automatically discovers this class and registers the permissions/policies in the system. You can then assign these permissions/policies to users/roles. There are two ways to do this:</p>
<ul>
<li>Using the <a href="https://abp.io/docs/latest/modules/permission-management">Permission Management Module</a></li>
<li>Using the <code>IPermissionManager</code> service (via code)</li>
</ul>
<h4>Setting Permissions to Roles and Users via Permission Management Module</h4>
<p>When you define a permission, it also becomes usable in the ASP.NET Core authorization system as a <strong>policy name</strong>. If you are using the <a href="https://abp.io/docs/latest/modules/permission-management">Permission Management Module</a>, you can manage the permissions through the UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-08-27-Building-a-permission-based-authorization-system-for-net-core/permission-management-module.png" alt="" /></p>
<p>In the permission management UI, you can grant permissions to roles and users through the <strong>Role Management</strong> and <strong>User Management</strong> pages within the &quot;permissions&quot; modals. You can then easily check these permissions in your code. In the screenshot above, you can see the permission modal for the user's page, clearly showing the permissions granted to the user by their role. (<strong>(R)</strong> in the UI indicates that the permission is granted by one of the current user's roles.)</p>
<h4>Setting Permissions to Roles and Users via Code</h4>
<p>You can also set permissions for roles and users programmatically. You just need to inject the <code>IPermissionManager</code> service and use its <code>SetForRoleAsync</code> and <code>SetForUserAsync</code> methods (or similar methods):</p>
<pre><code class="language-csharp">public class MyService : ITransientDependency
{
    private readonly IPermissionManager _permissionManager;

    public MyService(IPermissionManager permissionManager)
    {
        _permissionManager = permissionManager;
    }

    public async Task GrantPermissionForUserAsync(Guid userId, string permissionName)
    {
        await _permissionManager.SetForUserAsync(userId, permissionName, true);
    }

    public async Task ProhibitPermissionForUserAsync(Guid userId, string permissionName)
    {
        await _permissionManager.SetForUserAsync(userId, permissionName, false);
    }
}
</code></pre>
<h3>Checking Permissions in AppServices and Controllers</h3>
<p>ABP provides multiple ways to check permissions. The most common approach is using the <code>[Authorize]</code> attribute and passing the permission/policy name.</p>
<p>Here is an example of how to check permissions in an application service:</p>
<pre><code class="language-csharp">[Authorize(&quot;BookStore.Books&quot;)]
public class BookAppService : ApplicationService, IBookAppService
{
    [Authorize(&quot;BookStore.Books.Create&quot;)]
    public async Task&lt;BookDto&gt; CreateAsync(CreateBookDto input)
    {
        //logic here
    }
}
</code></pre>
<blockquote>
<p>Notice that you can use the <code>[Authorize]</code> attribute at both class and method levels. In the example above, the <code>CreateAsync</code> method is marked with the <code>[Authorize]</code> attribute, so it will check the user's permission before executing the method. Since the application service class also has a permission requirement, both permissions must be granted to the user to execute the method!</p>
</blockquote>
<p>And here is an example of how to check permissions in a controller:</p>
<pre><code class="language-csharp">[Authorize(&quot;BookStore.Books&quot;)]
public class CreateBookController : AbpController
{
    //omitted for brevity...
}
</code></pre>
<h3>Programmatic Permission Checking</h3>
<p>To conditionally control authorization in your code, you can use the <code>IAuthorizationService</code> service:</p>
<pre><code class="language-csharp">public class BookAppService : ApplicationService, IBookAppService
{
    public async Task&lt;BookDto&gt; CreateAsync(CreateBookDto input)
    {
        // Checks the permission and throws an exception if the user does not have the permission
        await AuthorizationService.CheckAsync(BookStorePermissions.Books.Create);
        
        // Your logic here
    }

    public async Task&lt;bool&gt; CanUserCreateBooksAsync()
    {
        // Checks if the permission is granted for the current user
        return await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create);
    }
}
</code></pre>
<p>You can use the <code>IAuthorizationService</code>'s helpful methods for authorization checking, as shown in the example above:</p>
<ul>
<li><code>IsGrantedAsync</code> checks if the current user has the given permission.</li>
<li><code>CheckAsync</code> throws an exception if the current user does not have the given permission.</li>
<li><code>AuthorizeAsync</code> checks if the current user has the given permission and returns an <code>AuthorizationResult</code>, which has a <code>Succeeded</code> property that you can use to verify if the user has the permission.</li>
</ul>
<p>Also notice that we did not inject the <code>IAuthorizationService</code> in the constructor, because we are using the <code>ApplicationService</code> base class, which already provides property injection for it. This means we can directly use it in our application services, just like other helpful base services (such as <code>ICurrentUser</code> and <code>ICurrentTenant</code>).</p>
<h2>Conclusion</h2>
<p>Permission-based authorization in ABP Framework provides a powerful and flexible approach to securing your applications. By building on ASP.NET Core's policy-based authorization, ABP offers a clean abstraction that simplifies permission management while maintaining the full power of the underlying system.</p>
<p>The ability to check permissions in both application services and controllers makes ABP Framework's authorization system very flexible and powerful, yet easy to use.</p>
<p>Additionally, the Permission Management Module makes it very easy to manage permissions and roles through the UI. You can learn more about how it works in the <a href="https://abp.io/docs/latest/modules/permission-management">documentation</a>.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1c0186-fb6b-f12d-5de2-f4efcf1cd077" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1c0186-fb6b-f12d-5de2-f4efcf1cd077" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-9.3-final-has-been-released-fw4n9sng</guid>
      <link>https://abp.io/community/posts/abp.io-platform-9.3-final-has-been-released-fw4n9sng</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP.IO Platform 9.3 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 9.3 stable version has been released today.</description>
      <pubDate>Thu, 14 Aug 2025 08:30:03 Z</pubDate>
      <a10:updated>2026-09-26T00:04:32Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 9.3 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 9.3 stable version has been released today.</p>
<h2>What's New With Version 9.3?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/announcements/announcing-abp-9-3-release-candidate-4dqgiryf">9.3 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 9.3</h2>
<h3>Creating New Solutions</h3>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a> to create new solutions.</p>
<blockquote>
<p><strong>Note</strong>: ABP Studio <strong>v1.2.1</strong> has been released with support for <strong>ABP 9.3</strong>. If you already have ABP Studio installed, update it to v1.2.1 (or later, if available) to create new applications targeting 9.3. ABP Studio checks for updates automatically and will prompt you in-app modal to update to the latest version, or you can download the latest installer from the <a href="https://abp.io/studio">Studio</a> page. See the <a href="https://abp.io/docs/latest/studio/installation#upgrading">upgrading guide</a> for details. After updating, the New Solution wizard will create applications with ABP 9.3 by default. You can check the <a href="https://abp.io/docs/latest/studio/version-mapping">ABP Studio and ABP Startup Template Version Mappings</a> documentation to see the corresponding ABP versions for other versions of Studio.</p>
</blockquote>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-08-08%20v9_3_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.2: <a href="https://abp.io/docs/9.3/release-info/migration-guides/abp-9-3">ABP Version 9.3 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/fahrigedik">Fahri Gedik</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/a-modern-approach-to-angular-dependency-injection-using-8np4o1ap">A Modern Approach to Angular Dependency Injection using inject function</a></li>
<li><a href="https://abp.io/community/articles/angular-application-builder-transitioning-from-webpack-to-3yzhzfl0">Angular Application Builder: Transitioning from Webpack to Esbuild</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/benjaminsqlserver@gmail.com">Benjamin Fadina</a> has published several videos on various topics such as <strong>Blazor Web Assembly Using ABP.IO</strong>, <strong>CQRS Implementation with MediatR in ABP</strong> and more. You can see all his videos <a href="https://abp.io/community/members/benjaminsqlserver@gmail.com">here</a>.</li>
<li><a href="https://abp.io/community/members/mansur.besleney">Mansur Besleney</a> has published <a href="https://abp.io/community/articles/how-to-build-persistent-background-jobs-with-abp-framework-n9aloh93">How to Build Persistent Background Jobs with ABP Framework and Quartz</a></li>
<li><a href="https://x.com/hibrahimkalkan">Halil Ibrahim Kalkan</a> has published <a href="https://abp.io/community/articles/multitenancy-with-separate-databases-in-dotnet-and-abp-51nvl4u9">Multitenancy with Separate Databases in .NET and ABP</a></li>
<li><a href="https://abp.io/community/members/alex.maiereanu@3sstudio.com">Alex Maiereanu</a> has published <a href="https://abp.io/community/articles/abphangfireazurepostgresql-s1jnf3yg">ABP-Hangfire-AzurePostgreSQL</a></li>
<li><a href="https://abp.io/community/members/jfistelmann">Jack Fistelmann</a> has published <a href="https://abp.io/community/articles/abp-and-maildev-gy13cr1p">ABP and maildev</a></li>
<li><a href="https://abp.io/community/members/harshgupta">Harsh Gupta</a> has published <a href="https://abp.io/community/articles/how-to-add-a-module-in-the-abp.io-application-sdeajkn6">How to Add a Module in the ABP.io Application?</a></li>
<li><a href="https://abp.io/community/members/mtozdemir">Tarık Özdemir</a> has published <a href="https://abp.io/community/articles/AI-First%20Architecture%20for%20.NET%20Projects%3A%20A%20Modern%20Blueprint-h2wgcoq3">AI-First Architecture for .NET Projects: A Modern Blueprint Inspired by McKinsey</a></li>
<li><a href="https://github.com/maliming">Liming Ma</a> has published <a href="https://abp.io/community/articles/using-hangfire-dashboard-in-abp-api-website--r32ox497">Using Hangfire Dashboard in ABP API Website</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 10.0. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1bb9e0-294b-e2e0-a7c8-8e0213a4abd9" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1bb9e0-294b-e2e0-a7c8-8e0213a4abd9" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-9.3-rc-has-been-released-4dqgiryf</guid>
      <link>https://abp.io/community/posts/abp-platform-9.3-rc-has-been-released-4dqgiryf</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP Platform 9.3 RC Has Been Released</title>
      <description>We are happy to release ABP version 9.3 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v9.3! Thanks to you in advance.</description>
      <pubDate>Wed, 18 Jun 2025 14:11:22 Z</pubDate>
      <a10:updated>2026-09-26T00:03:36Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 9.3 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>9.3 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v9.3! Thanks to you in advance.</p>
<h2>Get Started with the 9.3 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/studio-switch-to-preview.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.2 or earlier: <a href="https://abp.io/docs/9.3/release-info/migration-guides/abp-9-3">ABP Version 9.3 Migration Guide</a></p>
<h2>What's New with ABP v9.3?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>Cron Expression Support for Background Workers</li>
<li>Docs Module: PDF Export</li>
<li>Angular UI: Standalone Package Structure</li>
<li>Upgraded to Blazorise v1.7.7</li>
<li>Audit Logging Module: Excel Export</li>
</ul>
<h3>Cron Expression Support for Background Workers</h3>
<p>We've enhanced the <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers">Background Workers System</a> by adding support for Cron expressions when using <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers/hangfire">Hangfire</a> or <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers/quartz">Quartz</a> as the background worker manager. This new feature provides more flexibility in scheduling background tasks compared to the simple period-based timing system.</p>
<p>Now you can define complex scheduling patterns using standard Cron expressions. For example, you can schedule a task to run: &quot;Every day at midnight&quot;, &quot;Every Monday at 9 AM&quot;, or &quot;First day of every month&quot;.</p>
<p>Here's how you can use it in your background worker:</p>
<pre><code class="language-csharp">public class MyPeriodicBackgroundWorker : AsyncPeriodicBackgroundWorkerBase
{
    public MyPeriodicBackgroundWorker(
        AbpAsyncTimer timer,
        IServiceScopeFactory serviceScopeFactory)
        : base(timer, serviceScopeFactory)
    {
        // You can either use Period for simple intervals
        Timer.Period = 600000; //10 minutes

        // 👇 or use CronExpression for more complex scheduling 👇
        CronExpression = &quot;0 0/10 * * * ?&quot;; //Run every 10 minutes
    }

    protected async override Task DoWorkAsync(
        PeriodicBackgroundWorkerContext context)
    {
        // Your background work...
    }
}
</code></pre>
<p>The <code>CronExpression</code> property takes precedence over the <code>Period</code> property when both are set. This feature is available when you use either the <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers/hangfire">Hangfire</a> or <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers/quartz">Quartz</a> background worker managers.</p>
<blockquote>
<p>See the <a href="https://abp.io/docs/9.3/framework/infrastructure/background-workers">Background Workers documentation</a> for more information about configuring and using background workers with Cron expressions.</p>
</blockquote>
<h3>Docs Module: PDF Export</h3>
<p>We're excited to introduce a new feature in the Docs Module that allows users to export documentation as PDF files. This feature makes it easier for users to access documentation offline or share it with team members who might not have immediate access to the online documentation system.</p>
<p><strong>Administrators can generate PDF files from the back-office side</strong>:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/generate-pdf-docs.png" alt="PDF generation settings in the admin side" /></p>
<p>and <strong>then a &quot;Download PDF&quot; button appears in the document system</strong> (as shown in the image below - the bottom right of the navigation menu -), allowing users to download the compiled documentation as a PDF file:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/download-pdf-on-docs.png" alt="Download PDF button in the documentation system" /></p>
<p>The feature supports multiple versions of documentation, different language variants, and ensures proper formatting of all content including code blocks and technical documentation.</p>
<h3>Angular UI: Standalone Package Structure</h3>
<p>ABP v9.3 introduces support for Angular's standalone components architecture while maintaining <strong>full compatibility with existing module-based applications</strong>. This update aligns with Angular's strategic direction toward standalone components as the recommended approach for building Angular applications.</p>
<p>The key improvements include:</p>
<ul>
<li><strong>Dual-support routing configurations</strong> that work seamlessly with both module-based and standalone approaches</li>
<li><strong>ABP Suite integration</strong> for generating code that supports standalone components</li>
<li><strong>Updated schematics</strong> that provide templates for both development patterns</li>
</ul>
<p>This enhancement gives developers the flexibility to choose their preferred Angular architecture. Existing module-based applications <strong>continue to work without modifications</strong>, while new projects can leverage the standalone approach for simplified dependency management, reduced boilerplate code, and better lazy-loading capabilities.</p>
<blockquote>
<p>For developers interested in migrating to standalone components or starting new projects, we'll be publishing a comprehensive blog post with detailed guidance and best practices. In the meantime, you can check <a href="https://github.com/abpframework/abp/pull/22829">#22829</a> for implementation details of the standalone package structure and make the necessary changes to your project.</p>
</blockquote>
<h3>Upgraded to Blazorise v1.7.7</h3>
<p>Upgraded the <a href="https://blazorise.com/">Blazorise</a> library to v1.7.7 for Blazor UI. If you are upgrading your project to v9.3.0, please ensure that all the Blazorise-related packages are using v1.7.7 in your application. Otherwise, you might get errors due to incompatible versions.</p>
<blockquote>
<p>See <a href="https://github.com/abpframework/abp/pull/23013">#23013</a> for the updated NuGet packages.</p>
</blockquote>
<h3>Audit Logging Module: Excel Export</h3>
<p>In this version, we've added Excel export capabilities to the <a href="https://abp.io/docs/latest/modules/audit-logging-pro">Audit Logging Module</a>, allowing administrators to export audit logs and entity changes to Excel files for further analysis or reporting purposes.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/audit-logs-export-to-excel.png" alt="" /></p>
<p>This feature enables users to:</p>
<ul>
<li>Export audit logs with filtering options</li>
<li>Export entity changes with detailed information</li>
<li>Receive email notifications when exports are completed or fail</li>
<li>Download exported files via secure links</li>
</ul>
<p>The export process runs in the background, and once completed, users receive an email with a download link. This approach ensures that even large audit log exports don't block the UI or time out during processing.</p>
<p>You can configure various aspects of this feature using the <code>AuditLogExcelFileOptions</code> in your module's configuration:</p>
<pre><code class="language-csharp">Configure&lt;AuditLogExcelFileOptions&gt;(options =&gt;
{
    // How long to keep exported files before cleanup
    options.FileRetentionHours = 48;
    
    // Base URL for download links in notification emails
    options.DownloadBaseUrl = &quot;https://yourdomain.com&quot;;
    
    // Configure the cleanup worker schedule
    options.ExcelFileCleanupOptions.Period = (int)TimeSpan.FromHours(24).TotalMilliseconds;
    
    // Use cron expression for more advanced scheduling (requires Hangfire or Quartz)
    options.ExcelFileCleanupOptions.CronExpression = &quot;0 2 * * *&quot;; // Run at 2 AM daily
});
</code></pre>
<p>The module includes pre-configured email templates for notifications about completed or failed exports, ensuring users are always informed about the status of their export requests.</p>
<blockquote>
<p><strong>Note</strong>: This feature requires a configured BLOB storage provider to store the generated Excel files. See the <a href="https://abp.io/docs/9.3/framework/infrastructure/blob-storing">BLOB Storing documentation</a> for more information.</p>
</blockquote>
<p>For more details about the Audit Logging Module and its Excel export capabilities, please refer to the <a href="https://abp.io/docs/9.3/modules/audit-logging-pro">official documentation</a>.</p>
<h2>Community News</h2>
<h3>Announcing ABP Studio 1.0 General Availability 🚀</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/abp-studio.png" alt="" /></p>
<p>We are thrilled to announce that ABP Studio has reached version 1.0 and is now generally available! This marks a significant milestone for our integrated development environment designed specifically for ABP developers. The stable release brings several powerful features including:</p>
<ul>
<li>Enhanced Solution Runner with health monitoring capabilities</li>
<li>Theme style selection during project creation (Basic, LeptonX Lite, and LeptonX Themes)</li>
<li>New &quot;Container&quot; application type for better Docker container management</li>
<li>Improved handling of multiple DbContexts for migration operations</li>
</ul>
<blockquote>
<p>For a detailed overview of these features and to learn more about what's coming next, check out our <a href="https://abp.io/community/articles/announcing-abp-studio-1-0-general-availability-82yw62bt">announcement post</a>.</p>
</blockquote>
<h3>ABP Community Talks 2025.05: Empower Elsa Workflows with AI in .NET + ABP Framework</h3>
<p>In this episode of ABP Community Talks, 2025.05, we are thrilled to host <a href="https://github.com/sfmskywalker"><strong>Sipke Schoorstra</strong></a>, the creator of the <a href="https://docs.elsaworkflows.io/">Elsa Workflows</a> library! This month's session is all about <strong>&quot;Empower Elsa Workflows with AI in .NET + ABP Framework&quot;</strong>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/community-talk-2025-5.png" alt="" /></p>
<p>Sipke will join us to demonstrate how you can leverage AI within Elsa Workflows using .NET and the ABP Framework. The session will explore practical techniques and showcase how to integrate AI capabilities to enhance and automate your business processes within the Elsa workflow engine.</p>
<blockquote>
<p>👉 Don't miss this opportunity to learn directly from the creator of Elsa and see real-world examples of building intelligent, automated workflows! You can register from <a href="https://kommunity.com/volosoft/events/abp-community-talks-202505empower-elsa-workflows-with-ai-in-netabp-framework-3965dd32">here</a>.</p>
</blockquote>
<h3>ABP Bootcamp: Mastering Infrastructure &amp; Features</h3>
<p>We are excited to announce the very first <strong>ABP Bootcamp: Mastering Infrastructure &amp; Features</strong>! This is a live training program designed to give you hands-on, practical experience with ABP's core infrastructure and features.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-18%20v9_3_Preview/bootcamp.png" alt="ABP Bootcamp: Mastering Infrastructure &amp; Features" /></p>
<p>Join the ABP Bootcamp to learn directly from the core team in a focused, hands-on program designed for busy developers. Over four days, you'll gain a deep understanding of ABP's infrastructure, best practices, and practical skills you can immediately apply to your projects.</p>
<blockquote>
<p><strong>Seats are limited!</strong> Don't miss this opportunity to level up your ABP skills with direct guidance from the experts.</p>
<p>👉 <a href="https://abp.io/bootcamp">See full details and reserve your seat!</a></p>
</blockquote>
<h3>New ABP Community Articles</h3>
<p>There are exciting articles contributed by the ABP community as always. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/members/prabhjot">Prabhjot Singh</a> has published 3 new articles:
<ul>
<li><a href="https://abp.io/community/articles/consume-multi-backends-using-clients-6f4vcggh">Accessing Multiple Remote ABP based Backends Using HttpApi.Client</a></li>
<li><a href="https://abp.io/community/articles/adopting-the-new-.slnx-format-to-organize-applications-6cm3vl8k">Adopting the new .slnx format to organize applications and services</a></li>
<li><a href="https://abp.io/community/articles/replacing-dynamic-client-proxies-with-static-client-proxies-g30lf0vx">Replacing Dynamic client proxies with Static client proxies</a></li>
</ul>
</li>
<li><a href="https://github.com/maliming">Liming Ma</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/resolving-tenant-from-route-in-abp-framework-ah7oru97">Resolving Tenant from Route in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/integrating-.net-ai-chat-template-with-abp-framework-qavb5p2j">Integrating .NET AI Chat Template with ABP Framework</a></li>
</ul>
</li>
<li><a href="https://engincanveske.substack.com/">Engincan Veske</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/http-api-client-and-remote-services-in-abp-based-application-xkknsp6m">Understanding HttpApi.Client Project &amp; Remote Services in an ABP Based Application</a></li>
<li><a href="https://abp.io/community/articles/using-elsa-3-workflow-with-abp-framework-usqk8afg">Using Elsa 3 with the ABP Framework: A Comprehensive Guide</a></li>
</ul>
</li>
<li><a href="https://github.com/enisn">Enis Necipoğlu</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/white-labeling-in-abp-framework-5trwmrfm">White Labeling in ABP Framework</a> by <a href="https://github.com/enisn">Enis Necipoğlu</a></li>
<li><a href="https://abp.io/community/articles/you-do-it-wrong-customizing-abp-login-page-correctly-bna7wzt5">You do it wrong! Customizing ABP Login Page Correctly</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/abp-studio-docker-container-management-ex7r27y8">New in ABP Studio: Docker Container Management</a> by <a href="https://github.com/yekalkan">Yunus Emre Kalkan</a></li>
<li><a href="https://abp.io/community/articles/solving-mongodb-guid-issues-after-an-abp-framework-upgrade-tv8waw1n">Solving MongoDB GUID Issues After an ABP Framework Upgrade</a> by <a href="https://abp.io/community/members/burakdemir">Burak Demir</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP-related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/9.3/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.3 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a958e-295a-9957-9ff8-ee057afba500" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a958e-295a-9957-9ff8-ee057afba500" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/announcing-abp-studio-1.0-general-availability-82yw62bt</guid>
      <link>https://abp.io/community/posts/announcing-abp-studio-1.0-general-availability-82yw62bt</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp-studio</category>
      <category>release</category>
      <title>Announcing ABP Studio 1.0 General Availability</title>
      <description>It's the moment you've been waiting for! We are thrilled to announce the stable release of ABP Studio v1.0. This milestone marks a significant step forward in our mission to provide a first-class, integrated development environment for ABP developers. Paired with the recently released ABP v9.2, ABP Studio v1.0 brings new features and improvements that will make your development work faster and more efficient.</description>
      <pubDate>Fri, 13 Jun 2025 11:36:36 Z</pubDate>
      <a10:updated>2026-09-25T20:27:47Z</a10:updated>
      <content:encoded><![CDATA[<h1>Announcing ABP Studio 1.0 General Availability 🚀</h1>
<p>It's the moment you've been waiting for! We are thrilled to announce the stable release of ABP Studio v1.0. This milestone marks a significant step forward in our mission to provide a first-class, integrated development environment for ABP developers. Paired with the recently released <a href="https://abp.io/community/articles/announcing-abp-9-2-stable-release-061qmtzb">ABP v9.2</a>, ABP Studio v1.0 brings new features and improvements that will make your development work faster and more efficient.</p>
<p>For the past several months, our core ABP team has been hard at work, focusing on the features that matter most to you, our community of developers. This release is the peak of that effort, bringing a host of improvements and new capabilities to the forefront. Let's dive in and explore what's new in ABP Studio v1.0.</p>
<h2>What's New with ABP Studio v1.0?</h2>
<p>ABP Studio v1.0 is all about enhancing your development experience, from project creation to deployment. Here, we'll walk you through some of the latest features we've implemented, along with other key enhancements that make this release truly special.</p>
<h3>❤️ Solution Runner with Ready/Health Checks</h3>
<p>ABP Studio's Solution Runner now provides visual health monitoring that makes tracking your applications' status easily. When you start an application, a spinner indicates it's &quot;starting&quot;, then in the <em>Overall</em> tab, you can see the application's health (✅ for healthy, ⚠️ for unhealthy) that displays real-time health status:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/health-checks.png" alt="Health Checks" /></p>
<p>With <a href="https://abp.io/docs/9.2/solution-templates/layered-web-application/health-check-configuration">pre-configured health checks</a> in ABP solution templates including database connectivity tests, you get instant feedback on your applications' health.</p>
<p>When health check UI is configured, you can access comprehensive health dashboards with a dedicated &quot;Browse Health UI&quot; command or see the last health response from the &quot;Show Latest Health Check Response&quot; command:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/saas-health-check.png" alt="SaaS Health Check" /></p>
<p>When you restart applications that are open in your browser, ABP Studio automatically refreshes the pages for you.</p>
<h3>🎨 Theme Style Selection on Project Creation</h3>
<p>When creating a new solution, you can now choose your theme, theme style, and layout right from the project creation wizard instead of having to configure these settings later. ABP Studio lets you pick from <a href="https://abp.io/docs/latest/ui-themes">ABP's officially provided themes including Basic, LeptonX Lite, and LeptonX</a>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/theme-style-selection-leptonx.png" alt="Theme Style Selection LeptonX" /></p>
<p>If you select Basic or LeptonX Lite themes, only the theme will be changed. However, if you select the LeptonX theme, you'll get additional options to fine-tune your setup:</p>
<ul>
<li><strong>Theme Style Configuration</strong> - Pick from <strong>System, Light, Dim, or Dark</strong> styles to match how you like your development environment</li>
<li><strong>Layout Options</strong> - <strong>Sidebar menu</strong> / <strong>Top menu</strong></li>
</ul>
<h3>📦 &quot;Container&quot; Application Type for Solution Runner</h3>
<p>ABP Studio v1.0 introduces a dedicated &quot;Container&quot; application type that gives you better control over your Docker containers directly from the Solution Runner. Instead of managing all your containers through PowerShell scripts or running them all together, you can now see and control each container individually in the Solution Runner panel.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/containers-type.png" alt="Container Application Type" /></p>
<p>This new feature replaces the previous <em>Infrastructure</em> folder approach with a cleaner, more intuitive container section. You can now:</p>
<ul>
<li><strong>Start and stop containers individually</strong> - No more starting all containers at once when you only need specific services</li>
<li><strong>Monitor container status</strong> - See which containers are running, stopped, or have issues directly in the UI</li>
<li><strong>Manage container dependencies</strong> - Control the order and timing of container startup based on your application needs</li>
</ul>
<p>Whether you're working with databases, message brokers, or other containerized services, the new Container application type makes it much easier to manage your development environment. This is especially useful for microservice architectures where you might want to run only specific services during development or testing.</p>
<h3>⚙️ Handle Multiple DbContexts When Adding/Removing/Applying Migrations</h3>
<p>When working with ABP solutions that have multiple DbContexts (such as when using the separate tenant database option), ABP Studio now intelligently prompts you to select the appropriate DbContext for migration operations. This enhancement ensures you're always working with the correct database context and helps prevent common mistakes when managing multiple databases.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/new-migration-added.gif" alt="EF Core Migration Context Selection" /></p>
<p>The context selection dialog appears automatically when you perform any of these Entity Framework operations:</p>
<ul>
<li><strong>Adding a new migration</strong> - Choose which DbContext the new migration should target</li>
<li><strong>Removing an existing migration</strong> - Select the DbContext from which to remove the migration</li>
<li><strong>Updating the database</strong> - Specify which database context should be updated</li>
</ul>
<h2>Get Started with ABP Studio v1.0 Today!</h2>
<p>ABP Studio v1.0 is built on the solid foundation of the <a href="https://abp.io/community/articles/announcing-abp-9-2-stable-release-061qmtzb">latest version of ABP Framework, which is v9.2</a>. This means that when you create a new project with ABP Studio, you're getting all the latest features, performance improvements, and bug fixes that come with v9.2. This includes updates to dependencies, enhancements to the core framework, and improvements to application modules.</p>
<p>We are incredibly excited for you to get your hands on ABP Studio v1.0. We believe these new features will make a real difference in your day-to-day development workflow.</p>
<h3>⬇️ Download ABP Studio 1.0</h3>
<p>Ready to get started? You can download the stable v1.0 release right now from the official ABP Studio website: <strong><a href="https://abp.io/studio">https://abp.io/studio</a></strong></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-download.png" alt="ABP Studio 1.0 Download" /></p>
<p>If you are an existing ABP Studio user, it's even easier. You don't need to download the installer again. Simply launch ABP Studio, and it will prompt you to update to the latest version directly from the UI.</p>
<blockquote>
<p>Alternatively, you can click to the <em>Help -&gt; Check for Updates</em> context menu item to check for updates and install the latest version:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-10-Announcing-ABP-Studio-1.0-Stable-Release/abp-studio-check-for-updates.png" alt="ABP Studio 1.0 Check for Updates" /></p>
</blockquote>
<h3>🔮 What's Next?</h3>
<p>ABP Studio v1.0 represents just the beginning of our journey. We're committed to continuously evolving the platform, adding features that directly address real-world development challenges and enhance your workflow. Our goal is to make ABP Studio the go-to development environment for .NET and ABP Framework developers.</p>
<p>We will keep releasing new versions with exciting features based on our roadmap and your valuable feedback. To give you a sneak peek into what's planned for future releases, you can expect to see:</p>
<ul>
<li><strong>Environment Variable Management:</strong> A dedicated UI to easily manage environment variables for your solutions.</li>
<li><strong>OpenTelemetry Integration:</strong> We'll be integrating OpenTelemetry support directly into the startup templates, making distributed tracing and observability a seamless part of your application from day one.</li>
<li><strong>LeptonX Theme Builder</strong>: Allowing users to determine styling, colour palette and easily override their project's theme styles.</li>
<li><strong>Monitor dashboards of the tools used in the solution (e.g. Kubernetes, Redis, Grafana, etc...)</strong></li>
<li><strong>Pre-configured Aspire for the Microservice Startup Template</strong></li>
<li><strong>and more...</strong></li>
</ul>
<p>We are incredibly excited about the future of ABP Studio and can't wait to share the next set of features with you. Your comments and suggestions are invaluable to us. If you have any feedback, please drop a comment below.</p>
<p>Thank you for being part of our community and happy coding!</p>
<p><strong>The Volosoft Team</strong></p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a7b40-aa81-5910-8ee7-cfdd868b43e1" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a7b40-aa81-5910-8ee7-cfdd868b43e1" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-9.2-final-has-been-released-061qmtzb</guid>
      <link>https://abp.io/community/posts/abp.io-platform-9.2-final-has-been-released-061qmtzb</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP.IO Platform 9.2 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 9.2 stable version has been released today.</description>
      <pubDate>Wed, 11 Jun 2025 12:22:12 Z</pubDate>
      <a10:updated>2026-09-26T02:25:48Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 9.2 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 9.2 stable version has been released today.</p>
<h2>What's New With Version 9.2?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/articles/abp-platform-9.2-rc-has-been-released-jpq072nh">9.2 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 9.2</h2>
<h3>Creating New Solutions</h3>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a> to create new solutions.</p>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-06-02%20v9_2_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.x: <a href="https://abp.io/docs/9.2/release-info/migration-guides/abp-9-2">ABP Version 9.2 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://github.com/maliming">Liming Ma</a> has published 3 new articles:
<ul>
<li><a href="https://abp.io/community/articles/integrating-.net-ai-chat-template-with-abp-framework-qavb5p2j">Integrating .NET AI Chat Template with ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/resolving-tenant-from-route-in-abp-framework-ah7oru97">Resolving Tenant from Route in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/common-errors-in-jwt-bearer-authentication-4u3wrbs5">Common Errors in JWT Bearer Authentication</a></li>
</ul>
</li>
<li><a href="https://engincanveske.substack.com/">Engincan Veske</a> has published 3 new articles:
<ul>
<li><a href="https://abp.io/community/articles/http-api-client-and-remote-services-in-abp-based-application-xkknsp6m">Understanding HttpApi.Client Project &amp; Remote Services in an ABP Based Application</a></li>
<li><a href="https://abp.io/community/articles/using-elsa-3-workflow-with-abp-framework-usqk8afg">Using Elsa 3 with the ABP Framework: A Comprehensive Guide</a></li>
<li><a href="https://abp.io/community/articles/implementing-custom-tenant-logo-feature-in-abp-framework-a-stepbystep-guide-sba96ac9">Implementing Custom Tenant Logo Feature in ABP Framework: A Step-by-Step Guide</a></li>
</ul>
</li>
<li><a href="https://berkansasmaz.com/">Berkan Şaşmaz</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/understanding-the-domain-and-application-layers-in-abp-1fipc4x4">Understanding the Domain and Application Layers in ABP Framework</a></li>
<li><a href="https://abp.io/community/articles/how-do-we-maintain-code-quality-and-technical-debt-in-our-.net-codebase-z7glpya1">How Do We Maintain Code Quality and Technical Debt in Our .NET Codebase?</a></li>
</ul>
</li>
<li><a href="https://github.com/enisn">Enis Necipoğlu</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/white-labeling-in-abp-framework-5trwmrfm">White Labeling in ABP Framework</a> by <a href="https://github.com/enisn">Enis Necipoğlu</a></li>
<li><a href="https://abp.io/community/articles/you-do-it-wrong-customizing-abp-login-page-correctly-bna7wzt5">You do it wrong! Customizing ABP Login Page Correctly</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/members/arif">Ariful Islam</a> has published 2 new articles:
<ul>
<li><a href="https://abp.io/community/articles/multiworkspace-management-for-abp-applications-eghgty3j">Multi-Workspace Management for ABP Applications</a></li>
<li><a href="https://abp.io/community/articles/using-semantic-kernel-in-the-abp-framework-qo5cnuzs">Using Semantic Kernel in the ABP Framework</a></li>
</ul>
</li>
<li><a href="https://abp.io/community/articles/guide-to-add-custom-modules-in-abp.io-app-sttetffa">Guide to Add Custom Modules in ABP.IO App</a> by <a href="https://abp.io/community/members/harshgupta">Harsh Gupta</a></li>
<li><a href="https://abp.io/community/articles/debugging-nuget-packages-in-abp.io-a-complete-guide-h13y2033">Debugging NuGet Packages in ABP.IO: A Complete Guide</a> by <a href="https://suhaibmousa.com/">Suhaib Mousa</a></li>
<li><a href="https://abp.io/community/articles/using-microsoft-ai-extensions-library-and-openai-to-summarize-user-comments-gj1lusg7">Using Microsoft AI Extensions Library and OpenAI to Summarize User Comments</a> by <a href="https://twitter.com/hibrahimkalkan">Halil Ibrahim Kalkan</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>About the Next Version</h2>
<p>The next feature version will be 9.3. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a711d-b163-b1e3-7a44-087d7285563a" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a711d-b163-b1e3-7a44-087d7285563a" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/building-intelligent-blazor-apps-part-1-speechtotext-with-web-speech-api-iiy3vybu</guid>
      <link>https://abp.io/community/posts/building-intelligent-blazor-apps-part-1-speechtotext-with-web-speech-api-iiy3vybu</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>blazor</category>
      <title>Building Intelligent Blazor Apps: Part 1 - Speech-to-Text with Web Speech API</title>
      <description>In this two-part series, we'll explore how to build modern web applications that can listen to users and intelligently process their input.

**Series Overview**:

* **Part 1 (this article)**: Implementing speech-to-text functionality using the Web Speech API

* **Part 2 (coming next)**: Using .NET Smart Components to intelligently fill forms with AI</description>
      <pubDate>Wed, 04 Jun 2025 12:58:24 Z</pubDate>
      <a10:updated>2026-09-25T22:44:52Z</a10:updated>
      <content:encoded><![CDATA[<h1>Building Intelligent Blazor Apps: Part 1 - Speech-to-Text with Web Speech API</h1>
<p>In this two-part series, we'll explore how to build modern web applications that can listen to users and intelligently process their input.</p>
<p><strong>Series Overview:</strong></p>
<ul>
<li><strong>Part 1</strong> (this article): Implementing speech-to-text functionality using the Web Speech API</li>
<li><strong>Part 2</strong> (coming next): Using .NET Smart Components to intelligently fill forms with AI</li>
</ul>
<p>In this first part, we'll explore how to use speech recognition with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API">Web Speech API</a> to convert spoken words into text within a Blazor WebAssembly application.</p>
<blockquote>
<p>Web Speech API is a powerful browser-based interface that enables web applications to handle voice data, providing both speech recognition (converting spoken words to text) and speech synthesis (converting text to speech) capabilities. While the API is well-supported in modern browsers like Chrome, Edge, and Firefox, it's worth noting that Safari has limited support through the webkit prefix.</p>
</blockquote>
<p>Let's dive into implementing this functionality in our Blazor application.</p>
<h2>Building Our Speech Recognition Demo</h2>
<p>Let's walk through the step-by-step process of creating our voice-enabled Blazor application.</p>
<h3>Step 1: Create a New Blazor WASM (WebAssembly) Project</h3>
<p>Start by creating a new Blazor WASM application using the .NET CLI:</p>
<pre><code class="language-bash">dotnet new blazorwasm -n VoiceRecognitionDemo
</code></pre>
<p>This command creates a new Blazor WASM project with all the necessary scaffolding and dependencies.</p>
<h3>Step 2: Implementing the Web Speech API Integration</h3>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API">Web Speech API</a> provides powerful speech recognition capabilities directly in the browser. We'll create a JavaScript file to handle the speech recognition functionality and provide a clean interface for our Blazor components.</p>
<h4>2a. Speech Recognition Using Web Speech API</h4>
<p>First, create a new folder structure for our JavaScript files. In your project's <code>wwwroot</code> folder, create a <code>js</code> directory and add a new file called <code>speech-recognition.js</code>:</p>
<pre><code class="language-js">window.speechRecognizer = {
    recognition: null,
    startRecognition: function (dotNetObject) {
        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

        if (!SpeechRecognition) {
            alert(&quot;Speech recognition not supported in this browser.&quot;); //for Safari
            return;
        }

        const recognition = new SpeechRecognition();
        recognition.lang = 'en-US';
        recognition.interimResults = false;
        recognition.maxAlternatives = 1;

        recognition.onresult = function (event) {
            const text = event.results[0][0].transcript;
            dotNetObject.invokeMethodAsync('OnSpeechRecognized', text);
        };

        recognition.onerror = function (event) {
            console.error(&quot;Speech recognition error:&quot;, event.error);
        };

        recognition.start();
        window.speechRecognizer.recognition = recognition;
    },
    stopRecognition: function () {
        if (window.speechRecognizer.recognition) {
            window.speechRecognizer.recognition.stop();
        }
    },
    resetRecognition: function () {
        window.speechRecognizer.recognition = null;
    }
};
</code></pre>
<p>This JavaScript code provides several key features:</p>
<ul>
<li><strong>Browser compatibility detection</strong>: Handles both standard and webkit-prefixed versions of the Speech Recognition API</li>
<li><strong>Language configuration</strong>: Set to English (US) by default, but easily configurable</li>
<li><strong>Error handling</strong>: Gracefully handles browsers that don't support speech recognition (like Safari)</li>
<li><strong>Callback integration</strong>: Uses .NET's JavaScript interop to communicate results back to our Blazor component (<code>dotNetObject.invokeMethodAsync('OnSpeechRecognized', text);</code>)</li>
</ul>
<h4>2b. Adding the Script to the Project</h4>
<p>Next, we need to include our JavaScript file in the application. Add the following script reference to your <code>wwwroot/index.html</code> file, just before the closing <code>&lt;/body&gt;</code> tag:</p>
<pre><code class="language-html">&lt;script src=&quot;js/speech-recognition.js&quot;&gt;&lt;/script&gt;
</code></pre>
<h3>Step 3: Building the Blazor Component</h3>
<p>Open the <code>Home.razor</code> file and replace its content with the following comprehensive implementation:</p>
<pre><code class="language-razor">@page &quot;/&quot;
@inject IJSRuntime JS
@implements IDisposable

&lt;h3&gt;Speech to Text&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
    &lt;div class=&quot;col&quot;&gt;
        @if (!isListening)
        {
            &lt;button @onclick=&quot;StartSpeechRecognition&quot; class=&quot;btn btn-primary&quot;&gt;🎤 Start Speaking&lt;/button&gt;
        }
        else
        {
            &lt;button @onclick=&quot;StopSpeechRecognition&quot; class=&quot;btn btn-danger&quot;&gt;🚫 Stop Speaking&lt;/button&gt;
        }

        &lt;div class=&quot;row mt-3&quot;&gt;
            &lt;div class=&quot;col&quot;&gt;
                &lt;p&gt;&lt;strong&gt;Recognized Text:&lt;/strong&gt;&lt;/p&gt;
                &lt;textarea class=&quot;form-control&quot; rows=&quot;3&quot; readonly&gt;@recognizedText&lt;/textarea&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@code {
    private string recognizedText = &quot;&quot;;
    private bool isListening = false;

    private DotNetObjectReference&lt;Home&gt;? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    private async Task StartSpeechRecognition()
    {
        await JS.InvokeVoidAsync(&quot;speechRecognizer.startRecognition&quot;, objRef);
        isListening = true;
    }

    [JSInvokable]
    public Task OnSpeechRecognized(string text)
    {
        recognizedText = text;
        StateHasChanged();
        return Task.CompletedTask;
    }

    public async Task StopSpeechRecognition()
    {
        isListening = false;
        await JS.InvokeVoidAsync(&quot;speechRecognizer.stopRecognition&quot;);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}
</code></pre>
<p>Let's break down the key components of our Blazor implementation:</p>
<ul>
<li><p><strong>State management</strong>: We track the application state using two fields:</p>
<pre><code class="language-csharp">private string recognizedText = &quot;&quot;; // Stores the converted speech text
private bool isListening = false;   // Tracks if we're actively listening
</code></pre>
</li>
<li><p><strong>JavaScript interop</strong>: We use <code>IJSRuntime</code> to call our JavaScript functions:</p>
<pre><code class="language-csharp">private async Task StartSpeechRecognition()
{
    await JS.InvokeVoidAsync(&quot;speechRecognizer.startRecognition&quot;, objRef);
    isListening = true;
}
</code></pre>
</li>
<li><p><strong>Callback handling</strong>: The <code>[JSInvokable]</code> attribute enables JavaScript to call back into our code:</p>
<pre><code class="language-csharp">[JSInvokable]
public Task OnSpeechRecognized(string text)
{
    recognizedText = text;
    StateHasChanged();
    return Task.CompletedTask;
}
</code></pre>
</li>
<li><p><strong>Resource cleanup</strong>: We implement <code>IDisposable</code> to properly clean up the JavaScript reference:</p>
<pre><code class="language-csharp">private DotNetObjectReference&lt;Home&gt;? objRef;

public void Dispose()
{
    objRef?.Dispose();
}
</code></pre>
</li>
<li><p><strong>Reactive UI</strong>: The component automatically updates when speech is recognized:</p>
<pre><code class="language-csharp">// In OnSpeechRecognized:
recognizedText = text;      // Update the text
StateHasChanged();          // Trigger UI refresh
</code></pre>
</li>
</ul>
<h3>Step 4: Testing Your Application</h3>
<p>Once you've implemented all the components, run your application:</p>
<pre><code class="language-bash">dotnet run
</code></pre>
<p>When you navigate to the application and click the <strong>&quot;🎤 Start Speaking&quot;</strong> button, you'll see the interface change to show a <strong>&quot;🚫 Stop Speaking&quot;</strong> button, indicating that the application is actively listening for your voice input. As you speak, the recognized text will appear in the textarea below the buttons.</p>
<p><img src="https://raw.githubusercontent.com/EngincanV/EngincanV.github.io/main/_posts/Blazor-Voice-Recognition/demo.gif" alt="Speech Recognition Demo" /></p>
<p>The application provides a smooth user experience with clear visual feedback about the current state of speech recognition.</p>
<h2>What's Next?</h2>
<p>In Part 2 of this series, we'll enhance our speech recognition implementation with AI capabilities using <a href="https://devblogs.microsoft.com/dotnet/introducing-dotnet-smart-components/">.NET Smart Components</a>. We'll explore how to intelligently parse the recognized speech and automatically fill form fields, creating truly intelligent user interfaces that understand context and intent.</p>
<p>Stay tuned for the next part where we'll transform simple speech-to-text into intelligent form completion!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a4d32-526c-cd48-526b-0be33452459f" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a4d32-526c-cd48-526b-0be33452459f" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/understanding-httpapi.client-project-remote-services-in-an-abp-based-application-xkknsp6m</guid>
      <link>https://abp.io/community/posts/understanding-httpapi.client-project-remote-services-in-an-abp-based-application-xkknsp6m</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <title>Understanding HttpApi.Client Project &amp; Remote Services in an ABP Based Application</title>
      <description>When working with ABP Framework to build layered applications, developers often encounter the HttpApi.Client project within their solution structure. While this project plays a crucial role in the overall architecture, its purpose and implementation can initially seem complex or unclear. This comprehensive guide will demystify the HttpApi.Client project, explaining its fundamental purpose and demonstrating practical usage scenarios that will enhance your understanding of ABP's remote service capabilities.</description>
      <pubDate>Wed, 28 May 2025 09:39:09 Z</pubDate>
      <a10:updated>2026-04-24T12:29:01Z</a10:updated>
      <content:encoded><![CDATA[When working with ABP Framework to build layered applications, developers often encounter the HttpApi.Client project within their solution structure. While this project plays a crucial role in the overall architecture, its purpose and implementation can initially seem complex or unclear. This comprehensive guide will demystify the HttpApi.Client project, explaining its fundamental purpose and demonstrating practical usage scenarios that will enhance your understanding of ABP's remote service capabilities.<br \><a href="https://engincanveske.substack.com/p/understanding-httpapiclient-project" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a286f-6468-32c6-8fb8-30178a6ff3ab" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a286f-6468-32c6-8fb8-30178a6ff3ab" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/videos/ai-workflows-in-.net-elsa-3-abp-framework-integration-stepbystep-u8ownpse</guid>
      <link>https://abp.io/community/videos/ai-workflows-in-.net-elsa-3-abp-framework-integration-stepbystep-u8ownpse</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>elsa</category>
      <category>workflow</category>
      <title>AI Workflows in .NET: Elsa 3 + ABP Framework Integration (Step-by-Step)</title>
      <description>Unlock the power of AI workflows in your .NET applications! In this video, we walk through how to integrate Elsa 3, a powerful workflow engine, with the ABP Framework to create modular, maintainable, and scalable AI-powered solutions.</description>
      <pubDate>Fri, 23 May 2025 13:30:03 Z</pubDate>
      <a10:updated>2026-04-27T09:47:25Z</a10:updated>
      <content:encoded><![CDATA[Unlock the power of AI workflows in your .NET applications! In this video, we walk through how to integrate Elsa 3, a powerful workflow engine, with the ABP Framework to create modular, maintainable, and scalable AI-powered solutions. <br \> <a href="https://www.youtube.com/watch?v=XbHbQ1W21dA" rel="nofollow noopener noreferrer" title="Go to the Video">Go to the Video</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a0f82-fce4-8ef4-0f1d-5fa120da5cae" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a0f82-fce4-8ef4-0f1d-5fa120da5cae" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/using-elsa-3-with-the-abp-framework-a-comprehensive-guide-usqk8afg</guid>
      <link>https://abp.io/community/posts/using-elsa-3-with-the-abp-framework-a-comprehensive-guide-usqk8afg</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>elsa</category>
      <category>workflow</category>
      <title>Using Elsa 3 with the ABP Framework: A Comprehensive Guide</title>
      <description>In this article, we'll explore how to integrate Elsa 3, the powerful workflow engine, with the ABP Framework. This is a continuation of our previous post where we covered Elsa 2.x integration.

Here, we'll dive into the latest version and create an AI-powered workflow example.

&gt; 🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
&gt; 👉 [Subscribe here](https://engincanveske.substack.com)</description>
      <pubDate>Fri, 16 May 2025 10:11:22 Z</pubDate>
      <a10:updated>2026-04-27T10:38:46Z</a10:updated>
      <content:encoded><![CDATA[In this article, we'll explore how to integrate Elsa 3, the powerful workflow engine, with the ABP Framework. This is a continuation of our previous post where we covered Elsa 2.x integration.

Here, we'll dive into the latest version and create an AI-powered workflow example.

> 🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
> 👉 [Subscribe here](https://engincanveske.substack.com)<br \><a href="https://engincanveske.substack.com/p/using-elsa-3-with-the-abp-framework" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a19eac0-93a3-4a19-2e3e-be0c5e003ce2" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a19eac0-93a3-4a19-2e3e-be0c5e003ce2" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/implementing-custom-tenant-logo-feature-in-abp-framework-a-stepbystep-guide-sba96ac9</guid>
      <link>https://abp.io/community/posts/implementing-custom-tenant-logo-feature-in-abp-framework-a-stepbystep-guide-sba96ac9</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>multi-tenancy</category>
      <title>Implementing Custom Tenant Logo Feature in ABP Framework: A Step-by-Step Guide</title>
      <description>In multi-tenant applications built with ABP Framework, customizing the tenant's branding elements like logos is a common requirement. While ASP.NET Zero provides this feature out of the box, implementing it in a standard ABP application requires some custom development.

In this tutorial, I'll show you how to implement a custom tenant logo feature in an ABP application. We'll take a simple yet effective approach that you can later extend and customize according to your specific needs.</description>
      <pubDate>Tue, 29 Apr 2025 06:47:52 Z</pubDate>
      <a10:updated>2026-04-17T20:19:16Z</a10:updated>
      <content:encoded><![CDATA[In multi-tenant applications built with ABP Framework, customizing the tenant's branding elements like logos is a common requirement. While ASP.NET Zero provides this feature out of the box, implementing it in a standard ABP application requires some custom development.

In this tutorial, I'll show you how to implement a custom tenant logo feature in an ABP application. We'll take a simple yet effective approach that you can later extend and customize according to your specific needs.<br \><a href="https://engincanveske.substack.com/p/implementing-custom-tenant-logo-feature" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a19927a-26f3-3a08-735c-d96d52b1a5e2" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a19927a-26f3-3a08-735c-d96d52b1a5e2" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/videos/cqrs-in-abp-framework-without-mediatr-no-3rd-party-packages-needed-bzzjwh1c</guid>
      <link>https://abp.io/community/videos/cqrs-in-abp-framework-without-mediatr-no-3rd-party-packages-needed-bzzjwh1c</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>cqrs</category>
      <title>CQRS in ABP Framework Without MediatR – No 3rd Party Packages Needed</title>
      <description>In this video, we explore how to implement the CQRS (Command Query Responsibility Segregation) pattern in an ABP Framework application without using MediatR or any third-party libraries.

With MediatR going commercial, many developers are wondering whether they need to find an alternative package. But the good news is—you don’t need any extra dependencies.

The ABP Framework provides a built-in Local Event Bus System, which can act as a mediator, allowing you to publish and handle commands.</description>
      <pubDate>Tue, 15 Apr 2025 15:06:06 Z</pubDate>
      <a10:updated>2026-04-24T08:58:12Z</a10:updated>
      <content:encoded><![CDATA[In this video, we explore how to implement the CQRS (Command Query Responsibility Segregation) pattern in an ABP Framework application without using MediatR or any third-party libraries.

With MediatR going commercial, many developers are wondering whether they need to find an alternative package. But the good news is—you don’t need any extra dependencies.

The ABP Framework provides a built-in Local Event Bus System, which can act as a mediator, allowing you to publish and handle commands. <br \> <a href="https://www.youtube.com/watch?v=fqV6jn7MZac" rel="nofollow noopener noreferrer" title="Go to the Video">Go to the Video</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/images/others/blank-video-cover-image-344_196.png" />
      <media:content url="https://abp.io/images/others/blank-video-cover-image-344_196.png" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/videos/video-building-your-first-mcp-server-with-.net-a-developers-guide-to-model-context-protocol-ge7816fy</guid>
      <link>https://abp.io/community/videos/video-building-your-first-mcp-server-with-.net-a-developers-guide-to-model-context-protocol-ge7816fy</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>ai</category>
      <category>LLMs</category>
      <category>MCP</category>
      <title>Video: Building Your First MCP Server with .NET - A Developer's Guide to Model Context Protocol</title>
      <description>Model Context Protocol (MCP) offers a standard way to let LLMs interact with tools and APIs, making these integrations more structured and predictable.

In this practical guide, I'll explain:

✅ What MCP (Model Context Protocol) is and how it works?
✅ How to build your first MCP Server using .NET?
✅ How to integrate it with the Cursor code editor (- MCP Client -)?

&gt; ✍️ You can also check the related article from [here](https://engincanveske.substack.com/p/building-your-first-mcp-server-with)</description>
      <pubDate>Thu, 10 Apr 2025 15:12:52 Z</pubDate>
      <a10:updated>2026-04-20T08:27:30Z</a10:updated>
      <content:encoded><![CDATA[Model Context Protocol (MCP) offers a standard way to let LLMs interact with tools and APIs, making these integrations more structured and predictable.

In this practical guide, I'll explain:

✅ What MCP (Model Context Protocol) is and how it works?
✅ How to build your first MCP Server using .NET?
✅ How to integrate it with the Cursor code editor (- MCP Client -)?

> ✍️ You can also check the related article from [here](https://engincanveske.substack.com/p/building-your-first-mcp-server-with) <br \> <a href="https://youtu.be/ccAVySdFq58" rel="nofollow noopener noreferrer" title="Go to the Video">Go to the Video</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a19326f-aa2a-15d4-9b31-91248b0081d8" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a19326f-aa2a-15d4-9b31-91248b0081d8" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/building-your-first-mcp-server-with-.net-a-developers-guide-to-model-context-protocol-mfgqj2o4</guid>
      <link>https://abp.io/community/posts/building-your-first-mcp-server-with-.net-a-developers-guide-to-model-context-protocol-mfgqj2o4</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>LLMs</category>
      <category>MCP</category>
      <title>Building Your First MCP Server with .NET: A Developer's Guide to Model Context Protocol</title>
      <description>Model Context Protocol (MCP) offers a standard way to let LLMs interact with tools and APIs, making these integrations more structured and predictable.

In this practical guide, I'll explain:

✅ What MCP (Model Context Protocol) is and how it works
✅ How to build your first MCP Server using .NET
✅ How to integrate it with the Cursor code editor (- MCP Client -)

&gt; You can watch the video version of this article on [YouTube](https://engincanveske.substack.com/p/building-your-first-mcp-server-with)</description>
      <pubDate>Wed, 09 Apr 2025 10:57:45 Z</pubDate>
      <a10:updated>2026-04-27T12:49:23Z</a10:updated>
      <content:encoded><![CDATA[Model Context Protocol (MCP) offers a standard way to let LLMs interact with tools and APIs, making these integrations more structured and predictable.

In this practical guide, I'll explain:

✅ What MCP (Model Context Protocol) is and how it works
✅ How to build your first MCP Server using .NET
✅ How to integrate it with the Cursor code editor (- MCP Client -)

> You can watch the video version of this article on [YouTube](https://engincanveske.substack.com/p/building-your-first-mcp-server-with)<br \><a href="https://engincanveske.substack.com/p/building-your-first-mcp-server-with" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a192c5f-bfba-e92d-5b2a-d2780d8a3252" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a192c5f-bfba-e92d-5b2a-d2780d8a3252" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/automapper-mediatr-and-masstransit-are-going-commercial-whats-happening-3vrljuxr</guid>
      <link>https://abp.io/community/posts/automapper-mediatr-and-masstransit-are-going-commercial-whats-happening-3vrljuxr</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <title>AutoMapper, MediatR and MassTransit Are Going Commercial - What's Happening?</title>
      <description>In a surprising turn of events, three widely-used .NET libraries—AutoMapper, MediatR , and MassTransit —have announced transitions to commercial licensing models. These tools have been cornerstones in many .NET applications for years, making their sudden shift from open-source to paid licensing particularly noteworthy.

In this article, we'll delve into these announcements and explore the upcoming changes. Let's get started!</description>
      <pubDate>Thu, 03 Apr 2025 17:38:56 Z</pubDate>
      <a10:updated>2026-04-26T01:34:36Z</a10:updated>
      <content:encoded><![CDATA[In a surprising turn of events, three widely-used .NET libraries—AutoMapper, MediatR , and MassTransit —have announced transitions to commercial licensing models. These tools have been cornerstones in many .NET applications for years, making their sudden shift from open-source to paid licensing particularly noteworthy.

In this article, we'll delve into these announcements and explore the upcoming changes. Let's get started!<br \><a href="https://engincanveske.substack.com/p/automapper-mediatr-and-masstransit" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a190ee8-e050-e306-6771-56dae93a00b0" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a190ee8-e050-e306-6771-56dae93a00b0" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-9.2-rc-has-been-released-jpq072nh</guid>
      <link>https://abp.io/community/posts/abp-platform-9.2-rc-has-been-released-jpq072nh</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP Platform 9.2 RC Has Been Released</title>
      <description>We are happy to release ABP version 9.2 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.

Try this version and provide feedback for a more stable version of ABP v9.2! Thanks to you in advance.</description>
      <pubDate>Fri, 28 Mar 2025 10:44:03 Z</pubDate>
      <a10:updated>2026-09-25T21:41:44Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Platform 9.2 RC Has Been Released</h1>
<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>9.2 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v9.2! Thanks to you in advance.</p>
<h2>Get Started with the 9.2 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/studio-switch-to-preview.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.x or earlier: <a href="https://abp.io/docs/9.2/release-info/migration-guides/abp-9-2">ABP Version 9.2 Migration Guide</a></p>
<h2>What's New with ABP v9.2?</h2>
<p>In this section, I will introduce some major features released in this version.
Here is a brief list of titles explained in the next sections:</p>
<ul>
<li>Added <code>ApplicationName</code> Property to Isolate Background Jobs &amp; Background Workers</li>
<li>Docs Module: Added &quot;Alternative Words&quot; to Filter Items</li>
<li>Introducing the Bunny BLOB Storage Provider</li>
<li>Upgraded <code>MongoDB.Driver</code> to v3.1.0</li>
<li>Using Timezone Settings to Display Datetime</li>
<li>Identity Pro Module: Require Email Verification to Register</li>
<li>Switching users during OAuth login</li>
</ul>
<h3>Added ApplicationName Property to Isolate Background Jobs &amp; Background Workers</h3>
<p>ABP's <a href="https://abp.io/docs/latest/modules/background-jobs">Background Jobs Module</a> has been enhanced with a new <code>ApplicationName</code> property that helps isolate jobs and workers across multiple applications sharing the same database.</p>
<p>Previously, when different applications used the BackgroundJobs module and shared a database, an application might encounter jobs that didn't belong to it. This would lead to failed processing attempts and marking jobs as <code>IsAbandoned = true</code> with a &quot;Undefined background job for the job name&quot; error, preventing these jobs from ever being executed.</p>
<p>With the new <code>ApplicationName</code> property, applications now properly filter jobs at the repository level, ensuring each application only processes job types it recognizes. This prevents the incorrect abandonment of jobs and ensures consistent behavior in multi-application scenarios.</p>
<p>You can set <code>ApplicationName</code> of <code>AbpBackgroundJobWorkerOptions</code> to your application name to isolate jobs and workers across multiple applications sharing the same database:</p>
<pre><code class="language-csharp">public override void PreConfigureServices(ServiceConfigurationContext context)
{
    PreConfigure&lt;AbpBackgroundJobWorkerOptions&gt;(options =&gt;
    {
        options.ApplicationName = context.Services.GetApplicationName()!;
    });
}
</code></pre>
<blockquote>
<p>For more information, please refer to the <a href="https://abp.io/docs/latest/modules/background-jobs">Background Jobs Module</a> documentation and the <a href="https://github.com/abpframework/abp/pull/22169">PR</a> that added this feature.</p>
</blockquote>
<h3>Docs Module: Added &quot;Alternative Words&quot; to Filter Items</h3>
<p><a href="https://abp.io/docs/9.2/modules/docs">ABP's Docs Module</a> now supports &quot;alternative words&quot; to enhance the search functionality when filtering documentation items. This feature addresses a common user experience issue where users might search using terminology different from what appears in the documentation.</p>
<p>For example, when a user searches for &quot;Error&quot; in the documentation, they may actually be looking for content related to &quot;Exception Handling.&quot; With this new feature, documentation items can now be configured with alternative keywords that are considered during filtering.</p>
<p>The implementation allows defining optional &quot;keywords&quot; for items in the navigation tree. For example:</p>
<pre><code class="language-json">{
  &quot;text&quot;: &quot;Exception Handling&quot;,
  &quot;path&quot;: &quot;framework/fundamentals/exception-handling.md&quot;,
  &quot;keywords&quot;: [&quot;Error&quot;, &quot;Another Value&quot;]
}
</code></pre>
<p>When users search or filter content, the system now considers both the original text and these alternative keywords, improving discoverability of relevant documentation sections. This enhancement makes the documentation more accessible and user-friendly, especially for newcomers who might not be familiar with the exact terminology used in the ABP documentation.</p>
<h3>Introducing the Bunny BLOB Storage Provider</h3>
<p>ABP v9.2 RC introduces a new BLOB storage provider for <a href="https://bunny.net/storage/">Bunny Storage</a>, a global edge storage solution. This addition expands ABP's BLOB Storage options beyond the existing providers like Azure, AWS, and others.</p>
<p>The <a href="https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny">Bunny BLOB Storage Provider</a> allows ABP applications to seamlessly integrate with Bunny's CDN-backed storage service, which offers high-performance content delivery through its global network.</p>
<p>To use this new provider, you'll need to:</p>
<ul>
<li>Run <code>abp add-package Volo.Abp.BlobStoring.Bunny</code> command.</li>
<li>And then configure the provider in your module's <code>ConfigureServices</code> method:</li>
</ul>
<pre><code class="language-csharp">Configure&lt;AbpBlobStoringOptions&gt;(options =&gt;
{
    options.Containers.ConfigureDefault(container =&gt;
    {
        container.UseBunny(bunny =&gt;
        {
            bunny.StorageZoneName = &quot;your-storage-zone&quot;;
            bunny.ApiKey = &quot;your-api-key&quot;;
            bunny.Region = &quot;your-region&quot;; // de, ny, la, sg, or sy
        });
    });
});
</code></pre>
<p>This integration provides ABP applications with an efficient and globally distributed storage solution, particularly beneficial for applications requiring fast content delivery across different geographical regions. To use this new provider and make the related configurations, you can refer to the <a href="https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny">Bunny Storage Provider</a> documentation always.</p>
<blockquote>
<p>This new BLOB Storage provider is contributed by <a href="https://github.com/suhaib-mousa">@suhaib-mousa</a>. Thanks to him for his contribution!
We are always happy to see the community contributing to the ABP Framework and encouraging them to contribute more.</p>
</blockquote>
<h3>Upgraded <code>MongoDB.Driver</code> to <code>v3.1.0</code></h3>
<p>ABP v9.2 RC includes an upgrade to <code>MongoDB.Driver</code> version <code>3.1.0</code>. This significant version bump from previous releases brings several improvements and new features that benefit ABP applications using MongoDB as their database.</p>
<p>The upgrade provides:</p>
<ul>
<li>Async/Await Support: Write non-blocking, asynchronous code easily.</li>
<li>Fluent API: Build queries and updates intuitively with Builders.</li>
<li>LINQ Support: Use LINQ for querying MongoDB collections.</li>
<li>and more ...</li>
</ul>
<blockquote>
<p>For more information, please refer to the <a href="https://github.com/mongodb/mongo-csharp-driver/releases/tag/v3.1.0">MongoDB.Driver release notes</a>.</p>
</blockquote>
<p>We have prepared a <a href="https://abp.io/docs/9.2/release-info/migration-guides/MongoDB-Driver-2-to-3">migration guide</a> for this upgrade. Please refer to it to learn more about the changes and how to migrate your application.</p>
<h3>Using Timezone Settings to Display Datetime</h3>
<p>A significant enhancement in ABP v9.2 is the ability to use timezone settings to display <code>DateTime</code> values according to the user's or application's configured timezone. Introduced in <code>v9.2.0-rc.2</code>, this feature addresses the common challenge of ensuring users see accurate time information, regardless of their geographical location.</p>
<p>Previously, <code>DateTime</code> values were often shown in the server's timezone or in UTC, which could cause confusion for users in different timezones. With this new feature, ABP applications can now respect the configured timezone and automatically convert and display datetime values accordingly.</p>
<p><strong>Before setting the timezone:</strong></p>
<p>Consider a scenario where you have a list of books, and the <code>CreationTime</code> property is displayed without any timezone consideration. It may appear in the server's default timezone:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/before.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/before.png" />
<em>(Screenshot of a Books page showing the CreationTime in a default timezone.)</em></p>
<p><strong>Setting the timezone:</strong></p>
<p>To set the timezone, start by configuring the <code>AbpClockOptions</code> in your module's <code>ConfigureServices</code> method:</p>
<pre><code class="language-csharp">Configure&lt;AbpClockOptions&gt;(options =&gt;
{
    options.Kind = DateTimeKind.Utc;
});
</code></pre>
<blockquote>
<p>By setting the <code>Kind</code> property of <code>AbpClockOptions</code> to <code>DateTimeKind.Utc</code>, ABP will normalize all datetime values. Times stored in the database and returned to the frontend will be in UTC. Additionally, the <code>SupportsMultipleTimezone</code> property of the <code>IClock</code> service will be <strong>true</strong>, and you’ll be able to configure the timezone from the UI under the <em>Settings</em> page.</p>
</blockquote>
<p>After setting the <code>Kind</code> property, you can run your application and configure the timezone from the UI under the <em>Settings</em> page. For example, set the timezone to &quot;Asia/Tokyo (+09:00)&quot;:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/configure-timezone.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/configure-timezone.png" />
<em>(Screenshot showing the configuration setting for Abp.Timing.TimeZone, set to &quot;Asia/Tokyo (+09:00)&quot;.)</em></p>
<blockquote>
<p>ABP provides the <code>Abp.Timing.TimeZone</code> setting, which allows you to configure the desired timezone at the application, tenant, or user level and this setting can be configured in the UI under the <em>Settings</em> page, inside of the <em>Time Zone</em> tab.</p>
</blockquote>
<p><strong>After setting the timezone:</strong></p>
<p>Once the timezone is configured, ABP automatically handles the conversion and display of <code>DateTime</code> values. The <code>CreationTime</code> on the <em>Books</em> page will now be shown in the <strong>&quot;Asia/Tokyo&quot;</strong> timezone.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/after.png" alt="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/after.png" />
<em>(Screenshot of the <em>Books</em> page after setting the timezone, showing the <code>CreationTime</code> adjusted to the &quot;Asia/Tokyo&quot; timezone.)</em></p>
<p>This feature utilizes the <code>IClock</code> service, which provides methods like <code>ConvertToUserTime</code> and <code>ConvertToUtc</code> to facilitate timezone conversions. By configuring the <code>Abp.Timing.TimeZone</code> setting, developers can ensure a consistent and user-friendly experience across applications with a global user base.</p>
<blockquote>
<p>For a more detailed guide on implementing and using this feature, refer to the article: <a href="https://abp.io/community/articles/developing-a-multitimezone-application-using-the-abp-framework-zk7fnrdq">Developing a Multi-Timezone Application Using the ABP Framework</a>. It offers step-by-step instructions and examples for handling multi-timezone scenarios in ABP applications.</p>
</blockquote>
<h3>Identity Pro Module: Require Email Verification to Register</h3>
<p><a href="https://abp.io/docs/9.2/modules/identity-pro">ABP Identity Pro module</a> has been enhanced with a new feature that allows administrators to require email verification during the registration process. This security improvement ensures that users must verify their email addresses before their registration is considered complete. Enabling this feature is especially important for applications that want to prevent spam registrations.</p>
<p>Administrators can enable or disable this feature through the <strong>Identity management -&gt; Identity Verification (tab)</strong> settings page (by checking the <code>Enforce email verification to register</code> checkbox):</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/require-email-verification-for-register.png" alt="require-email-verification.png" /></p>
<h3>Switching users during OAuth login</h3>
<p>If you have an OAuth/Auth Server application using the <a href="https://abp.io/docs/9.2/modules/account-pro">ABP Account Pro module</a> , you can pass the <code>prompt=select_account</code> parameter to force the user to select an account.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-27%20v9_2_Preview/select-account.png" alt="select-account.png" /></p>
<p>For more information, please refer to the <a href="https://abp.io/docs/9.2/modules/account-pro#switching-users-during-oauth-login">Switching users during OAuth login</a> documentation.</p>
<h3>New ABP Community Articles</h3>
<p>There are exciting articles contributed by the ABP community as always. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/implementing-cqrs-with-mediatr-in-abp-xiqz2iio">Implementing CQRS with MediatR in ABP</a> by <a href="https://github.com/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv">Using Vue Components in a Razor Pages ABP Application</a> by <a href="https://github.com/enisn">Enis Necipoglu</a></li>
<li><a href="https://abp.io/community/articles/using-abps-aws-blob-storing-provider-with-digitalocean-spaces-7hlyb25g">Using ABP's AWS Blob Storing Provider with DigitalOcean Spaces</a> by <a href="https://abp.io/community/members/suhaib-mousa">Suhaib Mousa</a></li>
<li><a href="https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv">Video Post: Using Vue Components in a Razor Pages ABP Application</a> by <a href="https://github.com/enisn">Enis Necipoglu</a></li>
<li><a href="https://abp.io/community/articles/understanding-the-embedded-files-in-abp-framework-nsrp8aa9">Understanding the Embedded Files in ABP Framework</a> by <a href="https://github.com/maliming">Liming Ma</a></li>
<li><a href="https://abp.io/community/articles/how-to-change-the-currentuser-in-abp-i3uu1m7g">How to Change the CurrentUser in ABP?</a> by <a href="https://github.com/EngincanV">Engincan Veske</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP-related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/9.2/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.2 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a18ee86-e4c4-5511-ced5-ddc8178100c4" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a18ee86-e4c4-5511-ced5-ddc8178100c4" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/how-to-change-the-currentuser-in-abp-i3uu1m7g</guid>
      <link>https://abp.io/community/posts/how-to-change-the-currentuser-in-abp-i3uu1m7g</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <title>How to Change the CurrentUser in ABP?</title>
      <description>ABP Framework provides a powerful service for accessing information about the currently authenticated user in your application. Understanding how to use and modify this service (ICurrentUser) is essential for both basic and certain advanced scenarios.

In this article, we'll explore the CurrentUser service, its use cases, and how to change it when necessary.</description>
      <pubDate>Tue, 25 Mar 2025 06:23:55 Z</pubDate>
      <a10:updated>2026-09-26T00:15:01Z</a10:updated>
      <content:encoded><![CDATA[<h1>How to Change the CurrentUser in ABP?</h1>
<p><a href="https://abp.io/">ABP Framework</a> provides a powerful service for accessing information about the currently authenticated user in your application. Understanding how to use and modify this service (<code>ICurrentUser</code>) is essential for both basic and certain advanced scenarios.</p>
<p>In this article, we'll explore the <a href="https://abp.io/docs/latest/framework/infrastructure/current-user"><code>CurrentUser</code> service</a>, its use cases, and how to change it when necessary.</p>
<hr />
<blockquote>
<p>🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
👉 Subscribe here → engincanveske.substack.com
🎥 Also, check out my YouTube channel for hands-on demos and deep dives: https://www.youtube.com/@engincanv</p>
</blockquote>
<hr />
<h2>Understanding the ICurrentUser Service</h2>
<p>The <code>ICurrentUser</code> interface is the primary service in ABP Framework for obtaining information about the logged-in user. It provides some key properties, such as <code>Id</code>, <code>UserName</code>, <code>TenantId</code>, <code>Roles</code> (roleNames), and more...</p>
<p><code>ICurrentUser</code> is implemented on the <code>ICurrentPrincipalAccessor</code> service and works with claims as well. So, all of these properties are actually retrieved from the claims. ICurrentUser has some methods to directly work with the claims, such as:</p>
<ul>
<li>FindClaim (finds a single claim by name)</li>
<li>FindClaims (gets all claims with the given name)</li>
<li>IsInRole (checks if the user has a specific role)</li>
<li>GetAllClaims (gets all claims of the user)</li>
</ul>
<h2>Where the CurrentUser Service is Used?</h2>
<p>The CurrentUser service is used extensively throughout ABP applications whenever there's a need to access information about the logged-in user. Common scenarios include: authorization checks, logging, setting common properties like <code>CreatorId</code>, <code>LastModifierId</code>, <code>DeleterId</code>, and more...</p>
<h2>When to Change the CurrentUser Service?</h2>
<p>While the CurrentUser service works automatically in the context of HTTP requests (it gets the <code>User</code> property of the current <code>HttpContext</code>), there are advanced scenarios where you might need to manually set or change the current user:</p>
<ol>
<li><strong>Background workers:</strong> When executing code outside the context of a user request</li>
<li><strong>Event handlers:</strong> When processing events that may run in a different context</li>
<li><strong>Unit &amp; integration tests:</strong> When simulating a user for testing purposes</li>
</ol>
<h2>How to Change the CurrentUser Service?</h2>
<p>If you need to change the CurrentUser service, you can inject the <code>ICurrentPrincipalAccessor</code> service, use its <code>Change</code> method to change the current user, and then use the <code>CurrentUser</code> service as usual.</p>
<p>Here's how to change the current user for a specific scope:</p>
<pre><code class="language-csharp">using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;

namespace MyProject.Products;

public class ProductEventHandler : IDistributedEventHandler&lt;OrderPlacedEto&gt;, ITransientDependency
{
    private readonly IProductRepository _productRepository;
    private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
    private readonly IdentityUserManager _userManager;

    public ProductEventHandler(
        IProductRepository productRepository,
        ICurrentPrincipalAccessor currentPrincipalAccessor,
        IdentityUserManager userManager
    )
    {
        _productRepository = productRepository;
        _currentPrincipalAccessor = currentPrincipalAccessor;
        _userManager = userManager;
    }

    public async Task HandleEventAsync(OrderPlacedEto eventData)
    {
        var product = await _productRepository.FindAsync(eventData.ProductId);
        if (product == null)
        {
            return;
        }
        
        //Get the admin user
        var adminUser = await _userManager.FindByNameAsync(&quot;admin&quot;);
        if (adminUser == null)
        {
            return;
        }

        var newPrincipal = new ClaimsPrincipal(new ClaimsIdentity(
            new Claim[] { 
                new Claim(AbpClaimTypes.UserId, adminUser.Id.ToString()),
                new Claim(AbpClaimTypes.UserName, &quot;admin&quot;),
            }));
        
        //IMPORTANT: It will set the CreatorId, LastModifierId, etc. with the admin user
        using (_currentPrincipalAccessor.Change(newPrincipal))
        {
            product.StockCount -= eventData.Quantity;

            // Update the product
            await _productRepository.UpdateAsync(product);
        }
    }
}
</code></pre>
<p>In this example, we have a distributed event handler that processes an <code>OrderPlacedEto</code> event. When an order is placed, we need to update the product's stock count. However, we want this operation to be performed under an admin user's context for auditing purposes.</p>
<p>Here's what the code does step by step:</p>
<ol>
<li>First, it retrieves the product using the product ID from the event data.</li>
<li>Then, it finds the admin user by username using the <code>_userManager.FindByNameAsync(&quot;admin&quot;)</code>.</li>
<li>A new <code>ClaimsPrincipal</code> is created with the admin user's claims (<code>UserId</code> and <code>UserName</code>).</li>
<li>Using the <code>_currentPrincipalAccessor.Change()</code> method within a <code>using</code> statement, it temporarily changes the current user context to the admin user.</li>
<li>Inside this scope, it updates the product's stock count by subtracting the ordered quantity.</li>
<li>Finally, it saves the changes to the database using the repository.</li>
</ol>
<p><strong>The important part here is that any audit properties (like <code>CreatorId</code>, <code>LastModifierId</code>, etc.) will be set to the admin user's ID because we changed the current principal. Once the using block ends, the original user context is automatically restored.</strong></p>
<p>This pattern is particularly useful in background jobs, event handlers, or any scenario where you need to perform operations under a specific user's context, regardless of the actual authenticated user.</p>
<hr />
<blockquote>
<p>🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
👉 Subscribe here → engincanveske.substack.com
🎥 Also, check out my YouTube channel for hands-on demos and deep dives: https://www.youtube.com/@engincanv</p>
</blockquote>
<hr />
<h2>Conclusion</h2>
<p>The <code>CurrentUser</code> service in ABP Framework provides a simple way to access information about the authenticated user. While it works automatically in most scenarios, there are cases where you need to explicitly change the current user identity, particularly in background processing scenarios.</p>
<p>By using the ICurrentPrincipalAccessor.Change() method within a using statement, you can temporarily change the current user for a specific scope of execution, enabling your background processes, event handlers, or tests to execute with the identity of a specific user.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a18de25-a49e-6dae-03e1-de5d62ccc49c" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a18de25-a49e-6dae-03e1-de5d62ccc49c" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/implementing-cqrs-with-mediatr-in-abp-xiqz2iio</guid>
      <link>https://abp.io/community/posts/implementing-cqrs-with-mediatr-in-abp-xiqz2iio</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>cqrs</category>
      <category>mediatr</category>
      <title>Implementing CQRS with MediatR in ABP</title>
      <description>In this article, I will introduce the CQRS pattern, explore the MediatR library, and guide you through integrating MediatR within an ABP-based layered application. Through practical examples, I'll demonstrate how to implement CQRS using a single data source, with step-by-step explanations. In future articles, we can explore more advanced scenarios like separating read and write data sources and implementing database synchronization.</description>
      <pubDate>Tue, 18 Mar 2025 14:11:36 Z</pubDate>
      <a10:updated>2026-04-26T12:02:32Z</a10:updated>
      <content:encoded><![CDATA[In this article, I will introduce the CQRS pattern, explore the MediatR library, and guide you through integrating MediatR within an ABP-based layered application. Through practical examples, I'll demonstrate how to implement CQRS using a single data source, with step-by-step explanations. In future articles, we can explore more advanced scenarios like separating read and write data sources and implementing database synchronization.<br \><a href="https://engincanveske.substack.com/p/implementing-cqrs-with-mediatr-in" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a18bbc5-5106-d1ce-7959-cf6fb182f6d4" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a18bbc5-5106-d1ce-7959-cf6fb182f6d4" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-9.1-final-has-been-released-h96a56qa</guid>
      <link>https://abp.io/community/posts/abp.io-platform-9.1-final-has-been-released-h96a56qa</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP.IO Platform 9.1 Final Has Been Released!</title>
      <description>We are glad to announce that ABP 9.1 stable version has been released.</description>
      <pubDate>Mon, 10 Mar 2025 10:27:44 Z</pubDate>
      <a10:updated>2026-09-25T20:42:04Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP.IO Platform 9.1 Final Has Been Released!</h1>
<p>We are glad to announce that <a href="https://abp.io/">ABP</a> 9.1 stable version has been released today.</p>
<h2>What's New With Version 9.1?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/community/articles/abp-platform-9.1-rc-has-been-released-wws5l00k">9.1 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 9.1</h2>
<h3>Creating New Solutions</h3>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a> to create new solutions.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, it will be creating the solution with the latest stable version, which is v9.1 for now, so you don't need to specify the version.</p>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version to align it with ABP v9.1. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Upgrade ABP Packages</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-07%20v9_1_Release_Stable/upgrade-abp-packages.png" alt="" /></p>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool install -g Volo.Abp.Studio.Cli
</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">dotnet tool update -g Volo.Abp.Studio.Cli
</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">abp update
</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.0: <a href="https://abp.io/docs/latest/release-info/migration-guides/abp-9-1">ABP Version 9.1 Migration Guide</a></p>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>As always, exciting articles have been contributed by the ABP community. I will highlight some of them here:</p>
<ul>
<li><a href="https://abp.io/community/articles/urlbased-localization-3ivzinbb">URL-Based Localization</a> by <a href="https://twitter.com/alperebicoglu">Alper Ebiçoğlu</a></li>
<li><a href="https://abp.io/community/articles/building-a-crud-api-with-abp-framework-asp.net-core-and-postgresql-elrj0old">Building a CRUD API with ABP Framework, ASP.NET Core, and PostgreSQL</a> by <a href="https://github.com/berkansasmaz">Berkan Şaşmaz</a></li>
<li><a href="https://abp.io/community/articles/encryption-and-decryption-in-abp-framework-37uqhdwz">Encryption and Decryption in ABP Framework</a> by <a href="https://github.com/maliming">Liming Ma</a></li>
<li><a href="https://abp.io/community/articles/migrate-your-db-from-the-web-application-adding-a-db-migration-controller-in-abp-framework-x3u3uvk3">Migrate Your DB from the Web Application - Adding a DB Migration Controller</a> by <a href="https://twitter.com/alperebicoglu">Alper Ebiçoğlu</a></li>
<li><a href="https://abp.io/community/articles/containerization-blazor-wasm-jwt-web-api-docker-i3eirlsf">Containerization: Blazor WASM + JWT Web API =&gt; Docker</a> by <a href="https://abp.io/community/members/bartvanhoey">Bart Van Hoey</a></li>
<li><a href="https://abp.io/community/articles/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg">Configuring Post-Logout Redirect URI in ABP Based Blazor Applications with OpenIddict</a> by <a href="https://github.com/EngincanV">Engincan Veske</a></li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h3>ABP Community Talks 2025.2: Real World Problems and Solutions with AI</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Blog-Posts/2025-03-07%20v9_1_Release_Stable/community-talks.png" alt="" /></p>
<p>In this episode of ABP Community Talks (2025.2), Decision Tree joined us to explore how AI is being leveraged to solve real-world problems, showcasing a practical use case of AI applications.</p>
<blockquote>
<p>You can re-watch the talk from <a href="https://www.youtube.com/watch?v=CXpWjxCIY_E">here</a>.</p>
</blockquote>
<h2>About the Next Version</h2>
<p>The next feature version will be 9.2. You can follow the <a href="https://github.com/abpframework/abp/milestones">release planning here</a>. Please <a href="https://github.com/abpframework/abp/issues/new">submit an issue</a> if you have any problems with this version.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1891c5-7b32-dcea-5dd9-a27be65a09ed" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1891c5-7b32-dcea-5dd9-a27be65a09ed" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg</guid>
      <link>https://abp.io/community/posts/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>openiddict</category>
      <category>blazor</category>
      <category>abp</category>
      <title>Configuring Post-Logout Redirect URI in ABP Based Blazor Applications with OpenIddict</title>
      <description>OpenIddict module provides authentication features like single sign-on, single log-out, and API access control. While it comes pre-installed in ABP applications, the default post-logout redirect URI (/authentication/logout-callback) shows a generic logout page.

Let's modify this to redirect users to a more useful page after logout.</description>
      <pubDate>Tue, 11 Feb 2025 11:21:12 Z</pubDate>
      <a10:updated>2026-04-24T06:29:30Z</a10:updated>
      <content:encoded><![CDATA[OpenIddict module provides authentication features like single sign-on, single log-out, and API access control. While it comes pre-installed in ABP applications, the default post-logout redirect URI (/authentication/logout-callback) shows a generic logout page.

Let's modify this to redirect users to a more useful page after logout.<br \><a href="https://engincanveske.substack.com/configuring-post-logout-redirect" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1806ea-bae8-38d6-ea18-157c82234c6a" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1806ea-bae8-38d6-ea18-157c82234c6a" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/mysql-with-ef-core-9-in-abp-avoiding-translation-issues-zrlkcvjy</guid>
      <link>https://abp.io/community/posts/mysql-with-ef-core-9-in-abp-avoiding-translation-issues-zrlkcvjy</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>entity-framework-core</category>
      <category>abp</category>
      <category>mysql</category>
      <title>MySQL with EF Core 9 in ABP: Avoiding Translation Issues</title>
      <description>ABP Framework's MySQL provider (Volo.Abp.EntityFrameworkCore.MySQL) relies on Pomelo.EntityFrameworkCore.MySql NuGet package. However, as of now, the stable 9.0.0 version of this package has not been released yet. 

When using EF Core 9 with MySQL in ABP-based projects, you may encounter SQL translation issues. To workaround this issue, you must explicitly enable the TranslateParameterizedCollectionsToConstants() option in your EF Core configuration.</description>
      <pubDate>Tue, 04 Feb 2025 12:21:18 Z</pubDate>
      <a10:updated>2026-04-24T16:22:41Z</a10:updated>
      <content:encoded><![CDATA[ABP Framework's MySQL provider (Volo.Abp.EntityFrameworkCore.MySQL) relies on Pomelo.EntityFrameworkCore.MySql NuGet package. However, as of now, the stable 9.0.0 version of this package has not been released yet. 

When using EF Core 9 with MySQL in ABP-based projects, you may encounter SQL translation issues. To workaround this issue, you must explicitly enable the TranslateParameterizedCollectionsToConstants() option in your EF Core configuration.<br \><a href="https://dev.to/engincanv/mysql-with-ef-core-9-in-abp-avoiding-translation-issues-1il1" rel="nofollow noopener noreferrer" title="Go to the Post">Go to the Post</a>]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a17e315-3aa2-6ac3-75e7-7f698f412d99" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a17e315-3aa2-6ac3-75e7-7f698f412d99" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/customizing-authentication-flow-with-openiddict-events-in-abp-framework-e59qfi9n</guid>
      <link>https://abp.io/community/posts/customizing-authentication-flow-with-openiddict-events-in-abp-framework-e59qfi9n</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>authorization</category>
      <category>authentication</category>
      <category>abp</category>
      <category>openiddict-module</category>
      <title>Customizing Authentication Flow with OpenIddict Events in ABP Framework</title>
      <description>OpenIddict provides an event-driven model (event models) that allows developers to customize authentication and authorization processes. This event model enables handling actions such as user sign-in, sign-out, token validation, and request handling dynamically.

In this article, we will explore OpenIddict event models, their key use cases, and how to implement them effectively.</description>
      <pubDate>Tue, 04 Feb 2025 08:52:42 Z</pubDate>
      <a10:updated>2026-09-26T00:10:26Z</a10:updated>
      <content:encoded><![CDATA[<h1>Customizing Authentication Flow with OpenIddict Events in ABP Framework</h1>
<p><a href="https://abp.io/docs/latest/modules/openiddict">ABP's OpenIddict Module</a> provides an integration with the <a href="https://github.com/openiddict/openiddict-core">OpenIddict</a> library,  which provides advanced authentication features like <strong>single sign-on</strong>, <strong>single log-out</strong>, and <strong>API access control</strong>.</p>
<p>OpenIddict provides an event-driven model (<a href="https://documentation.openiddict.com/introduction#events-model">event models</a>) that allows developers to customize authentication and authorization processes. This event model enables handling actions such as user <strong>sign-in</strong>, <strong>sign-out</strong>, <strong>token validation</strong>, and <strong>request handling</strong> dynamically.</p>
<p>In this article, we will explore OpenIddict event models, their key use cases, and how to implement them effectively.</p>
<hr />
<blockquote>
<p>🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
👉 Subscribe here → engincanveske.substack.com
🎥 Also, check out my YouTube channel for hands-on demos and deep dives: https://www.youtube.com/@engincanv</p>
</blockquote>
<hr />
<h2>Understanding OpenIddict Event Model</h2>
<p>OpenIddict events are primarily used within the OpenIddict server component. These events provide hooks into the OpenID Connect flow, allowing developers to modify behavior at different stages of authentication &amp; authorization processes.</p>
<p>They are triggered during critical moments such as:</p>
<ul>
<li>User authentication (sign-in)</li>
<li>Session termination (sign-out)</li>
<li>Token validation and generation</li>
<li>Request processing</li>
<li>Error handling</li>
</ul>
<p>OpenIddict provides multiple server events, under the <code>OpenIddictServerEvents</code> static class to make them easier to find (also provides additonal validation events under the <code>OpenIddictValidationEvents</code> static class).</p>
<p>Here are some of the pre-defined <code>OpenIddictServerEvents</code>:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-02-04-OpenIddict-Custom-Logic/openiddict-server-events.png" alt="" /></p>
<p>Each event represents a specific checkpoint in the <strong>request processing pipeline</strong>, such as validating an OpenID Connect request, extracting request parameters, processing the request, or generating a response. As an application developer, you simply need to create event handlers that subscribe to these predefined events to implement your custom logic at the desired pipeline stage.</p>
<h2>Example: How to add custom logic when a user signs out?</h2>
<p>Let's walkthrough a practical example of implementing custom sign-out logic using OpenIddict events.</p>
<h3>Step 1: Create a Custom Event Handler</h3>
<p>First, create a handler that implements <code>IOpenIddictServerHandler&lt;OpenIddictServerEvents.ProcessSignOutContext&gt;</code>:</p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using OpenIddict.Server;

namespace MySolution;

public class SignOutEventHandler : IOpenIddictServerHandler&lt;OpenIddictServerEvents.ProcessSignOutContext&gt;
{
    public static OpenIddictServerHandlerDescriptor Descriptor { get; }
        = OpenIddictServerHandlerDescriptor.CreateBuilder&lt;OpenIddictServerEvents.ProcessSignOutContext&gt;()
            .UseSingletonHandler&lt;SignOutEventHandler&gt;()
            .SetOrder(100_000)
            .SetType(OpenIddictServerHandlerType.Custom)
            .Build();
    
    public ValueTask HandleAsync(OpenIddictServerEvents.ProcessSignOutContext context)
    {
        // Implement your custom sign-out logic here

        // Examples:
        // - Clear custom session data
        // - Perform audit logging
        // - Notify other services
        // - Clean up user-specific resources
        
        return ValueTask.CompletedTask;
    }
}
</code></pre>
<p>The handler configuration includes several important components:</p>
<ul>
<li><code>Descriptor</code> - Defines how the handler should be registered and executed</li>
<li><code>SetOrder</code> - Determines the execution order when multiple handlers exist</li>
<li><code>SetType</code> - Specifies this as a custom handler implementation</li>
<li><code>UseSingletonHandler</code> - Sets lifetime of the class as <em>Singleton</em></li>
</ul>
<h3>Step 2: Register the Event Handler</h3>
<p>Register your custom handler in your application's module configuration:</p>
<pre><code class="language-csharp">//...

public class MySolutionAuthServerModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure&lt;OpenIddictServerBuilder&gt;(serverBuilder =&gt;
        {
            serverBuilder.AddEventHandler(SignOutEventHandler.Descriptor);
        });
    }

    //...
}
</code></pre>
<p>That's it! After these steps, your <code>SignOutEventHandler.HandleAsync()</code> method should be triggered after each signout request. You can also use other pre-defined server events for other stages of the authentication &amp; authorization processes such as;</p>
<ul>
<li><code>OpenIddictServerEvents.ProcessSignInContext</code> -&gt; after each sign-in,</li>
<li><code>OpenIddictServerEvents.ProcessErrorContext</code> -&gt; when an error occurs in the authentication,</li>
<li><code>OpenIddictServerEvents.ProcessChallengeContext</code> -&gt; called when processing a challenge operation,</li>
<li>and other 40+ server events...</li>
</ul>
<p>Each event provides access to the relevant context, allowing you to access and modify the authentication flow's behavior.</p>
<hr />
<blockquote>
<p>🛠 Liked this post? I now share all my content on Substack — real-world .NET, AI, and scalable software design.
👉 Subscribe here → engincanveske.substack.com
🎥 Also, check out my YouTube channel for hands-on demos and deep dives: https://www.youtube.com/@engincanv</p>
</blockquote>
<hr />
<h2>Conclusion</h2>
<p>ABP Framework integrates OpenIddict as its authentication and authorization module. OpenIddict provides an event-driven model that allows developers to customize authentication and authorization processes within their ABP applications. It's pre-installed &amp; pre-configured in the ABP's startup templates.</p>
<p>OpenIddict provides a powerful and flexible way to customize authentication flows. By leveraging these events, developers can implement complex authentication scenarios while maintaining clean, maintainable code.</p>
<h2>References</h2>
<ul>
<li><a href="https://documentation.openiddict.com/introduction#events-model">OpenIddict Documentation</a></li>
<li><a href="https://abp.io/docs/latest/modules/openiddict">ABP OpenIddict Module Documentation</a></li>
<li><a href="https://kevinchalet.com/2018/07/02/implementing-advanced-scenarios-using-the-new-openiddict-rc3-events-model/">Advanced OpenIddict Scenarios</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a17e256-4105-2a44-a3a4-779e1a938238" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a17e256-4105-2a44-a3a4-779e1a938238" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-studio-now-supports-macos-intel--0x6kmwry</guid>
      <link>https://abp.io/community/posts/abp-studio-now-supports-macos-intel--0x6kmwry</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>abp-studio</category>
      <title>ABP Studio Now Supports MacOS Intel 🚀</title>
      <description>We are excited to announce that ABP Studio, our cross-platform desktop application for ABP developers, now supports Intel-based Mac computers!</description>
      <pubDate>Wed, 22 Jan 2025 13:58:52 Z</pubDate>
      <a10:updated>2026-09-26T02:37:05Z</a10:updated>
      <content:encoded><![CDATA[<p>We are excited to announce that <a href="https://abp.io/studio">ABP Studio, our cross-platform desktop application for ABP developers</a>, now supports Intel-based Mac computers!</p>
<p>This addition expands our platform compatibility, ensuring that developers using Intel-powered Macs can also benefit from the powerful features of ABP Studio.</p>
<h2>What is ABP Studio?</h2>
<p>For those who aren't familiar with it, <a href="https://abp.io/studio">ABP Studio</a> is a powerful desktop application that makes ABP development faster and easier. It offers:</p>
<ul>
<li><p>Easy creation of new solutions (from simple applications to microservices)</p>
</li>
<li><p>Visual architecture management for modular-monolith and microservice solutions</p>
</li>
<li><p>Solution exploration tools for entities, services, and HTTP APIs</p>
</li>
<li><p>Simplified running, debugging, and monitoring of multi-application or microservice solutions</p>
</li>
<li><p>Kubernetes cluster integration capabilities</p>
</li>
<li><p>and more...</p>
</li>
</ul>
<h2>Extended Platform Support</h2>
<p>ABP Studio has been proudly supporting multiple platforms, and we're excited to add MacOS Intel to our list of supported architectures. You can now use ABP Studio on:</p>
<ul>
<li><p>Windows x64</p>
</li>
<li><p>Windows ARM</p>
</li>
<li><p>MacOS Apple Silicon (M1/M2/M3)</p>
</li>
<li><p>MacOS Intel <strong>(New!)</strong></p>
</li>
</ul>
<h2>Why This Matters</h2>
<p>This update is particularly important for developers who are using Intel-based Mac computers. Previously, ABP Studio was only available for Apple Silicon Macs (for MacOS), but we understand that many developers are still using Intel-based Macs. With this release, we're ensuring that all Mac users can access our development tools, regardless of their processor architecture.</p>
<h2>Getting Started</h2>
<p>Installing ABP Studio on your Intel-based Mac is straightforward:</p>
<ol>
<li><p>Go to <a href="https://abp.io/studio">abp.io/studio</a></p>
</li>
<li><p>Click on the download button and select &quot;MacOS Intel&quot; from the dropdown menu</p>
</li>
<li><p>Once downloaded, open the installer package</p>
</li>
<li><p>Follow the installation wizard to complete the setup</p>
</li>
</ol>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-01-22-abp-studio-now-supports-macos-intel-/3a17a07b91b89169ba833586d80024db.png" alt="abp-studio-macos-intel.png" /></p>
<h2>Conclusion</h2>
<p>As the ABP team, we're always looking for ways to improve the developer experience. By supporting Intel-based Macs, we're ensuring that all Mac users can access our development tools, regardless of their processor architecture.</p>
<p>Stay tuned for more updates and enhancements as we continue to optimize ABP Studio and please provide us with your invaluable feedback. Thanks in advance!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a17a07b-e4b8-5a60-53bc-0b75bc7d1f9d" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a17a07b-e4b8-5a60-53bc-0b75bc7d1f9d" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-platform-9.1-rc-has-been-released-wws5l00k</guid>
      <link>https://abp.io/community/posts/abp-platform-9.1-rc-has-been-released-wws5l00k</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP Platform 9.1 RC Has Been Released</title>
      <description>We are happy to release ABP version 9.1 RC (Release Candidate). This blog post introduces the new features and important changes in this new version.</description>
      <pubDate>Tue, 21 Jan 2025 11:48:44 Z</pubDate>
      <a10:updated>2026-09-25T20:42:03Z</a10:updated>
      <content:encoded><![CDATA[<p>We are happy to release <a href="https://abp.io">ABP</a> version <strong>9.1 RC</strong> (Release Candidate). This blog post introduces the new features and important changes in this new version.</p>
<p>Try this version and provide feedback for a more stable version of ABP v9.1! Thanks to you in advance.</p>
<h2>Get Started with the 9.1 RC</h2>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a>.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/3a179addd003079681f0b02a4e00c96e.png" alt="studio-switch-to-preview.png" /></p>
<h2>Migration Guide</h2>
<ul>
<li><a href="https://abp.io/docs/9.1/release-info/migration-guides/abp-9-1">ABP Version 9.1 Migration Guide</a></li>
</ul>
<h2>What's New with ABP v9.1?</h2>
<p>In this section, I will introduce some major features released in this version.</p>
<p>Here is a brief list of titles explained in the next sections:</p>
<ul>
<li><p>Upgraded to Angular 19</p>
</li>
<li><p>Upgraded to OpenIddict 6.0</p>
</li>
<li><p>New Blazor WASM Bundling System</p>
</li>
<li><p>Idle Session Warning</p>
</li>
<li><p>Lazy Expandable Feature for Documentation</p>
</li>
</ul>
<h3>Upgraded to Angular 19</h3>
<p>We've upgraded the Angular templates and packages to <strong>Angular 19</strong>. This upgrade brings the latest features and improvements from the Angular ecosystem to ABP-based applications, including better performance and development experience.</p>
<h3>Upgraded to OpenIddict 6.0</h3>
<p>OpenIddict 6.0 has been released and we've upgraded the OpenIddict packages to version 6.0 in ABP 9.1. This brings enhanced security features and improved authentication capabilities. The migration is straightforward and mainly involves updating some constant names:</p>
<ul>
<li><p><code>OpenIddictConstants.Permissions.Endpoints.Logout</code> is now <code>OpenIddictConstants.Permissions.Endpoints.EndSession</code></p>
</li>
<li><p><code>OpenIddictConstants.Permissions.Endpoints.Device</code> is now <code>OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization</code></p>
</li>
</ul>
<p>If you're using IdentityModel packages directly, you'll need to upgrade them to the latest stable version (8.3.0). This update ensures your applications stay current with the latest security standards and best practices.</p>
<blockquote>
<p>Please refer to the <a href="https://abp.io/docs/9.1/release-info/migration-guides/openiddict5-to-6">OpenIddict 6.0 migration guide</a> for more information.</p>
</blockquote>
<h3>New Blazor WASM Bundling System</h3>
<p>We've implemented a new bundling system for Blazor WebAssembly applications that eliminates the need to manually run the <code>abp bundle</code> command. This system automatically handles JavaScript and CSS file bundling at runtime, significantly improving both development experience and application loading performance.</p>
<p><strong>Key improvements include:</strong></p>
<ul>
<li><p>Automatic bundling of JavaScript and CSS files without manual intervention</p>
</li>
<li><p>Dynamic file generation through the host application</p>
</li>
<li><p>Better integration with the ABP module system</p>
</li>
<li><p>Improved asset management through the virtual file system</p>
</li>
</ul>
<p>The new system is particularly beneficial for modular applications, as it allows modules to contribute their assets automatically to the global bundles. This results in a more maintainable and efficient asset management system for Blazor WebAssembly applications.</p>
<blockquote>
<p>Please refer to <a href="https://abp.io/docs/9.1/framework/ui/blazor/global-scripts-styles">this documentation</a> for more information.</p>
</blockquote>
<h3>Idle Session Warning</h3>
<p>We've introduced a new idle session warning feature for the <a href="https://abp.io/docs/latest/modules/account-pro">Account (Pro) Module</a> that helps manage user sessions more effectively. This security enhancement automatically monitors user activity and manages session timeouts in a user-friendly way.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/3a179ade018718fbdf909f1d5f224dfb.png" alt="idle-session-settings.png" /></p>
<p>The feature can be easily configured through the administration interface, where administrators can:</p>
<ul>
<li><p>Enable/disable the idle session timeout</p>
</li>
<li><p>Set custom timeout duration in minutes</p>
</li>
<li><p>Configure when users should be signed out</p>
</li>
</ul>
<p>When a user becomes inactive for the configured duration, they'll receive a warning dialog:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/3a179ade1481e5e85d35cd60aeeddb1b.png" alt="session-expiration-warning.png" /></p>
<p><strong>Key features and behaviors:</strong></p>
<ul>
<li><p>Tracks real user activity (mouse movements, keyboard presses) across all tabs</p>
</li>
<li><p>Works on a per-browser session basis - affects all tabs of the same session</p>
</li>
<li><p>Maintains session if user is active in any tab of the application</p>
</li>
<li><p>Provides a countdown timer before automatic sign-out</p>
</li>
<li><p>Offers options to &quot;Stay signed in&quot; or &quot;Sign out now&quot;</p>
</li>
</ul>
<p>This feature significantly improves application security while maintaining a smooth user experience by preventing unexpected session expirations and data loss.</p>
<h3>Lazy Expandable Feature for Documentation</h3>
<p>We've introduced a new lazy expandable feature to the documentation system that significantly improves navigation through large documentation sections. This enhancement addresses common challenges when dealing with extensive documentation hierarchies by introducing smart menu management.</p>
<p><strong>Key benefits and features:</strong></p>
<ul>
<li><p><strong>Cleaner Navigation:</strong> The menu stays concise by hiding sub-items until they're needed, reducing visual clutter</p>
</li>
<li><p><strong>Better Performance:</strong> Reduces the initial load of the navigation tree by loading sub-items on demand</p>
</li>
<li><p><strong>Improved Search Experience:</strong> Makes filtering documentation items more efficient by showing only relevant top-level items</p>
</li>
<li><p><strong>Context-Aware Expansion:</strong> Automatically expands relevant sections when viewing specific documentation pages</p>
</li>
</ul>
<p>The feature works by marking certain documentation sections as &quot;lazy expandable&quot; in the navigation configuration. When users navigate to a document within a lazy expandable section, the system automatically expands the relevant menu items while keeping other sections collapsed.</p>
<p>This improvement is particularly valuable for complex documentation areas like tutorials, solution templates, and extensive module documentation, where having all navigation items visible at once could be overwhelming.</p>
<p>An example of lazy expandable feature from the <a href="https://abp.io/docs/latest/tutorials/book-store/part-01">ABP's BookStore Tutorial</a>:</p>
<pre><code class="language-json">
        {

          &quot;text&quot;: &quot;Book Store Application&quot;,

          &quot;isLazyExpandable&quot;: true,

          &quot;path&quot;: &quot;tutorials/book-store&quot;,

          &quot;items&quot;: [

            {

              &quot;text&quot;: &quot;Overview&quot;,

              &quot;path&quot;: &quot;tutorials/book-store&quot;,

              &quot;isIndex&quot;: true

            },

            //other items...

          ]

        }

</code></pre>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/3a179ade326fb5d8185ce5dcc0d4dac0.png" alt="lazy-expandable.png" /></p>
<h3>Others</h3>
<p>Some other highlights from this release:</p>
<ul>
<li><p>Updated Iyzico NuGet packages to the latest version, which is used in the <a href="https://abp.io/docs/latest/modules/payment#payment-module-pro">ABP's Payment Module</a>.</p>
</li>
<li><p>Removed optional <em>secondaryIds</em> from path. See: <a href="https://github.com/abpframework/abp/pull/21307">#21307</a></p>
</li>
<li><p><a href="https://abp.io/docs/latest/modules/cms-kit-pro">CMS Kit Pro</a>: Added automatic deletion of comments when a blog post is deleted - comments are now automatically removed when their associated blog post is deleted.</p>
</li>
<li><p>Avoiding global blocking in distributed event handlers (See <a href="https://github.com/abpframework/abp/pull/21716">#21716</a>).</p>
</li>
</ul>
<h2>Community News</h2>
<h3>New ABP Community Articles</h3>
<p>There are exciting articles contributed by the ABP community as always. I will highlight some of them here:</p>
<ul>
<li><p><a href="https://abp.io/community/articles/integrating-abp-modules-in-your-asp.net-core-web-api-project.-a-stepbystep-guide-jtbyosnr">Integrating ABP Modules in Your ASP.NET Core Web API Project. A Step-by-Step Guide</a> by <a href="https://abp.io/community/members/connect">Sajankumar Vijayan</a></p>
</li>
<li><p><a href="https://abp.io/community/articles/abp-framework-background-jobs-vs-background-workers-when-to-use-which-t98pzjv6">ABP Framework: Background Jobs vs Background Workers</a> — When to Use Which? by <a href="https://twitter.com/alperebicoglu">Alper Ebiçoğlu</a></p>
</li>
<li><p><a href="https://abp.io/community/articles/the-new-unit-test-structure-in-abp-application-4vvvp2oy">The new Unit Test structure in ABP application</a> by <a href="https://github.com/maliming">Liming Ma</a></p>
</li>
<li><p><a href="https://abp.io/community/articles/how-to-use-openai-api-with-abp-framework-rsfvihla">How to Use OpenAI API with ABP Framework</a> by <a href="https://github.com/berkansasmaz">Berkan Şaşmaz</a></p>
</li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP-related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://abp.io/docs/9.1/release-info/road-map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.1 RC and provide feedback to help us release a more stable version.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a179ade-63a7-e882-3ea3-e976346fc11b" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a179ade-63a7-e882-3ea3-e976346fc11b" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-studio-goes-aot-faster-startups-with-readytorun-r2r-publishing-i851t8xt</guid>
      <link>https://abp.io/community/posts/abp-studio-goes-aot-faster-startups-with-readytorun-r2r-publishing-i851t8xt</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>abp-studio</category>
      <title>ABP Studio Goes AOT: Faster Startups with Ready-to-Run (R2R) Publishing</title>
      <description>We're excited that ABP Studio now supports Ready-to-Run (R2R) publishing (starting from v0.9.16+), a hybrid form of ahead-of-time (AOT) compilation. This enhancement significantly improves the startup time and overall performance of ABP Studio, making it faster and more performant than ever before.</description>
      <pubDate>Mon, 16 Dec 2024 15:00:05 Z</pubDate>
      <a10:updated>2026-09-26T00:04:34Z</a10:updated>
      <content:encoded><![CDATA[<p>We're excited that <a href="https://abp.io/studio">ABP Studio</a> now supports <a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run">Ready-to-Run (R2R) publishing</a> (starting from v0.9.16+), a hybrid form of ahead-of-time (AOT) compilation. This enhancement significantly improves the startup time and overall performance of ABP Studio, making it faster and more performant than ever before.</p>
<p>Let's dive into what R2R publishing is, how it works, and the benefits it brings to ABP Studio.</p>
<h2>What is Ready-to-Run (R2R) Publishing?</h2>
<p>Ready-to-Run (R2R) is a form of AOT compilation available in the .NET ecosystem. Unlike traditional just-in-time (JIT) compilation, R2R precompiles parts of your application to native code before deployment. This precompiled code helps reduce the startup time by minimizing the work needed during runtime.</p>
<p>However, R2R isn't a complete AOT compilation. Instead, it's a hybrid approach because it stores both:</p>
<ul>
<li><p><strong>Native code for precompiled methods</strong> (to improve startup time and performance)</p>
</li>
<li><p><strong>Intermediate Language (IL) code</strong> for methods that may need further JIT compilation</p>
</li>
</ul>
<p>This hybrid nature is why R2R binaries are typically larger. For ABP Studio, the storage size increased by ~150 MB with R2R enabled, but the trade-off is well worth it for the performance and startup-time gains.</p>
<h2>How R2R (Ready-to-Run) Improves ABP Studio</h2>
<h3>Faster Startup Time 🚀</h3>
<p>One of the biggest advantages of R2R publishing is its impact on startup times. In our local tests, enabling R2R resulted in startup times being <strong>reduced by 2.5x</strong> ⬇️.</p>
<p>This means you can get to work faster, without waiting for the application to being startup from the beginning. Whether you're launching ABP Studio to manage projects, generate code, or deploy applications, the improved responsiveness is noticeable.</p>
<h3>Performance Enhancements 📈</h3>
<p>In addition to faster startups, R2R publishing contributes to overall performance improvements. By precompiling frequently used methods, R2R reduces the workload on the JIT compiler during execution, leading to smoother and more efficient operations.</p>
<h3>Trade-offs: Increased Storage Size 🆙</h3>
<p>With great performance comes a slight trade-off: storage size. R2R binaries include both <strong>native</strong> and <strong>IL code</strong>, which increases the file size. In the case of ABP Studio, the storage footprint increased by ~150 MB. However, the substantial improvements in speed and responsiveness make this a worthwhile investment.</p>
<h2>How to Enable R2R Publishing in Your Applications?</h2>
<p>If you're developing applications and want to benefit from R2R, here's a quick guide on how to enable it in your .NET projects:</p>
<ol>
<li>You can add the following configuration to your final project's <code>.csproj</code> file:</li>
</ol>
<pre><code class="language-xml">
&lt;PropertyGroup&gt;

    &lt;PublishReadyToRun&gt;true&lt;/PublishReadyToRun&gt;

&lt;/PropertyGroup&gt;

</code></pre>
<ol start="2">
<li>Then, publish your application with the <code>dotnet publish</code> command:</li>
</ol>
<pre><code class="language-bash">
dotnet publish -c Release

</code></pre>
<p>Alternatively, you can specify the <em>PublishReadyToRun</em> flag directly to the <code>dotnet publish</code> command as follows:</p>
<pre><code class="language-bash">
dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true

</code></pre>
<p>That's it! Your application will now include precompiled native code for faster startup and great performance benefits.</p>
<blockquote>
<p>Please refer to the <a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run">official documentation</a> before publishing your application with R2R.</p>
</blockquote>
<h2>Conclusion</h2>
<p>As ABP team, we're always looking for ways to improve the developer experience. By adopting <strong>Ready-to-Run (R2R) publishing</strong> for ABP Studio, we're aiming to deliver a faster and more efficient tool for your development needs.</p>
<p>Stay tuned for more updates and enhancements as we continue to optimize ABP Studio and please provide us with your invaluable feedback.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a16e228-a1bd-29d9-f41e-bdd2e8021743" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a16e228-a1bd-29d9-f41e-bdd2e8021743" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp.io-platform-9.0-has-been-released-based-on-.net-9.0-aqeuzs2m</guid>
      <link>https://abp.io/community/posts/abp.io-platform-9.0-has-been-released-based-on-.net-9.0-aqeuzs2m</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>abp</category>
      <category>release</category>
      <title>ABP.IO Platform 9.0 Has Been Released Based on .NET 9.0</title>
      <description>Today, ABP 9.0 stable version has been released based on .NET 9.0. You can create solutions with ABP 9.0 starting from ABP Studio v0.9.11 or by using the ABP CLI as explained in the following sections.</description>
      <pubDate>Thu, 21 Nov 2024 16:07:30 Z</pubDate>
      <a10:updated>2026-09-25T20:27:35Z</a10:updated>
      <content:encoded><![CDATA[<p>Today, <a href="https://abp.io/">ABP</a> 9.0 stable version has been released based on <a href="https://dotnet.microsoft.com/en-us/download/dotnet/9.0">.NET 9.0</a>. You can create solutions with ABP 9.0 starting from ABP Studio v0.9.11 or using the ABP CLI as explained in the following sections.</p>
<h2>What's New With Version 9.0?</h2>
<p>All the new features were explained in detail in the <a href="https://abp.io/blog/announcing-abp-9-0-release-candidate">9.0 RC Announcement Post</a>, so there is no need to review them again. You can check it out for more details.</p>
<h2>Getting Started with 9.0</h2>
<h3>Creating New Solutions</h3>
<p>You can check the <a href="https://abp.io/get-started">Get Started page</a> to see how to get started with ABP. You can either download <a href="https://abp.io/get-started#abp-studio-tab">ABP Studio</a> (<strong>recommended</strong>, if you prefer a user-friendly GUI application - desktop application) or use the <a href="https://abp.io/docs/latest/cli">ABP CLI</a> to create new solutions.</p>
<p>By default, ABP Studio uses stable versions to create solutions. Therefore, it will be creating the solution with the latest stable version, which is v9.0 for now, so you don't need to specify the version. <strong>You can create solutions with ABP 9.0 starting from v0.9.11.</strong></p>
<h3>How to Upgrade an Existing Solution</h3>
<p>You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained:</p>
<h3>Upgrading via ABP Studio</h3>
<p>If you are already using the ABP Studio, you can upgrade it to the latest version to align it with ABP v9.0. ABP Studio periodically checks for updates in the background, and when a new version of ABP Studio is available, you will be notified through a modal. Then, you can update it by confirming the opened modal. See <a href="https://abp.io/docs/latest/studio/installation#upgrading">the documentation</a> for more info.</p>
<p>After upgrading the ABP Studio, then you can open your solution in the application, and simply click the <strong>Switch to stable</strong> action button to instantly upgrade your solution:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/3a16616d10340c7e12d1e7b47aff921f.png" alt="switch-to-stable.png" /></p>
<blockquote>
<p>Please note that ABP CLI &amp; ABP Studio only upgrade the related ABP packages, so you need to upgrade the other packages for .NET 9.0 manually.</p>
</blockquote>
<h3>Upgrading via ABP CLI</h3>
<p>Alternatively, you can upgrade your existing solution via ABP CLI. First, you need to install the ABP CLI or upgrade it to the latest version.</p>
<p>If you haven't installed it yet, you can run the following command:</p>
<pre><code class="language-bash">
dotnet tool install -g Volo.Abp.Studio.Cli

</code></pre>
<p>Or to update the existing CLI, you can run the following command:</p>
<pre><code class="language-bash">
dotnet tool update -g Volo.Abp.Studio.Cli

</code></pre>
<p>After installing/updating the ABP CLI, you can use the <a href="https://abp.io/docs/latest/CLI#update"><code>update</code> command</a> to update all the ABP related NuGet and NPM packages in your solution as follows:</p>
<pre><code class="language-bash">
abp update

</code></pre>
<p>You can run this command in the root folder of your solution to update all ABP related packages.</p>
<blockquote>
<p>Please note that ABP CLI &amp; ABP Studio only upgrade the related ABP packages, so you need to upgrade the other packages for .NET 9.0 manually.</p>
</blockquote>
<h2>Migration Guides</h2>
<p>There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v8.x: <a href="https://abp.io/docs/9.0/release-info/migration-guides/abp-9-0">ABP Version 9.0 Migration Guide</a></p>
<h2>Community News</h2>
<h3>ABP Community Talks 2024.7: What’s New with .NET 9 &amp; ABP 9?</h3>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/3a16616d38e49ac84cbb0b8628adccf6.png" alt="community-talks.png" /></p>
<p>In this episode of ABP Community Talks, 2024.7; we will dive into the features that came with .NET 9.0 with <a href="https://github.com/ebicoglu">Alper Ebicoglu</a>, <a href="https://github.com/EngincanV">Engincan Veske</a>, <a href="https://github.com/berkansasmaz">Berkan Sasmaz</a> and <a href="https://github.com/ahmetfarukulu">Ahmet Faruk Ulu</a>.</p>
<h3>Highlights from .NET 9.0</h3>
<p>Our team has closely followed the ASP.NET Core and Entity Framework Core 9.0 releases, read Microsoft's guides and documentation, and adapted the changes to our ABP.IO Platform. We are proud to say that we've shipped the ABP 9.0 based on .NET 9.0 just after Microsoft's .NET 9.0 release.</p>
<p>In addition to the ABP's .NET 9.0 upgrade, our team has created many great articles to highlight the important features coming with ASP.NET Core 9.0 and Entity Framework Core 9.0.</p>
<blockquote>
<p>You can read <a href="https://volosoft.com/blog/Highlights-for-ASP-NET-Entity-Framework-Core-NET-9-0">this post</a> to see the list of all articles.</p>
</blockquote>
<h3>New ABP Community Articles</h3>
<p>In addition to <a href="https://volosoft.com/blog/Highlights-for-ASP-NET-Entity-Framework-Core-NET-9-0">the articles to highlight .NET 9.0 features written by our team</a>, here are some of the recent posts added to the <a href="https://abp.io/community">ABP Community</a>:</p>
<ul>
<li><p><a href="https://abp.io/community/videos/building-modular-monolith-applications-with-asp.net-core-abp-studio-66znukvf">Video: Building Modular Monolith Applications with ASP.NET Core &amp; ABP Studio</a> by <a href="https://x.com/hibrahimkalkan">Halil İbrahim Kalkan</a></p>
</li>
<li><p><a href="https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c">How to create your Own AI Bot on WhatsApp Using an ABP.io Template</a> by <a href="https://abp.io/community/members/Michal_Kokula">Michael Kokula</a></p>
</li>
<li><p><a href="https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f">ABP Now Supports .NET 9</a> by <a href="https://x.com/alperebicoglu">Alper Ebiçoğlu</a></p>
</li>
</ul>
<p>Thanks to the ABP Community for all the content they have published. You can also <a href="https://abp.io/community/posts/create">post your ABP related (text or video) content</a> to the ABP Community.</p>
<h2>Conclusion</h2>
<p>This version comes with some new features and a lot of enhancements to the existing features. You can see the <a href="https://docs.abp.io/en/abp/9.0/Road-Map">Road Map</a> documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.0 and provide feedback to help us release more stable versions.</p>
<p>Thanks for being a part of this community!</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1661a7-6132-61c3-7f49-ebf706e60d15" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1661a7-6132-61c3-7f49-ebf706e60d15" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/highlights-for-asp.net-core-entity-framework-core-features-shipped-with-.net-9.0-wzvsvwlp</guid>
      <link>https://abp.io/community/posts/highlights-for-asp.net-core-entity-framework-core-features-shipped-with-.net-9.0-wzvsvwlp</link>
      <a10:author>
        <a10:name>EngincanV</a10:name>
        <a10:uri>https://abp.io/community/members/EngincanV</a10:uri>
      </a10:author>
      <category>entity-framework</category>
      <category>aspnetcore</category>
      <title>Highlights for ASP.NET Core &amp; Entity Framework Core features shipped with .NET 9.0</title>
      <description>As Volosoft, we are passionate about the technology and tools we work on. Since our open-source and commercial developer platforms are based on Microsoft's .NET technology, we are closely following the .NET team for new releases, features, and improvements made in each release.  In November 2024, exciting things happening in the .NET world and in this blog post, we are covering some of them.</description>
      <pubDate>Thu, 14 Nov 2024 12:18:09 Z</pubDate>
      <content:encoded><![CDATA[<p>As Volosoft, we are passionate about the technology and tools we work on. Since our open-source and commercial developer platforms are based on Microsoft's .NET technology, we are closely following the .NET team for new releases, features, and improvements made in each release.</p>
<p>In November 2024, exciting things happening in the .NET world and in this blog post, we are covering some of them.</p>
<h2>.NET Conf 2024</h2>
<p>Microsoft organized the .NET Conf 2024 between November 12 and 14 as an online event. There were many speakers who talked at the conference from all around the world.</p>
<p>The co-founder of <a href="https://volosoft.com/">Volosoft</a> and Lead Developer of the <a href="https://abp.io/">ABP</a>, <a href="https://x.com/hibrahimkalkan">Halil Ibrahim Kalkan</a> gave a speech about &quot;Building Modular Applications with ASP.NET Core &amp; ABP&quot;.</p>
<p><img src="/api/posts/migrated-images/088704ff7bbb60d242123a163cc84471.jpg" alt="dotnet-conf-2024.jpg" /></p>
<p>In his session, he discussed building modular applications and mentioned how <a href="https://abp.io/studio">ABP Studio</a> makes it easier to create fully modular systems with ASP.NET Core.</p>
<h2>What's new with ASP.NET &amp; Entity Framework Core 9.0</h2>
<p>Microsoft released the .NET 9.0 in the .NET Conf 2024, with ASP.NET Core 9.0 and Entity Framework Core 9.0.</p>
<p>Our team has closely followed the ASP.NET Core and Entity Framework Core 9.0 releases, read Microsoft's guides, documentation, and adapted the changes to our ABP.IO Platform. We are proud to say that we shipped the ABP 9.0 RC.1 based on .NET 9.0 just after Microsoft's .NET 9.0 pre-release and we are going to share the v9.0 with .NET 9.0 stable release soon.</p>
<p>In addition to the ABP's .NET 9.0 upgrade, the team has created many great articles to highlight the important features coming with ASP.NET Core 9.0 and Entity Framework Core 9.0. Here, is a list of all the articles:</p>
<h3>ASP.NET Core 9.0</h3>
<ul>
<li><a href="https://abp.io/community/articles/optimizing-static-asset-delivery-feature-in-asp.net-core-9.0-gyv140vb">Optimizing Static Asset Delivery feature in ASP.NET Core 9.0</a> by <a href="https://abp.io/community/members/maliming">Liming Ma</a></li>
<li><a href="https://abp.io/community/articles/c-13-features-1aq5pzuy">C# 13 Features</a> by <a href="https://abp.io/community/members/enisn">Enis Necipoğlu</a></li>
<li><a href="https://abp.io/community/articles/hybrid-cache-in-.net-9-5s0l2pa6">Hybrid Cache in .NET 9</a> by <a href="https://abp.io/community/members/EngincanV">Engincan Veske</a></li>
<li><a href="https://abp.io/community/articles/.net-aspire-9.0-features-q7nojisw">.NET Aspire 9.0 Features</a> by <a href="https://abp.io/community/members/ismcagdas">İsmail Çağdaş</a></li>
<li><a href="https://abp.io/community/articles/.net-9.0-signalr-supports-trimming-and-native-aot-4oxx0qbs">.NET 9.0 SignalR supports trimming and Native AOT</a> by <a href="https://abp.io/community/members/ahmetfarukulu">Ahmet Faruk Ulu</a></li>
<li><a href="https://abp.io/community/articles/builtin-openapi-document-generation-with-.net-9-no-more-swaggerui--au56cck5">Built-in OpenAPI Document Generation with .NET 9 — No more SwaggerUI! 👋</a> by <a href="https://abp.io/community/members/alper">Alper Ebiçoğlu</a></li>
<li><a href="https://abp.io/community/articles/middleware-now-supports-keyed-dependency-injection-in-.net-9-4whni6rx">Middleware Now Supports Keyed Dependency Injection in .NET 9</a> by <a href="https://abp.io/community/members/salih">Salih Özkara</a></li>
</ul>
<h3>Entity Framework 9.0</h3>
<ul>
<li><a href="https://abp.io/community/articles/ef-core-9-linq-sql-translation-b7pzcj09">EF Core 9 LINQ &amp; SQL translation</a> by <a href="https://community.abp.io/members/liangshiwei">liangshiwei</a></li>
<li><a href="https://abp.io/community/articles/ef-core-9-readonly-primitive-collections-iy6ztbx8">EF Core 9 Read-only Primitive Collections</a> by <a href="https://abp.io/community/members/berkansasmaz">Berkan Şaşmaz</a></li>
</ul>
<p>We enjoyed writing these, we hope you also enjoy and like them while reading. Happy coding!</p>
<p>-- The Volosoft Team</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/7edee84e-145c-eb91-d718-3a163cc8e0e0" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/7edee84e-145c-eb91-d718-3a163cc8e0e0" medium="image" />
    </item>
  </channel>
</rss>