@ -0,0 +1,86 @@ |
|||||
|
# EF Core 9 Read-only Primitive Collections |
||||
|
|
||||
|
In this article, we will explore the new features introduced in EF Core 9, specifically focusing on Read-only Primitive Collections. EF Core 8 introduced support for mapping arrays and mutable lists of primitive types, and you can read more about it [here](https://abp.io/community/articles/ef-core-8-primitive-collections-ttn5b6xp). This has been expanded in EF Core 9 to include read-only collections/lists. Specifically, EF Core 9 supports collections typed as `IReadOnlyList`, `IReadOnlyCollection`, or `ReadOnlyCollection`. |
||||
|
|
||||
|
## Introduction to EF Core 9 Read-only Primitive Collections |
||||
|
|
||||
|
Entity Framework Core 9 introduces several enhancements, one of which is the support for Read-only Primitive Collections. This feature aims to provide better support for scenarios where collections of primitive types, such as `int`, `string`, or `bool`, need to be used in a read-only manner in your entity classes. Previously, developers had to use complex workarounds to ensure collections couldn't be modified, but EF Core 9 now provides a simpler, built-in solution to handle this more effectively. |
||||
|
|
||||
|
### Why Read-only Primitive Collections Matter |
||||
|
|
||||
|
Read-only Primitive Collections are particularly useful when you need to guarantee the integrity of certain data within your entities. For example, imagine you have a `Car` entity that has a collection of `Colors`, represented as a set of enums. You might not want these colors to be modified after they're initially set, ensuring that any business logic reliant on these values remains consistent. |
||||
|
|
||||
|
EF Core 9 introduces a convenient way to define these collections as read-only, helping developers maintain stricter control over their data. |
||||
|
|
||||
|
### How It Works |
||||
|
|
||||
|
Defining a read-only primitive collection is quite straightforward in EF Core 9. You can use the `IReadOnlyList<T>`, `IReadOnlyCollection<T>`, or `ReadOnlyCollection<T>` types to declare your properties, ensuring a consistent read-only behavior. This helps maintain data integrity by preventing modifications after the collection is set. Below is an example that includes a `Car` class and a `Color` enum. The `Car` class has a `Colors` property that holds a read-only list of available colors, ensuring that these values cannot be modified after being initially set: |
||||
|
|
||||
|
```csharp |
||||
|
public enum Color |
||||
|
{ |
||||
|
Black, |
||||
|
White, |
||||
|
Red, |
||||
|
Blue |
||||
|
} |
||||
|
|
||||
|
public class Car |
||||
|
{ |
||||
|
public int Id { get; set; } |
||||
|
public string Brand { get; set; } |
||||
|
public string Model { get; set; } |
||||
|
public IReadOnlyList<Color> Colors { get; private set; } = new List<Color> { Color.Black, Color.White }.AsReadOnly(); |
||||
|
|
||||
|
protected Car() |
||||
|
{ |
||||
|
/* This constructor is for deserialization / ORM purpose */ |
||||
|
} |
||||
|
|
||||
|
public Car(string brand, string model, IEnumerable<Color> colors) |
||||
|
{ |
||||
|
Brand = brand; |
||||
|
Model = model; |
||||
|
Colors = colors.ToList().AsReadOnly(); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
In the example above, `Colors` is defined as a read-only list, preventing any accidental modifications once it is set. This ensures that data integrity is maintained without the need for manual validation. |
||||
|
|
||||
|
To query cars with specific colors, you can use the following example: |
||||
|
|
||||
|
```csharp |
||||
|
var colors = new List<Color> { Color.Black, Color.White }; |
||||
|
var cars = await context.Cars |
||||
|
.Where(c => c.Colors.Intersect(colors).Any()) |
||||
|
.ToListAsync(); |
||||
|
``` |
||||
|
|
||||
|
The query selects all cars that have any of the specified colors in their `Colors` collection. |
||||
|
|
||||
|
The SQL result looks like this; as you can see, it sends colors as parameters instead of adding them inline. It also uses the `json_each` function to deserialize on the database side: |
||||
|
|
||||
|
```sql |
||||
|
SELECT "c"."id", |
||||
|
"c"."brand", |
||||
|
"c"."colors", |
||||
|
"c"."model" |
||||
|
FROM "cars" AS "c" |
||||
|
WHERE EXISTS (SELECT 1 |
||||
|
FROM (SELECT "c0"."value" |
||||
|
FROM Json_each("c"."colors") AS "c0" |
||||
|
INTERSECT |
||||
|
SELECT "c1"."value" |
||||
|
FROM Json_each(@__colors_0) AS "c1") AS "i") |
||||
|
``` |
||||
|
|
||||
|
### Conclusion |
||||
|
|
||||
|
Read-only primitive collections make it easier to enforce data integrity by preventing changes to your collection data. This feature helps simplify your code while ensuring that critical parts of your data remain consistent. |
||||
|
|
||||
|
## References |
||||
|
|
||||
|
- https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/whatsnew#read-only-primitive-collections |
||||
|
- https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collections |
||||
|
- https://abp.io/community/articles/ef-core-8-primitive-collections-ttn5b6xp |
||||
@ -0,0 +1,54 @@ |
|||||
|
# .NET Aspire 9.0 Features |
||||
|
|
||||
|
.NET Aspire 9.0 is the next major release, supporting both .NET 8 and .NET 9. This version includes new features and improvements. |
||||
|
|
||||
|
## Upgrade to .NET Aspire |
||||
|
|
||||
|
Now, you don't need workloads to develop .NET Aspire applications. In your project, you can add an SDK reference to `Aspire.AppHost.Sdk`. |
||||
|
For more information, you can check out [https://learn.microsoft.com/en-us/dotnet/aspire/whats-new/dotnet-aspire-9?tabs=windows#upgrade-to-net-aspire-9](https://learn.microsoft.com/en-us/dotnet/aspire/whats-new/dotnet-aspire-9?tabs=windows#upgrade-to-net-aspire-9) which explains upgrading an existing project in details. |
||||
|
|
||||
|
## Dashboard |
||||
|
|
||||
|
.NET Aspire offers a nice dashboard for developers to observe the performance and behavior of their applications. In this version, there are some enhancements; |
||||
|
|
||||
|
* **Manage resource lifecycle**: You can stop, start, and restart resources. |
||||
|
* **Mobile and responsive support**: The .NET Aspire dashboard is now mobile-friendly. |
||||
|
* **Sensitive properties**: Properties can be marked as sensitive, automatically masking them in the dashboard UI. |
||||
|
* **Volumes**: Configured container volumes are listed in resource details. |
||||
|
* **Health checks**: .NET Aspire 9 adds support for health checks. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
## Telemetry |
||||
|
|
||||
|
.NET Aspire 9 comes with many new features to the Telemetry service. |
||||
|
|
||||
|
* **Improve telemetry filtering**: Telemetry data can now be filtered by attribute values. |
||||
|
* **Combine telemetry from multiple resources**: If a resource has multiple replicas, you can now filter telemetry data to view from all instances. |
||||
|
* **Browser telemetry support**: The dashboard now supports OpenTelemetry Protocol (OTLP) over HTTP and cross-origin resource sharing (CORS). |
||||
|
|
||||
|
 |
||||
|
|
||||
|
## Orchestration |
||||
|
|
||||
|
The .NET App Host is a core component of the .NET runtime that helps launch and execute .NET applications. |
||||
|
.NET Aspire 9 introduces many new features to the app host. Let's take a look; |
||||
|
|
||||
|
* **Waiting for dependencies**: You can configure a resource to wait for another resource to start before starting. |
||||
|
* **Resource health checks**: The `Waiting for dependencies` feature uses health checks to determine if a resource is ready. |
||||
|
|
||||
|
## Integrations |
||||
|
|
||||
|
.NET Aspire has integrations with some services and tools that make it easy to get started. New integrations are coming with .NET Aspire 9. |
||||
|
|
||||
|
* Redis Insight |
||||
|
* OpenAI (Preview) |
||||
|
* MongoDB |
||||
|
* Azure |
||||
|
|
||||
|
For Azure part, it is better to check the official documentation here [https://learn.microsoft.com/en-us/dotnet/aspire/whats-new/dotnet-aspire-9-release-candidate-1?tabs=windows&pivots=visual-studio#azure](https://learn.microsoft.com/en-us/dotnet/aspire/whats-new/dotnet-aspire-9-release-candidate-1?tabs=windows&pivots=visual-studio#azure) because it has a very detailed explanation. |
||||
|
|
||||
|
## ABP Studio |
||||
|
|
||||
|
.NET Aspire and [ABP Studio](https://abp.io/studio) are tools for different purposes with different scopes, and they have different approaches to solving problems; many developers may still be confused since they also have some similar functionalities and solve some common problems. You can check the comparison of .NET Aspire and ABP Studio in this [article](https://abp.io/community/articles/.net-aspire-vs-abp-studio-side-by-side-t1c73d1l). |
||||
|
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 55 KiB |
@ -0,0 +1,113 @@ |
|||||
|
# SignalR supports trimming and Native AOT |
||||
|
|
||||
|
## What is SignalR? |
||||
|
|
||||
|
SignalR is a library that allows you to add real-time web functionality to your applications. It provides a simple API for creating server-to-client remote procedure calls (RPC) that can be called from the server and client. Now SignalR supports trimming and Native AOT in .NET 8.0 and .NET 9.0. You can learn more about [SignalR new features](https://abp.io/community/articles/asp.net-core-signalr-new-features-summary-kcydtdgq) in this article. |
||||
|
|
||||
|
## What is trimming and Native AOT? |
||||
|
|
||||
|
AOT (Ahead-of-Time) compilation is a feature that allows you to compile your application into native code before running it. This can help improve performance and reduce startup times. Trimming is a feature that allows you to remove unused code from your application, reducing its size and improving performance. You can learn more about [Native AOT Compilation](https://abp.io/community/articles/native-aot-compilation-in-.net-8-oq7qtwov) in this article. |
||||
|
|
||||
|
## How to use SignalR with trimming and Native AOT? |
||||
|
|
||||
|
You can create ASP.NET Core AOT application with using the following command: |
||||
|
|
||||
|
```bash |
||||
|
dotnet new webapiaot -n Acme.Sample |
||||
|
``` |
||||
|
|
||||
|
The created application uses `CreateSlimBuilder` method to create minimal builder for the application. You can use `CreateBuilder` method to create a builder with all the services registered. However, deploying an application with `CreateSlimBuilder` method is more convenient because it reduces the size of the application. You can learn more about [CreateSlimBuilder vs CreateBuilder](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/native-aot#createslimbuilder-vs-createbuilder). |
||||
|
|
||||
|
Replace the `Program.cs` file with the following code: |
||||
|
|
||||
|
```csharp |
||||
|
using Microsoft.AspNetCore.SignalR; |
||||
|
using System.Text.Json.Serialization; |
||||
|
|
||||
|
var builder = WebApplication.CreateSlimBuilder(args); |
||||
|
|
||||
|
builder.Services.AddSignalR(); |
||||
|
builder.Services.Configure<JsonHubProtocolOptions>(o => |
||||
|
{ |
||||
|
o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); |
||||
|
}); |
||||
|
|
||||
|
var app = builder.Build(); |
||||
|
|
||||
|
app.MapHub<ChatHub>("/chatHub"); |
||||
|
app.MapGet("/", () => Results.Content(""" |
||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>SignalR Chat</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<input id="userInput" placeholder="Enter your name" /> |
||||
|
<input id="messageInput" placeholder="Type a message" /> |
||||
|
<button onclick="sendMessage()">Send</button> |
||||
|
<ul id="messages"></ul> |
||||
|
|
||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script> |
||||
|
<script> |
||||
|
const connection = new signalR.HubConnectionBuilder() |
||||
|
.withUrl("/chatHub") |
||||
|
.build(); |
||||
|
|
||||
|
connection.on("ReceiveMessage", (user, message) => { |
||||
|
const li = document.createElement("li"); |
||||
|
li.textContent = `${user}: ${message}`; |
||||
|
document.getElementById("messages").appendChild(li); |
||||
|
}); |
||||
|
|
||||
|
async function sendMessage() { |
||||
|
const user = document.getElementById("userInput").value; |
||||
|
const message = document.getElementById("messageInput").value; |
||||
|
await connection.invoke("SendMessage", user, message); |
||||
|
} |
||||
|
|
||||
|
connection.start().catch(err => console.error(err)); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
|
""", "text/html")); |
||||
|
|
||||
|
app.Run(); |
||||
|
|
||||
|
[JsonSerializable(typeof(string))] |
||||
|
internal partial class AppJsonSerializerContext : JsonSerializerContext { } |
||||
|
|
||||
|
public class ChatHub : Hub |
||||
|
{ |
||||
|
public async Task SendMessage(string user, string message) |
||||
|
{ |
||||
|
await Clients.All.SendAsync("ReceiveMessage", user, message); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
It is a simple chat application that uses SignalR to send and receive messages. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Before deploying the application, ensure that **Desktop development with C++** is installed on your machine if you're using Windows OS. For more details, you can check the [pre-requisites](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot#prerequisites). |
||||
|
|
||||
|
You can deploy the application with the following command: |
||||
|
|
||||
|
```bash |
||||
|
dotnet publish -c Release |
||||
|
``` |
||||
|
|
||||
|
### Limitations |
||||
|
|
||||
|
Since we are using Native AOT, there are some limitations that you should be aware of: |
||||
|
|
||||
|
- **Only the JSON protocol is supported**: For the payload serialization in SignalR, only the JSON protocol is supported. You need to configure the `JsonHubProtocolOptions` to use the `AppJsonSerializerContext` for serialization/deserialization. |
||||
|
- **Reflection**: Native AOT does not support reflection. You need to use the `JsonSerializable` attribute to specify the types that should be serialized/deserialized. In this example, we have used the `JsonSerializable` attribute for the `string` type in the `AppJsonSerializerContext` class. |
||||
|
|
||||
|
For more details, you can check the [limitations](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot#limitations-of-native-aot-deployment) of Native AOT. |
||||
|
|
||||
|
## Conclusion |
||||
|
|
||||
|
In this article, we learned how to use SignalR with trimming and Native AOT in .NET 8.0 and .NET 9.0. We created a simple chat application that uses SignalR to send and receive messages. We also discussed the limitations of using Native AOT and how to overcome them. |
||||
|
|
||||
|
For more information, you can refer to the [Microsoft's official document](https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-9.0#signalr-supports-trimming-and-native-aot). |
||||
|
After Width: | Height: | Size: 31 KiB |
@ -0,0 +1,58 @@ |
|||||
|
# Middleware Now Supports Keyed Dependency Injection in .NET 9 |
||||
|
|
||||
|
This article explores a new feature in .NET 9 that enables keyed dependency injection in middleware. Previously, .NET 8 introduced keyed services, which allowed developers to register multiple instances of the same service type with distinct keys. Now, .NET 9 extends this feature to middleware, making it easier to inject specific services within the middleware based on defined keys. For more details, see this [overview on the .NET blog](https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/rc1/aspnetcore.md#keyed-di-in-middleware). |
||||
|
|
||||
|
## What is Keyed Dependency Injection? |
||||
|
|
||||
|
Keyed dependency injection is a technique for registering multiple service versions with unique identifiers, or “keys.” This approach is especially helpful when multiple implementations of the same service are required in different contexts. For example, you may have various logging services but want to inject a specific logger based on the application’s current needs. By using keys, developers can ensure that the appropriate service version is injected precisely where it’s needed. |
||||
|
|
||||
|
## Using Keyed Dependency Injection in Middleware |
||||
|
|
||||
|
In .NET 9, developers can now use keyed dependency injection directly in middleware. Keyed services can be injected through the middleware constructor or via the `Invoke`/`InvokeAsync` methods, allowing for straightforward and flexible control of service instances in middleware components. Here’s an example of how to configure and use keyed dependency injection in middleware: |
||||
|
|
||||
|
```csharp |
||||
|
var builder = WebApplication.CreateBuilder(args); |
||||
|
|
||||
|
// Register services with unique keys |
||||
|
builder.Services.AddKeyedSingleton<MySingletonClass>("test"); |
||||
|
builder.Services.AddKeyedScoped<MyScopedClass>("test2"); |
||||
|
|
||||
|
var app = builder.Build(); |
||||
|
app.UseMiddleware<MyMiddleware>(); |
||||
|
app.Run(); |
||||
|
|
||||
|
internal class MyMiddleware |
||||
|
{ |
||||
|
private readonly RequestDelegate _next; |
||||
|
private readonly MySingletonClass _singletonService; |
||||
|
|
||||
|
// Constructor injection with key |
||||
|
public MyMiddleware(RequestDelegate next, [FromKeyedServices("test")] MySingletonClass singletonService) |
||||
|
{ |
||||
|
_next = next; |
||||
|
_singletonService = singletonService; |
||||
|
} |
||||
|
|
||||
|
// Invoke method with additional scoped service injection using key |
||||
|
public Task Invoke(HttpContext context, [FromKeyedServices("test2")] MyScopedClass scopedService) |
||||
|
{ |
||||
|
// Middleware logic here |
||||
|
return _next(context); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
In this example: |
||||
|
- `MySingletonClass` and `MyScopedClass` are registered with unique keys (`"test"` and `"test2"`). |
||||
|
- These services are injected into the middleware through both the constructor and `Invoke` method, based on their respective keys. |
||||
|
|
||||
|
This approach allows developers to manage which service instances are available within middleware precisely. |
||||
|
|
||||
|
## Conclusion |
||||
|
|
||||
|
Keyed dependency injection in middleware is a significant addition in .NET 9. It provides developers with more control over which services are injected based on specific keys. This enhancement enables selective service injection in middleware scenarios, allowing for more modular and maintainable applications. |
||||
|
|
||||
|
## References |
||||
|
|
||||
|
- [.NET 9 Release Notes](https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/rc1/aspnetcore.md#keyed-di-in-middleware) |
||||
|
- [Dependency Injection and Keyed Services](https://learn.microsoft.com/aspnet/core/fundamentals/dependency-injection#keyed-services) |
||||
@ -0,0 +1,108 @@ |
|||||
|
# Optimizing Static Asset Delivery feature in ASP.NET Core 9.0 |
||||
|
|
||||
|
Delivering static assets efficiently is a key factor in building performant web applications. By optimizing how assets like CSS, JavaScript, and images are served to the browser, you can reduce load times, decrease network traffic, and improve the overall user experience. |
||||
|
|
||||
|
One powerful tool to help achieve this is **MapStaticAssets**, a feature in ASP.NET Core that significantly optimizes the delivery of static resources. Whether you're working with Blazor, Razor Pages, MVC, or other UI frameworks, **MapStaticAssets** streamlines asset management and ensures that your web app delivers resources in the most efficient way possible. |
||||
|
|
||||
|
## Why Optimizing Static Assets Matters |
||||
|
|
||||
|
Serving static assets without optimization can lead to several performance bottlenecks: |
||||
|
|
||||
|
- **Excessive network requests**: The browser may need to request the same resources multiple times, even if they haven’t changed. |
||||
|
- **Unnecessary data transfer**: Larger files are sent over the network, consuming bandwidth and slowing down page loads. |
||||
|
- **Outdated assets**: Without proper cache management, users may receive stale versions of files after an app update. |
||||
|
|
||||
|
Optimizing static assets involves compressing files, managing caching headers, and ensuring that only the necessary resources are sent to the client. **MapStaticAssets** takes care of all these issues in a seamless, automated way. |
||||
|
|
||||
|
## What is MapStaticAssets? |
||||
|
|
||||
|
**MapStaticAssets** is designed to enhance the default static asset serving mechanism in ASP.NET Core. It can replace `UseStaticFiles` in most scenarios and comes with several built-in optimizations. These optimizations are executed at both build and publish time, ensuring that static resources are served in the most efficient way possible when your app is running. |
||||
|
|
||||
|
Here's how you can implement **MapStaticAssets** in your app: |
||||
|
|
||||
|
```csharp |
||||
|
var builder = WebApplication.CreateBuilder(args); |
||||
|
|
||||
|
builder.Services.AddRazorPages(); |
||||
|
|
||||
|
var app = builder.Build(); |
||||
|
|
||||
|
if (!app.Environment.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseExceptionHandler("/Error"); |
||||
|
app.UseHsts(); |
||||
|
} |
||||
|
|
||||
|
app.UseHttpsRedirection(); |
||||
|
|
||||
|
app.UseRouting(); |
||||
|
|
||||
|
app.UseAuthorization(); |
||||
|
|
||||
|
// Replacing UseStaticFiles with MapStaticAssets |
||||
|
app.MapStaticAssets(); |
||||
|
app.MapRazorPages(); |
||||
|
|
||||
|
app.Run(); |
||||
|
``` |
||||
|
|
||||
|
## Key Features of MapStaticAssets |
||||
|
|
||||
|
1. **Build-time Compression**: |
||||
|
**MapStaticAssets** automatically compresses all static assets during the build process. It uses **gzip** compression during development and **gzip + brotli** compression when publishing. This reduces the file size significantly, ensuring faster download times. |
||||
|
|
||||
|
For example, in a default Razor Pages template, assets like `bootstrap.min.css` and `jquery.js` are compressed by over 80%, resulting in significantly reduced file sizes: |
||||
|
|
||||
|
| File | Original Size | Compressed Size | Compression Reduction | |
||||
|
|----------------------|---------------|-----------------|-----------------------| |
||||
|
| `bootstrap.min.css` | 163 KB | 17.5 KB | 89.26% | |
||||
|
| `jquery.js` | 89.6 KB | 28 KB | 68.75% | |
||||
|
| `bootstrap.min.js` | 78.5 KB | 20 KB | 74.52% | |
||||
|
| **Total** | 331.1 KB | 65.5 KB | 80.20% | |
||||
|
|
||||
|
2. **Content-based ETags**: |
||||
|
**MapStaticAssets** generates **ETags** based on the SHA-256 hash of the file content, encoded in Base64. This ensures that the browser only re-downloads a resource if its content has changed. This eliminates unnecessary network requests, improving page load speeds. |
||||
|
|
||||
|
3. **Smaller File Sizes for Libraries**: |
||||
|
Popular component libraries, such as **Fluent UI Blazor** and **MudBlazor**, benefit from similar compression optimizations. For example, the size of the **MudBlazor** library is reduced by over 90%, from 588 KB to just 46.7 KB after compression. |
||||
|
|
||||
|
| File | Original Size | Compressed Size | Compression Reduction | |
||||
|
|----------------------|---------------|-----------------|-----------------------| |
||||
|
| `MudBlazor.min.css` | 541 KB | 37.5 KB | 93.07% | |
||||
|
| `MudBlazor.min.js` | 47.4 KB | 9.2 KB | 80.59% | |
||||
|
| **Total** | 588.4 KB | 46.7 KB | 92.07% | |
||||
|
|
||||
|
4. **Automatic Optimization**: |
||||
|
As libraries or components are added or updated, **MapStaticAssets** automatically optimizes the assets as part of the build process. This includes minimizing the size of JavaScript and CSS files, reducing the impact of mobile or low-bandwidth environments. |
||||
|
|
||||
|
5. **Serving Assets with a CDN**: |
||||
|
Although **MapStaticAssets** is focused on server-side optimizations, integrating a **CDN (Content Delivery Network)** can further boost performance by serving static assets from servers geographically closer to the user, reducing latency. |
||||
|
|
||||
|
## Comparing MapStaticAssets to IIS Dynamic Compression |
||||
|
|
||||
|
**MapStaticAssets** provides several advantages over traditional dynamic compression techniques, such as IIS **gzip** compression: |
||||
|
|
||||
|
- **Simplicity**: There is no need for server-specific configuration, making **MapStaticAssets** easy to implement. |
||||
|
- **Performance**: By compressing assets at build time, the app doesn't need to perform compression during every request, which improves server performance. |
||||
|
- **Optimization**: Developers can focus on ensuring that assets are compressed to the smallest possible size during the build process. |
||||
|
|
||||
|
For example, using **MapStaticAssets**, a file like `MudBlazor.min.css` is compressed down to 37.5 KB, whereas IIS dynamic compression might result in a size of 90 KB. This represents a **59%** reduction in size. |
||||
|
|
||||
|
## About MapAbpStaticAssets |
||||
|
|
||||
|
The ABP framework is 100% compatible with this new feature. |
||||
|
|
||||
|
However, some JavaScript, CSS, and image files exist in the [Virtual File System](https://abp.io/docs/latest/framework/infrastructure/virtual-file-system), which ASP.NET Core's **MapStaticAssets** can't handle. For these files, additional **StaticFileMiddleware** is needed to serve them, which is where **MapAbpStaticAssets** comes in. |
||||
|
|
||||
|
**MapAbpStaticAssets** adds the necessary **StaticFileMiddleware** to ensure that virtual files are correctly served. This middleware setup ensures seamless delivery of virtual resources alongside static assets. |
||||
|
|
||||
|
You can view the source code of **MapAbpStaticAssets** on [GitHub](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs#L129-L198). |
||||
|
|
||||
|
## Conclusion |
||||
|
|
||||
|
Optimizing static asset delivery is essential for building fast, efficient web applications. **MapStaticAssets** simplifies and automates the optimization of static files by providing build-time compression, caching headers, and content-based ETags. This ensures that your app's static assets are always delivered in the most efficient way, whether users are on fast broadband or slower mobile connections. By using **MapStaticAssets**, you can deliver a faster, more reliable experience for your users with minimal effort. |
||||
|
|
||||
|
## References |
||||
|
|
||||
|
* [Static files in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-9.0) |
||||
|
* [What's new in ASP.NET Core 9.0](https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-8.0#optimize-static-web-asset-delivery) |
||||
|
After Width: | Height: | Size: 460 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 106 KiB |
@ -0,0 +1,126 @@ |
|||||
|
Built-in OpenAPI Document Generation with .NET 9 — No more SwaggerUI! 👋 |
||||
|
======================================================================== |
||||
|
|
||||
|
 |
||||
|
|
||||
|
What’s Swagger UI? |
||||
|
------------------ |
||||
|
|
||||
|
[Swagger UI](https://swagger.io/) is an open-source tool that automatically generates an interactive, web-based documentation interface for APIs defined using the OpenAPI Specification (OAS). It simplifies the process of exploring and testing APIs directly from the browser, making it a popular choice for developers and API consumers. It provides paid also free services. |
||||
|
|
||||
|
> Previously, Swagger was included by default from **.NET 5** to **.NET 8**, which provides OpenAPI support. |
||||
|
|
||||
|
What’s OpenAPI? |
||||
|
--------------- |
||||
|
|
||||
|
OpenAPI is a standard specification for defining RESTful APIs. Its website is [https://www.openapis.org/](https://www.openapis.org/). Their specifications let developers create, understand, document, and integrate APIs more efficiently. |
||||
|
|
||||
|
OpenAPI Microsoft Documentation 👉 [https://aka.ms/aspnet/openapi](https://aka.ms/aspnet/openapi) |
||||
|
|
||||
|
|
||||
|
|
||||
|
Replacement of Swagger UI with OpenAPI for API projects — Bye Swashbuckle 👋 |
||||
|
---------------------------------------------------------------------------- |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Swagger UI, a third-party tool used for documenting and testing APIs, is no longer integrated into NET 9, as Microsoft wants a solution with first-class support, better control, and enhanced security. |
||||
|
|
||||
|
In March 2024, the ASP.NET Core team announced plans to remove the `Swashbuckle.AspNetCore` dependency from web API templates in the upcoming .NET 9 release. |
||||
|
|
||||
|
> This decision was influenced by the project's lack of active maintenance and the absence of an official release for .NET 8. |
||||
|
|
||||
|
The team intends to enhance the `Microsoft.AspNetCore.OpenApi` package to provide built-in OpenAPI document generation capabilities, reducing reliance on external tools. Community members expressed concerns about the inactivity of Swashbuckle and discussed alternatives like contributing to or forking the project. The ASP.NET Core team has engaged with the maintainers of both Swashbuckle and NSwag to explore potential collaborations and ensure a smooth transition for developers. |
||||
|
|
||||
|
In the below issue, you can see the details of this decision: |
||||
|
|
||||
|
* https://github.com/dotnet/aspnetcore/issues/54599 |
||||
|
|
||||
|
|
||||
|
|
||||
|
**Jeremy** -Product Manager- at Microsoft answers why they took this decision in [this post](https://github.com/dotnet/aspnetcore/issues/54599#issuecomment-2004975574). |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Why is SwaggerUI Removed from .NET 9? |
||||
|
------------------------------------- |
||||
|
|
||||
|
**The change is due to a lack of maintenance of the Swagger library**, although it has seen some recent updates. |
||||
|
|
||||
|
This change aims to reduce dependency on external tools and provide a streamlined, out-of-the-box experience for generating OpenAPI documentation for ASP.NET Core Web APIs. |
||||
|
|
||||
|
|
||||
|
|
||||
|
What are the Benefits of the New OpenAI Documentation? |
||||
|
--------------------------------------------------------------- |
||||
|
|
||||
|
1. **Native Support and Reduced Dependency** |
||||
|
|
||||
|
The new `Microsoft.AspNetCore.OpenApi` package provides first-class support for OpenAPI.Reduces reliance on external tools like Swashbuckle or NSwag for basic documentation needs. |
||||
|
|
||||
|
2. **Simplified Configuration** |
||||
|
|
||||
|
No need for extensive setup or third-party integrations. Just by defining controllers and endpoints, the framework can automatically generate OpenAPI specifications. |
||||
|
|
||||
|
3. **Better Integration with Minimal APIs** |
||||
|
|
||||
|
- The built-in support is optimized for **Minimal APIs**, a feature introduced in .NET 6. Automatically includes metadata for routes, request parameters, and responses. |
||||
|
|
||||
|
4. **Performance Improvement** |
||||
|
|
||||
|
- The native implementation leverages source generators to reduce runtime overhead. Faster and more efficient than traditional runtime-based solutions like Swashbuckle. |
||||
|
|
||||
|
5. **Compatibility with Existing Tools** |
||||
|
|
||||
|
* The OpenAPI output can still be consumed by Swagger UI, NSwag, or other tools for visualization, testing, or client generation. |
||||
|
|
||||
|
How to Use the New OpenAPI in .NET9? |
||||
|
------------------------------------ |
||||
|
|
||||
|
If you are creating a new ASP.NET Core project, you can see the below checkbox to add OpenAPI. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
When I created a new .NET 9 web project, I saw that OpenAPI had already been added. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Add the required NuGet package [Microsoft.AspNetCore.OpenApi](https://www.nuget.org/packages/Microsoft.AspNetCore.OpenApi) |
||||
|
|
||||
|
``` |
||||
|
dotnet add package Microsoft.AspNetCore.OpenApi |
||||
|
``` |
||||
|
|
||||
|
### Enable OpenAPI Support |
||||
|
|
||||
|
Add the following services and middleware in `Program.cs` |
||||
|
|
||||
|
``` |
||||
|
var builder = WebApplication.CreateBuilder(); |
||||
|
builder.Services.AddOpenApi(); //<<----- |
||||
|
var app = builder.Build(); |
||||
|
app.MapOpenApi(); //<<----- |
||||
|
app.MapGet("/", () => "Test"); |
||||
|
app.Run(); |
||||
|
``` |
||||
|
|
||||
|
Your OpenAPI document URL is [_https://localhost:7077/openapi/v1.json_](https://localhost:7077/openapi/v1.json) |
||||
|
|
||||
|
Change the port to your active port. This is how it looks like: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
Alternative 3rd Party Tool: Scalar |
||||
|
================================== |
||||
|
|
||||
|
**Scalar** is an open-source API platform for RestAPI documentation. Also, it provides an interface for interacting with RESTful API. Generates interactive and user-friendly API documentation. Supports OpenAPI and Swagger specifications. It’s open-source with **7K stars** on GitHub. |
||||
|
|
||||
|
See the repo 👉 [https://github.com/scalar/scalar](https://github.com/scalar/scalar). |
||||
|
|
||||
|
|
||||
|
|
||||
|
**References** |
||||
|
|
||||
|
* [https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-8.0#openapi](https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-8.0#openapi) |
||||
|
|
||||
|
|
||||
@ -0,0 +1,184 @@ |
|||||
|
# Solution Templates: A Guide to Select the Right One For You |
||||
|
|
||||
|
ABP provides several [startup templates](index.md) to you. It is important to start with the right startup template that is suitable for your **project** and **team**. This guide aims to lead you to select the most proper startup template for your requirements. |
||||
|
|
||||
|
The following **architectures** will be discussed based on ABP startup templates: |
||||
|
|
||||
|
* **Single-Layer** (non-layered) application |
||||
|
* **N-Layered** application |
||||
|
* **Modular** application |
||||
|
* **Microservice** solution |
||||
|
|
||||
|
## What is a Startup Template? |
||||
|
|
||||
|
In the following section, you will understand what a startup template is and what it provides. |
||||
|
|
||||
|
### Pre-Architected Solution Structure |
||||
|
|
||||
|
A startup solution template is a **pre-architected** structure. For example, the [layered startup template](layered-web-application/index.md) is a great starting point if you want to build a layered application code-base based on [Domain-Driven Design](../framework/architecture/domain-driven-design/index.md) principles and patterns. |
||||
|
|
||||
|
However, starting with any startup template **doesn't limit you** on adding or removing projects, layers, integration packages, and creating other applications/services. You can even start with a [single-layer application template]() and convert it to a microservice solution. However, if you want to build a microservice solution, starting with the [microservice startup template](microservice/index.md) is the best. |
||||
|
|
||||
|
So, it is **best to start with the most suitable startup template** for your purpose and then modify the solution to fit your custom requirements. |
||||
|
|
||||
|
### Well-Structured Code Organization |
||||
|
|
||||
|
Besides the overall solution structure, the internals of each project in a solution template are well-structured. It is clear where will you place your [entities](../framework/architecture/domain-driven-design/entities.md), [repositories](../framework/architecture/domain-driven-design/repositories.md), [data access code](../framework/data/index.md), [application services](../framework/architecture/domain-driven-design/application-services.md), [API controllers](../framework/api-development/index.md), [UI elements](../framework/ui/index.md), etc. You don't spend time thinking about how to **organize your codebase** in every new project. If you prefer to build automated tests for your application, the test projects are also included and pre-configured. |
||||
|
|
||||
|
### Library Integrations & Configurations |
||||
|
|
||||
|
When you use ABP startup solution templates to create a new solution, some **fundamental library installations** ([Serilog](https://serilog.net/), [Autofac](https://autofac.org/), [AutoMapper](https://automapper.org/), [Swagger](https://swagger.io/), [HealthCheck](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks) and others..) and their fine-tuned configurations are already prepared for you. Also, required **[ABP packages](https://abp.io/packages)** are just installed based on your preferences and configured for **development and production environments**. |
||||
|
|
||||
|
### Development Ready |
||||
|
|
||||
|
When you create a new solution, or when a new teammate starts to work on your existing solution, it is pretty **easy to understand** the solution structure, design decisions and the development flow by the help of [solution structure documents](index.md) and [development tutorials](../tutorials/index.md). |
||||
|
|
||||
|
### Production Ready |
||||
|
|
||||
|
ABP's startup templates are ready for production out of the box. You can just create a new solution and **deploy it** to your production environment. It is not only about **technical readiness**, but also about **functional readiness**. |
||||
|
|
||||
|
When you create a new ABP solution, **fundamental modules are already installed**. Your application has a robust [account module](../modules/account.md) (user registration, login, social logins, 2-factor authentication, user lockout, password complexity control...), an advanced [identity module](../modules/identity.md) (user, role and permission management), and many other production-ready [pre-built application modules](../modules/index.md) are just working as a part your application in the first day. |
||||
|
|
||||
|
## The Startup Solution Templates |
||||
|
|
||||
|
Up to this point, it is explained what a startup template is and the features it offers. In the following section, you will see the types of startup solution templates and **which one is best for you**. |
||||
|
|
||||
|
### Single-Layer Application Solution Template |
||||
|
|
||||
|
The [single-layer solution template](single-layer-web-application/index.md) is the simplest. It provides a **minimal solution architecture** while starting a new project. Your .NET solution typically contains a **single, or a few .NET projects** depending on your UI and other preferences while creating your solution. |
||||
|
|
||||
|
The following figure shows a single-project web application that has [MVC (Razor Pages) UI](../framework/ui/mvc-razor-pages/overall.md) and [Entity Framework Core](../framework/data/entity-framework-core/index.md) database provider with the default configuration: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
As you see in the preceding figure, all the application code (entities, data access, services, UI pages, etc.) are located in a **single .NET project**. |
||||
|
|
||||
|
#### When Should You Start a Single-Layer Solution Template? |
||||
|
|
||||
|
In the following conditions, you may consider to use the single-layer solution template: |
||||
|
|
||||
|
* If **your project is small** and you don't expect that it will grow by the time. But remember that many projects are thought of as small in the beginning. |
||||
|
* If your project is a **temporary project** and it will be trash in a short time. It can be a POC project or a temporary application for a demo or advertisement campaign. |
||||
|
* If you are a **single developer** or there are only 2-3 developers working on your solution, and these developers are not experienced and are not willing to understand the structure and benefits of a layered application. |
||||
|
|
||||
|
If the preceding conditions are met with your case, you can consider starting with this solution template. |
||||
|
However, keep in mind that your solution can quickly become a **[big ball of mud](https://thedomaindrivendesign.io/big-ball-of-mud/)**. |
||||
|
We think that only a **very small portion of applications** are suitable for that kind of solution structure. |
||||
|
|
||||
|
Note that the single-layer solution template doesn't provide the following options/features: |
||||
|
|
||||
|
* Mobile application integration |
||||
|
* Public website (a second web application for landing page of your product) |
||||
|
* Tiered architecture (for MVC applications, separates UI and service layers) |
||||
|
* Separate tenant schema (for multi-tenant applications) |
||||
|
* Kubernetes / Helm configuration |
||||
|
* Automated (unit/integration) test projects |
||||
|
|
||||
|
These options are not implemented to keep the solution structure as simple as possible. If you need some of these, please feel free to use the layered solution template. |
||||
|
|
||||
|
### Layered Solution Template |
||||
|
|
||||
|
The [layered application startup template](layered-web-application/index.md) is a .NET solution that consists of several projects. |
||||
|
Each project represents a layer of the application or has a specific functionality for the solution. |
||||
|
|
||||
|
The exact project count in your solution depends on the options you have selected. |
||||
|
The following figure shows a solution with [MVC (Razor Pages) UI](../framework/ui/mvc-razor-pages/overall.md) and [Entity Framework Core](../framework/data/entity-framework-core/index.md) database provider with the default configuration: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
The solution is layered based on Domain-Driven Design principles and extended based on real-world business application requirements. It includes test projects for each layer. Layering the solution has great benefits: |
||||
|
|
||||
|
* It makes your business code (domain & application layers) independent from infrastructure (UI and database) that makes it more **maintainable and long-living**. |
||||
|
* Different developers can **focus on different layers**. It is valuable when multiple developers (with different roles) touching to the same solution. |
||||
|
* It **separate concerns**, so you can focus on a single concern at a time. You can optimize your data access code without touching other layers, you can change your UI code without breaking your business logic. |
||||
|
* It provides **maximum code reusability**. If you have multiple applications (e.g. one back office application, one end-user application, and a mobile application), it is easy to separate these application's codebases (simply create new application & UI layers for each application type) while they can share the same domain and data access layers. |
||||
|
* Separating **UI layer** gives an opportunity to **replace/modify** it in the future without affecting other parts of your solution. You know, UI is the fastest changing technology in software industry. |
||||
|
|
||||
|
While it may seem a little complicated at the beginning, once you **complete the [Book Store tutorial](../tutorials/book-store/index.md)**, you will easily understand the purpose and usage of each project. |
||||
|
|
||||
|
#### When Should You Start a Layered Solution Template? |
||||
|
|
||||
|
In the following conditions, you may consider to use the layered solution template: |
||||
|
|
||||
|
* If your project **code-base** is relatively **larger**. |
||||
|
* If your project is **relatively complex** and involves multiple business domains or complex workflows. |
||||
|
* If your project is a **long-term** project and you want to design it as **maintainable** for long years. |
||||
|
* If you are **a team of developers** working on your solution. When multiple teams or developers work on different parts of the system. |
||||
|
* If your solution will have **multiple** web, mobile or other type of **applications** that need to **share** the same business logic. |
||||
|
* If your project needs **scalability**, solutions that may need to scale in functionality or user load over time. |
||||
|
* If your project needs **extensibility**, which requires the addition of new features or integration with third-party services. |
||||
|
|
||||
|
### Modular Monolith Applications |
||||
|
|
||||
|
ABP does not provide a specific modular monolith application startup template. However, it is not needed. Let us explain why. |
||||
|
|
||||
|
The ABP Framework and [ABP Studio](../studio/index.md) are already designed to support modular application development from their beginning. ABP framework provides all the **necessary infrastructure** for [modularity](../framework/architecture/modularity/basics.md) and all other framework features are **compatible with modular solutions**. |
||||
|
|
||||
|
On the other hand, the main purpose of ABP Studio's [Solution Explorer panel](../studio/solution-explorer.md) is to **architect and build modular and complex software solutions**. You can easily create new modules, arrange dependencies between the modules and import/install these modules into a monolith application. While you can do all these manually yourself, ABP Studio makes it extremely easy to do and understand it. |
||||
|
|
||||
|
#### How to Build a Modular Monolith Application? |
||||
|
|
||||
|
A **modular monolith** application consists of a **single host** application and **multiple sub-modules**. Typically, each module has its own .NET solution that contains the code related to that module. So, the general structure is shown in the following figure: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
In this example, `MyCrm.Host` is an almost-empty host application that has package references to other modules. Every module consists of two packages: implementation and contract packages. |
||||
|
|
||||
|
You can follow the steps below to create such a modular solution with ABP Studio: |
||||
|
|
||||
|
* **Create a new application** using either [single-layer](single-layer-web-application/index.md) or [layered](layered-web-application/index.md) application startup template. That application will be the **host application** of your solution. |
||||
|
* **Create new modules** (right-click to the solution root, select the *Add* -> *New Module* -> ... command). |
||||
|
* **Import & Install** these **modules** to the host application. |
||||
|
|
||||
|
> You can follow the **[Modular Monolith Application Development Tutorial](../tutorials/modular-crm/index.md)** to learn how to build a modular application step by step. |
||||
|
|
||||
|
#### Which Startup Template should be used for a Modular Application? |
||||
|
|
||||
|
So, both [single-layer](single-layer-web-application/index.md) and [layered](layered-web-application/index.md) application startup templates are inherently modular. Just use one of them and start your modular solution. You may wonder which one to start: |
||||
|
|
||||
|
* Use the **[single-layer startup template](single-layer-web-application/index.md)** for the host application of your modular monolith if you will leave the host application as empty. It will contain some configuration code of course, but it won't contain any actual application code. **This is the suggested approach.** |
||||
|
* Use the **[layered application startup template](layered-web-application/index.md)** if you will write some application code into the hosting application. You may want to write some code that makes multiple module operations that are not easy to implement in a particular module. In that case, a layered hosting application will be a better way to organize your codebase. However, this approach can quickly move your solution away from a modular system. So, take your own risk. |
||||
|
|
||||
|
#### When Should You Start a Modular Monolith Application? |
||||
|
|
||||
|
In the following conditions, you may consider to build a modular software solution: |
||||
|
|
||||
|
* If your **domain is too complex** to develop and maintain in a single monolith codebase. |
||||
|
* If your business domain has clear functional boundaries and is possible to **split into sub-domains**. |
||||
|
* If you have **multiple teams** that will work on the solution. |
||||
|
* When you need to **reduce complexity** early on. And if you are considering migrating your application to a **microservice system**. |
||||
|
|
||||
|
While all these are also suitable for microservice solutions (which will be discussed in the next section), a modular solution is more suitable than microservices for most of the projects. |
||||
|
Especially if you don't need to have technology diversity, deploy and scale services independently and serve too many users concurrently with a fault-tolerant system, a modular monolith application would be a better choice to not deal with the complexity of a microservice system. Also, it is better to start with a thin architecture when you have a small team that can focus on building features without spending time managing distributed architecture complexities. Another advantage is that the modular monoliths avoid the network latency and communication overhead inherent in microservices, so debugging and monitoring are easier as all modules run within a single application. In summary, if you anticipate that your application may grow to require a microservices architecture in the future, a modular monolith is a great starting point. |
||||
|
|
||||
|
> Build for Now, Scale for Tomorrow |
||||
|
|
||||
|
Even if you are considering building a microservice architecture, it is usually advised to [start with a monolith modular first](https://martinfowler.com/bliki/MonolithFirst.html), then migrate to microservices later once your business and module boundaries are more stable. |
||||
|
|
||||
|
### Microservice Solution Template |
||||
|
|
||||
|
ABP's [microservice startup template](microservice/index.md) includes multiple services, API gateways and applications that are well integrated into each other and ready to be a great **base solution for your microservice system**. |
||||
|
In the following picture, you can see an overall diagram that shows the main components of the solution (they vary based on the options while you are creating your solution): |
||||
|
|
||||
|
 |
||||
|
|
||||
|
#### When Should You Start a Microservice Solution Template? |
||||
|
|
||||
|
In the following conditions, you may consider to build a microservice system: |
||||
|
|
||||
|
* If your **domain is too complex** to develop and maintain in a single monolith codebase. |
||||
|
* If your business domain is possible to **split into sub-domains**. |
||||
|
* If you have **multiple teams** that will work on the solution in parallel. |
||||
|
* If you need to **develop**, **test**, **deploy** and **scale services** independently. |
||||
|
* If you need to use **multiple technology stacks** so, some services can be built with .NET, and others can be built with Java, Python, etc... |
||||
|
* If you need to serve **too many users** concurrently with a **high available** and **fault tolerant** system. |
||||
|
* If you have **DevOps** knowledge and culture in your company. If you can deal with **complex** development, build, test, deployment and production environments. |
||||
|
|
||||
|
**Use Case Examples** |
||||
|
* **E-Commerce Platforms**: Separate services for cart, checkout, inventory, and shipping. |
||||
|
* **Streaming Services**: Modules for content delivery, user preferences, and recommendations. |
||||
|
* **SaaS Platforms**: Independent services for billing, user accounts, and analytics. |
||||
|
|
||||
|
## Conclusion |
||||
|
|
||||
|
In summary, the ABP Platform offers a variety of solution templates tailored to different architectural needs, including layered applications, microservices, and modular development. These templates provide a great foundation for building robust applications by implementing best practices and providing essential features. By selecting the right template, you can streamline the development process, ensuring scalability and maintainability in your project. |
||||
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 12 KiB |
@ -1,12 +1,21 @@ |
|||||
# Solution Templates |
# Solution Templates |
||||
|
|
||||
ABP provides pre-architected and production-ready templates to jump start a new solution. The following solution templates are provided out of the box: |
ABP provides pre-architected and production-ready templates to jump start a new solution. |
||||
|
|
||||
* **[Single-Layer Solution](single-layer-web-application)**: A single-project solution. Recommended for building an application with a **simpler and easy to understand** architecture. |
> **You can see the [Solution Template Selection Guide](guide.md) if you are not sure which solution template is suitable for you.** |
||||
* **[Layered Solution](layered-web-application)**: A fully layered (multiple projects) solution based on [Domain Driven Design](../framework/architecture/domain-driven-design) practices. Recommended for long-term projects that need a **maintainable and extensible** codebase. |
|
||||
* **[Microservice Solution](microservice)**: A **distributed solution** to build **microservice systems**. It includes pre-built services, API gateways, web and mobile applications, Kubernetes and Helm configuration, and everything you need to start your large-scale microservice solution. |
The following solution templates are provided out of the box: |
||||
* **[Application Module](application-module)**: A template that can be used to create a **reusable [application module](../modules)** based on the [module development best practices & conventions](../framework/architecture/best-practices). It is also suitable for creating **services** (with or without UI). |
|
||||
|
* **[Single-Layer Solution](single-layer-web-application/index.md)**: A single-project solution. Recommended for building an application with a **simpler and easy to understand** architecture. |
||||
|
* **[Layered Solution](layered-web-application/index.md)**: A fully layered (multiple projects) solution based on [Domain Driven Design](../framework/architecture/domain-driven-design) practices. Recommended for long-term projects that need a **maintainable and extensible** codebase. |
||||
|
* **[Microservice Solution](microservice/index.md)**: A **distributed solution** to build **microservice systems**. It includes pre-built services, API gateways, web and mobile applications, Kubernetes and Helm configuration, and everything you need to start your large-scale microservice solution. |
||||
|
* **[Application Module](application-module/index.md)**: A template that can be used to create a **reusable [application module](../modules/index.md)** based on the [module development best practices & conventions](../framework/architecture/best-practices/index.md). It is also suitable for creating **services** (with or without UI). |
||||
* **Others** |
* **Others** |
||||
- [MAUI Application](../get-started/maui.md) |
- [MAUI Application](../get-started/maui.md) |
||||
- [WPF Application](../get-started/wpf.md) |
- [WPF Application](../get-started/wpf.md) |
||||
- [Console Application](../get-started/console.md) |
- [Console Application](../get-started/console.md) |
||||
|
|
||||
|
## See Also |
||||
|
|
||||
|
* [Solution Template Selection Guide](guide.md) |
||||
|
* [Get Started with ABP Platform](../get-started/index.md) |
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 27 KiB |
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"sdk": { |
"sdk": { |
||||
"version": "9.0.100-rc.2.24474.11", |
"version": "9.0.100", |
||||
"rollForward": "latestFeature" |
"rollForward": "latestFeature" |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,3 +1,2 @@ |
|||||
{ |
{ |
||||
"role": "lib.ef" |
|
||||
} |
} |
||||
@ -1,3 +1,2 @@ |
|||||
{ |
{ |
||||
"role": "lib.mongodb" |
|
||||
} |
} |
||||