|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 525 KiB |
@ -0,0 +1,93 @@ |
|||
# ABP.IO Platform 9.0 Has Been Released Based on .NET 9.0 |
|||
|
|||
 |
|||
|
|||
Today, [ABP](https://abp.io/) 9.0 stable version has been released based on [.NET 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/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. |
|||
|
|||
## What's New With Version 9.0? |
|||
|
|||
All the new features were explained in detail in the [9.0 RC Announcement Post](https://abp.io/blog/announcing-abp-9-0-release-candidate), so there is no need to review them again. You can check it out for more details. |
|||
|
|||
## Getting Started with 9.0 |
|||
|
|||
### Creating New Solutions |
|||
|
|||
You can check the [Get Started page](https://abp.io/get-started) to see how to get started with ABP. You can either download [ABP Studio](https://abp.io/get-started#abp-studio-tab) (**recommended**, if you prefer a user-friendly GUI application - desktop application) or use the [ABP CLI](https://abp.io/docs/latest/cli) to create new solutions. |
|||
|
|||
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. **You can create solutions with ABP 9.0 starting from v0.9.11.** |
|||
|
|||
### How to Upgrade an Existing Solution |
|||
|
|||
You can upgrade your existing solutions with either ABP Studio or ABP CLI. In the following sections, both approaches are explained: |
|||
|
|||
### Upgrading via ABP Studio |
|||
|
|||
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 [the documentation](https://abp.io/docs/latest/studio/installation#upgrading) for more info. |
|||
|
|||
After upgrading the ABP Studio, then you can open your solution in the application, and simply click the **Switch to stable** action button to instantly upgrade your solution: |
|||
|
|||
 |
|||
|
|||
> Please note that ABP CLI & ABP Studio only upgrade the related ABP packages, so you need to upgrade the other packages for .NET 9.0 manually. |
|||
|
|||
### Upgrading via ABP CLI |
|||
|
|||
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. |
|||
|
|||
If you haven't installed it yet, you can run the following command: |
|||
|
|||
```bash |
|||
dotnet tool install -g Volo.Abp.Studio.Cli |
|||
``` |
|||
|
|||
Or to update the existing CLI, you can run the following command: |
|||
|
|||
```bash |
|||
dotnet tool update -g Volo.Abp.Studio.Cli |
|||
``` |
|||
|
|||
After installing/updating the ABP CLI, you can use the [`update` command](https://abp.io/docs/latest/CLI#update) to update all the ABP related NuGet and NPM packages in your solution as follows: |
|||
|
|||
```bash |
|||
abp update |
|||
``` |
|||
|
|||
You can run this command in the root folder of your solution to update all ABP related packages. |
|||
|
|||
> Please note that ABP CLI & ABP Studio only upgrade the related ABP packages, so you need to upgrade the other packages for .NET 9.0 manually. |
|||
|
|||
## Migration Guides |
|||
|
|||
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: [ABP Version 9.0 Migration Guide](https://abp.io/docs/9.0/release-info/migration-guides/abp-9-0) |
|||
|
|||
## Community News |
|||
|
|||
### Highlights from .NET 9.0 |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
> You can read [this post](https://volosoft.com/blog/Highlights-for-ASP-NET-Entity-Framework-Core-NET-9-0) to see the list of all articles. |
|||
|
|||
### New ABP Community Articles |
|||
|
|||
In addition to [the articles to highlight .NET 9.0 features written by our team](https://volosoft.com/blog/Highlights-for-ASP-NET-Entity-Framework-Core-NET-9-0), here are some of the recent posts added to the [ABP Community](https://abp.io/community): |
|||
|
|||
* [Video: Building Modular Monolith Applications with ASP.NET Core & ABP Studio](https://abp.io/community/videos/building-modular-monolith-applications-with-asp.net-core-abp-studio-66znukvf) by [Halil İbrahim Kalkan](https://x.com/hibrahimkalkan) |
|||
* [How to create your Own AI Bot on WhatsApp Using an ABP.io Template](https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c) by [Michael Kokula](https://abp.io/community/members/Michal_Kokula) |
|||
* [ABP Now Supports .NET 9](https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f) by [Alper Ebiçoğlu](https://x.com/alperebicoglu) |
|||
|
|||
Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. |
|||
|
|||
### ABP Community Talks 2024.7: What’s New with .NET 9 & ABP 9? |
|||
|
|||
 |
|||
|
|||
In this episode of ABP Community Talks, 2024.7; we will dive into the features that came with .NET 9.0 with [Alper Ebicoglu](https://github.com/ebicoglu), [Engincan Veske](https://github.com/EngincanV), [Berkan Sasmaz](https://github.com/berkansasmaz) and [Ahmet Faruk Ulu](https://github.com/ahmetfarukulu). |
|||
|
|||
## Conclusion |
|||
|
|||
This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://docs.abp.io/en/abp/9.0/Road-Map) 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. |
|||
|
|||
Thanks for being a part of this community! |
|||
|
After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,67 @@ |
|||
# ABP Studio Goes AOT: Faster Startups with Ready-to-Run (R2R) Publishing |
|||
|
|||
We're excited that [ABP Studio](https://abp.io/studio) now supports [Ready-to-Run (R2R) publishing](https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run) (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. |
|||
|
|||
Let's dive into what R2R publishing is, how it works, and the benefits it brings to ABP Studio. |
|||
|
|||
## What is Ready-to-Run (R2R) Publishing? |
|||
|
|||
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. |
|||
|
|||
However, R2R isn't a complete AOT compilation. Instead, it's a hybrid approach because it stores both: |
|||
|
|||
* **Native code for precompiled methods** (to improve startup time and performance) |
|||
|
|||
* **Intermediate Language (IL) code** for methods that may need further JIT compilation |
|||
|
|||
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. |
|||
|
|||
## How R2R (Ready-to-Run) Improves ABP Studio |
|||
|
|||
### Faster Startup Time 🚀 |
|||
|
|||
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 **reduced by 2.5x** ⬇️. |
|||
|
|||
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. |
|||
|
|||
### Performance Enhancements 📈 |
|||
|
|||
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. |
|||
|
|||
### Trade-offs: Increased Storage Size 🆙 |
|||
|
|||
With great performance comes a slight trade-off: storage size. R2R binaries include both **native** and **IL code**, 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. |
|||
|
|||
## How to Enable R2R Publishing in Your Applications? |
|||
|
|||
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: |
|||
|
|||
1. You can add the following configuration to your final project's `.csproj` file: |
|||
|
|||
```xml |
|||
<PropertyGroup> |
|||
<PublishReadyToRun>true</PublishReadyToRun> |
|||
</PropertyGroup> |
|||
``` |
|||
|
|||
2. Then, publish your application with the `dotnet publish` command: |
|||
|
|||
```bash |
|||
dotnet publish -c Release |
|||
``` |
|||
|
|||
Alternatively, you can specify the _PublishReadyToRun_ flag directly to the `dotnet publish` command as follows: |
|||
|
|||
```bash |
|||
dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true |
|||
``` |
|||
|
|||
That's it! Your application will now include precompiled native code for faster startup and great performance benefits. |
|||
|
|||
> Please refer to the [official documentation](https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run) before publishing your application with R2R. |
|||
|
|||
## Conclusion |
|||
|
|||
As ABP team, we're always looking for ways to improve the developer experience. By adopting **Ready-to-Run (R2R) publishing** for ABP Studio, we're aiming to deliver a faster and more efficient tool for your development needs. |
|||
|
|||
Stay tuned for more updates and enhancements as we continue to optimize ABP Studio and please provide us with your invaluable feedback. |
|||
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 271 KiB |
@ -0,0 +1,309 @@ |
|||
# ABP Global Assets - New way to bundle JavaScript/CSS files in Blazor WebAssembly app |
|||
|
|||
We have introduced a new feature in the ABP framework to bundle the `JavaScript/CSS` files in the Blazor wasm app. This feature is called `Global Assets`. |
|||
With this feature, you don't need to run the `abp bundle` command to manually create/maintain the `global.js` and `global.css` files in your Blazor wasm app. |
|||
|
|||
## How Global Assets works? |
|||
|
|||
The new `Blazor wasm app` has two projects: |
|||
|
|||
1. `MyProjectName` (ASP.NET Core app) |
|||
2. `MyProjectName.Client` (Blazor wasm app) |
|||
|
|||
The `MyProjectName` reference the `MyProjectName.Client` project, and will be the entry point of the application, which means the `MyProjectName` project will be the `host` project of the `MyProjectName.Client` project. |
|||
|
|||
The static/virtual files of `MyProjectName` can be accessed by the `MyProjectName.Client` project, so we can create dynamic global assets in the `MyProjectName` project and use them in the `MyProjectName.Client` project. |
|||
|
|||
## How it works in ABP? |
|||
|
|||
We have created a new package `WebAssembly.Theme.Bundling` for the theme `WebAssembly` module and used the `Volo.Abp.AspNetCore.Mvc.UI.Bundling.BundleContributor` to add `JavaScript/CSS` files to the bundling system. |
|||
|
|||
* LeptonXLiteTheme: `AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule` |
|||
* LeptonXTheme: `AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule` |
|||
* LeptonTheme: `AbpAspNetCoreComponentsWebAssemblyLeptonThemeBundlingModule` |
|||
* BasicTheme: `AbpAspNetCoreComponentsWebAssemblyBasicThemeBundlingModule` |
|||
|
|||
The new `ThemeBundlingModule` only depends on `AbpAspNetCoreComponentsWebAssemblyThemingBundlingModule(new package)`. It's an `abstractions module`, which only depends on `AbpAspNetCoreMvcUiBundlingAbstractionsModule`. |
|||
|
|||
We will get all `JavaScript/CSS` files on `OnApplicationInitializationAsync` method of `AbpAspNetCoreMvcUiBundlingModule` from bundling system and add them to `IDynamicFileProvider` service. After that, we can access the `JavaScript/CSS` files in the Blazor wasm app. |
|||
|
|||
## Add the Global Assets in the module |
|||
|
|||
If your module has `JavaScript/CSS` files that need to the bundling system, You have to create a new project(`YourModuleName.Blazor.WebAssembly.Bundling`) to your module solution, and reference the new project in the `MyProjectName` project and module dependencies. |
|||
|
|||
The new project should **only** depend on the `AbpAspNetCoreComponentsWebAssemblyThemingBundlingModule` and define `BundleContributor` classes to contribute the `JavaScript/CSS` files. |
|||
|
|||
> Q: The new project(`YourModuleName.Blazor.WebAssembly.Bundling`) doesn't have the `libs/myscript.js` and `libs/myscript.css` files why the files can be added to the bundling system? |
|||
|
|||
> A: Because the `MyProjectName.Client` will depend on the `MyBlazorModule(YourModuleName.Blazor)` that contains the `JavaScript/CSS` files, The `MyProjectName` is referencing the `MyProjectName.Client` project, so the `MyProjectName` project can access the `JavaScript/CSS` files in the `MyProjectName.Client` project and add them to the bundling system. |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreComponentsWebAssemblyThemingBundlingModule) |
|||
)] |
|||
public class MyBlazorWebAssemblyBundlingModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpBundlingOptions>(options => |
|||
{ |
|||
// Script Bundles |
|||
options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global).AddContributors(typeof(MyModuleBundleScriptContributor)); |
|||
|
|||
// Style Bundles |
|||
options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global).AddContributors(typeof(MyModuleBundleStyleBundleContributor)); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```csharp |
|||
public class MyModuleBundleScriptContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("_content/MyModule.Blazor/libs/myscript.js"); |
|||
} |
|||
} |
|||
|
|||
public class MyModuleBundleStyleBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("_content/MyModule.Blazor/libs/myscript.css"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Use the Global Assets in the Blazor WASM |
|||
|
|||
### MyCompanyName.MyProjectName.Blazor |
|||
|
|||
Convert your `MyCompanyName.MyProjectName.Blazor` project to integrate the `ABP module` system and depend on the `AbpAspNetCoreMvcUiBundlingModule` and `AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule/AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule`: |
|||
|
|||
* The `AbpAspNetCoreMvcUiBundlingModule` uses to create the `JavaScript/CSS` files to virtual files. |
|||
* The `AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule/AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule` uses to add theme `JavaScript/CSS` to the bundling system. |
|||
|
|||
Here is how your project files look like: |
|||
|
|||
**`Program.cs`:** |
|||
|
|||
```csharp |
|||
public class Program |
|||
{ |
|||
public async static Task<int> Main(string[] args) |
|||
{ |
|||
//... |
|||
|
|||
var builder = WebApplication.CreateBuilder(args); |
|||
builder.Host.AddAppSettingsSecretsJson() |
|||
.UseAutofac() |
|||
.UseSerilog(); |
|||
await builder.AddApplicationAsync<MyProjectNameBlazorModule>(); |
|||
var app = builder.Build(); |
|||
await app.InitializeApplicationAsync(); |
|||
await app.RunAsync(); |
|||
return 0; |
|||
|
|||
//... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**`MyProjectNameBlazorModule.cs`:** |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpAspNetCoreMvcUiBundlingModule), |
|||
typeof(AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule/AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule) //Should be added! |
|||
)] |
|||
public class MyProjectNameBlazorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//https://github.com/dotnet/aspnetcore/issues/52530 |
|||
Configure<RouteOptions>(options => |
|||
{ |
|||
options.SuppressCheckForUnhandledSecurityMetadata = true; |
|||
}); |
|||
|
|||
// Add services to the container. |
|||
context.Services.AddRazorComponents() |
|||
.AddInteractiveWebAssemblyComponents(); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var env = context.GetEnvironment(); |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
// Configure the HTTP request pipeline. |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseWebAssemblyDebugging(); |
|||
} |
|||
else |
|||
{ |
|||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. |
|||
app.UseHsts(); |
|||
} |
|||
|
|||
app.UseHttpsRedirection(); |
|||
app.MapAbpStaticAssets(); |
|||
app.UseRouting(); |
|||
app.UseAntiforgery(); |
|||
|
|||
app.UseConfiguredEndpoints(builder => |
|||
{ |
|||
builder.MapRazorComponents<App>() |
|||
.AddInteractiveWebAssemblyRenderMode() |
|||
.AddAdditionalAssemblies(WebAppAdditionalAssembliesHelper.GetAssemblies<MyProjectNameBlazorClientModule>()); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**`MyCompanyName.MyProjectName.Blazor.csproj`:** |
|||
|
|||
```xml |
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0.0" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Bundling" Version="9.0.0" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme.Bundling" Version="9.0.0" /> |
|||
<!-- <PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXTheme.Bundling" Version="9.0.0" /> --> if you're using LeptonXTheme |
|||
<ProjectReference Include="..\MyProjectName.Blazor.Client\MyProjectName.Blazor.Client.csproj" /> |
|||
</ItemGroup> |
|||
``` |
|||
|
|||
### BlazorWebAssemblyBundlingModule in the ABP commercial |
|||
|
|||
Here is the list of `Bundling Modules` in the ABP commercial. If you're using the pro template, you should add them to the `MyCompanyName.MyProjectName.Blazor` project. |
|||
|
|||
| BundlingModules | Nuget Package | |
|||
|---------------------------------------------|-----------------------------------------------------| |
|||
| AbpAuditLoggingBlazorWebAssemblyBundlingModule | Volo.Abp.AuditLogging.Blazor.WebAssembly.Bundling | |
|||
| FileManagementBlazorWebAssemblyBundlingModule | Volo.FileManagement.Blazor.WebAssembly.Bundling | |
|||
| SaasHostBlazorWebAssemblyBundlingModule | Volo.Saas.Host.Blazor.WebAssembly.Bundling | |
|||
| ChatBlazorWebAssemblyBundlingModule | Volo.Chat.Blazor.WebAssembly.Bundling | |
|||
| CmsKitProAdminBlazorWebAssemblyBundlingModule | Volo.CmsKit.Pro.Admin.Blazor.WebAssembly.Bundling | |
|||
|
|||
|
|||
### MyCompanyName.MyProjectName.Blazor.Client |
|||
|
|||
1. Remove the `global.JavaScript/CSS` files from the `MyCompanyName.MyProjectName.Blazor`'s `wwwroot` folder. |
|||
2. Remove the `AbpCli:Bundle` section from the `appsettings.json` file. |
|||
3. Remove all BundleContributor classes that inherit from IBundleContributor. Then, create `MyProjectNameStyleBundleContributor` and `MyProjectNameScriptBundleContributor` classes to add your style and JavaScript files. Finally, add them to `AbpBundlingOptions`. |
|||
|
|||
|
|||
```cs |
|||
public class MyProjectNameStyleBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.Add(new BundleFile("main.css", true)); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class MyProjectNameScriptBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.Add(new BundleFile("main.js", true)); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```cs |
|||
Configure<AbpBundlingOptions>(options => |
|||
{ |
|||
var globalStyles = options.StyleBundles.Get(BlazorWebAssemblyStandardBundles.Styles.Global); |
|||
globalStyles.AddContributors(typeof(MyProjectNameStyleBundleContributor)); |
|||
|
|||
var globalScripts = options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global); |
|||
globalScripts.AddContributors(typeof(MyProjectNameScriptBundleContributor)); |
|||
}); |
|||
``` |
|||
|
|||
## Use the Global Assets in the Blazor WebApp |
|||
|
|||
### MyCompanyName.MyProjectName.Blazor.WebApp |
|||
|
|||
Depending on the `AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule/AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule` in your `MyCompanyName.MyProjectName.Blazor.WebApp` project. |
|||
|
|||
* The `AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeBundlingModule/AbpAspNetCoreComponentsWebAssemblyLeptonXThemeBundlingModule` uses to add theme `JavaScript/CSS` to the bundling system. |
|||
|
|||
|
|||
### BlazorWebAssemblyBundlingModule in the ABP commercial |
|||
|
|||
Here is the list of `Bundling Modules` in the ABP commercial. If you're using the pro template, you should add them to the `MyCompanyName.MyProjectName.Blazor.WebApp` project. |
|||
|
|||
| BundlingModules | Nuget Package | |
|||
|---------------------------------------------|-----------------------------------------------------| |
|||
| AbpAuditLoggingBlazorWebAssemblyBundlingModule | Volo.Abp.AuditLogging.Blazor.WebAssembly.Bundling | |
|||
| FileManagementBlazorWebAssemblyBundlingModule | Volo.FileManagement.Blazor.WebAssembly.Bundling | |
|||
| SaasHostBlazorWebAssemblyBundlingModule | Volo.Saas.Host.Blazor.WebAssembly.Bundling | |
|||
| ChatBlazorWebAssemblyBundlingModule | Volo.Chat.Blazor.WebAssembly.Bundling | |
|||
| CmsKitProAdminBlazorWebAssemblyBundlingModule | Volo.CmsKit.Pro.Admin.Blazor.WebAssembly.Bundling | |
|||
|
|||
|
|||
### MyCompanyName.MyProjectName.Blazor.WebApp.Client |
|||
|
|||
1. Remove the `global.JavaScript/CSS` files from the `MyCompanyName.MyProjectName.Blazor.WebApp.Client`'s `wwwroot` folder. |
|||
2. Remove the `AbpCli:Bundle` section from the `appsettings.json` file. |
|||
3. Remove all BundleContributor classes that inherit from IBundleContributor. Then, create `MyProjectNameStyleBundleContributor` and `MyProjectNameScriptBundleContributor` classes to add your style and JavaScript files. Finally, add them to `AbpBundlingOptions`. |
|||
|
|||
```cs |
|||
public class MyProjectNameStyleBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.Add(new BundleFile("main.css", true)); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class MyProjectNameScriptBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.Add(new BundleFile("main.js", true)); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```cs |
|||
Configure<AbpBundlingOptions>(options => |
|||
{ |
|||
var globalStyles = options.StyleBundles.Get(BlazorWebAssemblyStandardBundles.Styles.Global); |
|||
globalStyles.AddContributors(typeof(MyProjectNameStyleBundleContributor)); |
|||
|
|||
var globalScripts = options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global); |
|||
globalScripts.AddContributors(typeof(MyProjectNameScriptBundleContributor)); |
|||
}); |
|||
``` |
|||
|
|||
### Check the Global Assets |
|||
|
|||
Run the `MyProject` project and check the `https://localhost/global.js` and `https://localhost/global.css` files. You should be able to see the `JavaScript/CSS` files content from the Bundling system: |
|||
|
|||
 |
|||
|
|||
## GlobalAssets(AbpBundlingGlobalAssetsOptions) |
|||
|
|||
You can configure the JavaScript and CSS file names in the `GlobalAssets` property of the `AbpBundlingOptions` class. |
|||
|
|||
The default values are `global.js` and `global.css`. |
|||
|
|||
## Conclusion |
|||
|
|||
With the new `Global Assets` feature, you can easily bundle the `JavaScript/CSS` files in the Blazor wasm app. This feature is very useful for the Blazor wasm app, and it will save you a lot of time and effort. We hope you will enjoy this feature and use it in your projects. |
|||
|
|||
## References |
|||
|
|||
* [Virtual Files](https://docs.abp.io/en/abp/latest/Virtual-Files) |
|||
* [Bundle Contributors](https://abp.io/docs/latest/framework/ui/mvc-razor-pages/bundling-minification#bundle-contributors) |
|||
* [Global Assets Pull Request](https://github.com/abpframework/abp/pull/19968) |
|||
|
|||
|
After Width: | Height: | Size: 360 KiB |
@ -0,0 +1,385 @@ |
|||
# How to Use OpenAI API with ABP Framework |
|||
|
|||
In this article, I will show you how to integrate and use the [OpenAI API](https://github.com/openai/openai-dotnet?tab=readme-ov-file#getting-started) with the [ABP Framework](https://abp.io/). We will explore step-by-step how these technologies can work together to enhance your application with powerful AI capabilities, such as natural language processing, image generation, and more. |
|||
|
|||
 |
|||
|
|||
## Creating an ABP Project |
|||
|
|||
To begin integrating OpenAI API with ABP Framework, you first need to create an ABP project. Follow these steps to create and set up your ABP project: |
|||
### Step 1: Install ABP CLI |
|||
|
|||
The ABP CLI is a command-line interface tool that helps you create and manage ABP projects easily. To install the ABP CLI, run the following command in your terminal: |
|||
|
|||
```bash |
|||
dotnet tool install -g Volo.Abp.Studio.Cli |
|||
``` |
|||
|
|||
### Step 2: Create a New ABP Project |
|||
|
|||
Once you have installed the ABP CLI, you can create a new ABP project using the following command: |
|||
|
|||
```bash |
|||
abp new Acme.OpenAIIntegration -t app --ui-framework mvc --database-provider ef -dbms PostgreSQL --csf |
|||
``` |
|||
|
|||
> This command will generate a complete ABP project with an [MVC UI](https://abp.io/docs/latest/framework/ui/mvc-razor-pages/overall). The examples provided in this article make use of UI controllers for demonstration purposes. However, the same approach can easily be applied to other UI types supported by ABP, such as Blazor or Angular. You can find other options [here](https://abp.io/docs/latest/cli). |
|||
## OpenAI Integration Setup |
|||
|
|||
To begin integrating OpenAI API with ABP Framework, follow these steps: |
|||
|
|||
### Step 1: Create an API Key |
|||
|
|||
To use the OpenAI services, you first need an API key. To obtain one, first [create a new OpenAI account](https://platform.openai.com/signup) or [log in](https://platform.openai.com/login). Next, navigate to the [API key page](https://platform.openai.com/account/api-keys) and select "Create new secret key", optionally naming the key. Make sure to save your API key somewhere safe and do not share it with anyone. |
|||
|
|||
This key will be used to authenticate your application when making requests to the OpenAI endpoints. |
|||
|
|||
### Step 2: Adding *Microsoft.Extensions.AI* Package |
|||
|
|||
To integrate OpenAI API with ABP, we use [Microsoft.Extensions.AI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/). This package offers a unified API for integrating AI services, making it easy for developers to work with different AI providers. You can find more details in [this blog post](https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/). |
|||
|
|||
To begin integrating OpenAI API with ABP Framework, follow these steps: |
|||
|
|||
1. Add the **Microsoft.Extensions.AI** and **Microsoft.Extensions.AI.OpenAI** (used to interact specifically with OpenAI services. Additionally, this package has alternatives like [Azure OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/), [Azure AI Inference](https://www.nuget.org/packages/Microsoft.Extensions.AI.AzureAIInference/), and [Ollama](https://www.nuget.org/packages/Microsoft.Extensions.AI.Ollama/), offering flexibility for developers to choose the AI provider that best fits their needs) packages: |
|||
|
|||
```bash |
|||
dotnet add package Microsoft.Extensions.AI --prerelease |
|||
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease |
|||
``` |
|||
|
|||
2. Add the required configuration to the `appsettings.json` file located inside the `Acme.OpenAIIntegration.Web` project and dependencies to your `ConfigureServices` method: |
|||
|
|||
```json |
|||
"AI": { |
|||
"OpenAI": { |
|||
"Key": "YOUR-API-KEY", |
|||
"Chat": { |
|||
"ModelId": "gpt-4o-mini" |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> Replace the value of the `Key` with your OpenAI API key. |
|||
|
|||
Next, add the following code to the `ConfigureServices` method in `OpenAIIntegrationBlazorModule`: |
|||
|
|||
```csharp |
|||
context.Services.AddSingleton(new OpenAIClient(configuration["AI:OpenAI:Key"])); |
|||
|
|||
context.Services.AddChatClient(services => |
|||
services.GetRequiredService<OpenAIClient>().AsChatClient(configuration["AI:OpenAI:Chat:ModelId"] ?? "gpt-4o-mini")); |
|||
``` |
|||
|
|||
## Creating a Sample Page |
|||
|
|||
To demonstrate the use of OpenAI API, let's create a page named `Sample` in the `Acme.OpenAIIntegration.Web` project: |
|||
|
|||
Create a `Sample` folder under the `Pages` folder of the `Acme.OpenAIIntegration.Web` project. Add a new Razor Page by right-clicking the `Sample` folder then selecting `Add > Razor Page`. Name it `Index`. |
|||
|
|||
Open the `Index.cshtml` and change the whole content as shown below: |
|||
|
|||
> Note: This example demonstrates a simple implementation of a sample page that interacts with the OpenAI API, covering chat, [retrieval-augmented generation (RAG)](https://github.com/openai/openai-dotnet?tab=readme-ov-file#how-to-use-assistants-with-retrieval-augmented-generation-rag), and image generation features. Each example is explained in detail in the next section, so feel free to continue for a better understanding of the steps and logic involved. |
|||
|
|||
```html |
|||
@page |
|||
@model Acme.OpenAIIntegration.Web.Pages.Sample |
|||
@{ |
|||
ViewData["Title"] = "OpenAI API Demonstration"; |
|||
} |
|||
|
|||
<h1>@ViewData["Title"]</h1> |
|||
|
|||
<br/><br/> |
|||
|
|||
<div class="row"> |
|||
<div class="col-md-4"> |
|||
<h2>Chat Example</h2> |
|||
<form method="post" asp-page-handler="Chat"> |
|||
<div class="form-group"> |
|||
<label asp-for="ChatInput">Enter your message:</label> |
|||
<textarea asp-for="ChatInput" class="form-control" rows="4"></textarea> |
|||
</div> |
|||
<button type="submit" class="btn btn-primary mt-2">Send</button> |
|||
</form> |
|||
@if (!string.IsNullOrEmpty(Model.ChatResponse)) |
|||
{ |
|||
<h3 class="mt-3">Response:</h3> |
|||
<p>@Model.ChatResponse</p> |
|||
} |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
<h2>RAG Example</h2> |
|||
<form method="post" asp-page-handler="RAG"> |
|||
<div class="form-group mt-2"> |
|||
<label asp-for="RAGQuery">Query:</label> |
|||
<input asp-for="RAGQuery" class="form-control" /> |
|||
</div> |
|||
<button type="submit" class="btn btn-primary mt-2">Ask</button> |
|||
</form> |
|||
@if (!string.IsNullOrEmpty(Model.RAGResponse)) |
|||
{ |
|||
<h3 class="mt-3">Result:</h3> |
|||
<p>@Model.RAGResponse</p> |
|||
} |
|||
</div> |
|||
|
|||
<div class="col-md-4"> |
|||
<h2>Image Generation Example</h2> |
|||
<form method="post" asp-page-handler="ImageGeneration"> |
|||
<div class="form-group"> |
|||
<label asp-for="ImagePrompt">Image Description:</label> |
|||
<input asp-for="ImagePrompt" class="form-control" /> |
|||
</div> |
|||
<button type="submit" class="btn btn-primary mt-2">Generate Image</button> |
|||
</form> |
|||
@if (Model.GeneratedImageBytes != null) |
|||
{ |
|||
<h3 class="mt-3">Generated Image:</h3> |
|||
<img src="data:image/png;base64,@Convert.ToBase64String(Model.GeneratedImageBytes)" alt="Generated image" class="img-fluid mt-2" /> |
|||
} |
|||
</div> |
|||
</div> |
|||
``` |
|||
|
|||
`Index.cshtml.cs` content should be like that: |
|||
|
|||
```csharp |
|||
using System; |
|||
using System.ClientModel; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Microsoft.Extensions.AI; |
|||
using OpenAI; |
|||
using OpenAI.Assistants; |
|||
using OpenAI.Files; |
|||
using OpenAI.Images; |
|||
|
|||
namespace Acme.OpenAIIntegration.Web.Pages; |
|||
|
|||
public class Sample : PageModel |
|||
{ |
|||
[BindProperty] |
|||
public string ChatInput { get; set; } |
|||
public string ChatResponse { get; set; } |
|||
|
|||
[BindProperty] |
|||
public string RAGQuery { get; set; } |
|||
public string RAGResponse { get; set; } |
|||
|
|||
[BindProperty] |
|||
public string ImagePrompt { get; set; } |
|||
public byte[] GeneratedImageBytes { get; set; } |
|||
|
|||
private readonly IChatClient _chatClient; |
|||
private readonly OpenAIClient _openAiClient; |
|||
|
|||
public Sample( |
|||
IChatClient chatClient, |
|||
OpenAIClient openAiClient) |
|||
{ |
|||
_chatClient = chatClient; |
|||
_openAiClient = openAiClient; |
|||
} |
|||
|
|||
public async Task<IActionResult> OnPostChatAsync() |
|||
{ |
|||
ChatResponse = $"Chat response: {(await _chatClient.CompleteAsync(ChatInput)).Message}"; |
|||
return Page(); |
|||
} |
|||
|
|||
public async Task<IActionResult> OnPostRAGAsync() |
|||
{ |
|||
#pragma warning disable OPENAI001 |
|||
var fileClient = _openAiClient.GetOpenAIFileClient(); |
|||
var assistantClient = _openAiClient.GetAssistantClient(); |
|||
|
|||
using var document = BinaryData.FromBytes(GetExceptionHandlingDocumentContent().ToArray()).ToStream(); |
|||
var exceptionHandlingDoc = await fileClient.UploadFileAsync( |
|||
document, |
|||
"ExceptionHandling.md", |
|||
FileUploadPurpose.Assistants); |
|||
|
|||
AssistantCreationOptions assistantOptions = new() |
|||
{ |
|||
Name = "Exception Handling Assistant", |
|||
Instructions = |
|||
""" |
|||
This assistant helps you with exception handling in ABP Framework. You can ask questions about exception handling and get answers. |
|||
|
|||
- Do not make any assumptions when asked for information that is not in the document |
|||
- Give the most accurate information possible |
|||
- Give short(max 1-2 sentence) and concise answers |
|||
- Do not provide file citations |
|||
""", |
|||
|
|||
Tools = |
|||
{ |
|||
new FileSearchToolDefinition(), |
|||
}, |
|||
ToolResources = new() |
|||
{ |
|||
FileSearch = new() |
|||
{ |
|||
NewVectorStores = |
|||
{ |
|||
new VectorStoreCreationHelper([exceptionHandlingDoc.Value.Id]), |
|||
} |
|||
} |
|||
}, |
|||
}; |
|||
|
|||
var assistant = await assistantClient.CreateAssistantAsync("gpt-4o", assistantOptions); |
|||
|
|||
ThreadCreationOptions threadOptions = new() |
|||
{ |
|||
InitialMessages = { RAGQuery } |
|||
}; |
|||
|
|||
ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Value.Id, threadOptions); |
|||
|
|||
do |
|||
{ |
|||
Thread.Sleep(TimeSpan.FromSeconds(1)); |
|||
threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id); |
|||
} while (!threadRun.Status.IsTerminal); |
|||
|
|||
CollectionResult<ThreadMessage> messages |
|||
= assistantClient.GetMessages(threadRun.ThreadId, |
|||
new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending }); |
|||
|
|||
var response = new StringBuilder(); |
|||
|
|||
foreach (var message in messages) |
|||
{ |
|||
response.AppendLine($"[{message.Role.ToString().ToUpper()}]: "); |
|||
foreach (var contentItem in message.Content) |
|||
{ |
|||
if (!string.IsNullOrEmpty(contentItem.Text)) |
|||
{ |
|||
response.AppendLine(contentItem.Text); |
|||
|
|||
if (contentItem.TextAnnotations.Count > 0) |
|||
{ |
|||
response.AppendLine(""); |
|||
} |
|||
} |
|||
} |
|||
|
|||
response.AppendLine(""); |
|||
#pragma warning restore OPENAI001 |
|||
} |
|||
|
|||
RAGResponse = response.ToString(); |
|||
|
|||
return Page(); |
|||
} |
|||
|
|||
public async Task<IActionResult> OnPostImageGenerationAsync() |
|||
{ |
|||
var client = _openAiClient.GetImageClient("dall-e-3"); |
|||
|
|||
var image = await client.GenerateImageAsync(ImagePrompt, new ImageGenerationOptions |
|||
{ |
|||
ResponseFormat = GeneratedImageFormat.Bytes |
|||
}); |
|||
|
|||
var imageBytes = image.Value.ImageBytes; |
|||
|
|||
using var memoryStream = new MemoryStream(); |
|||
await imageBytes.ToStream().CopyToAsync(memoryStream); |
|||
GeneratedImageBytes = memoryStream.ToArray(); |
|||
|
|||
return Page(); |
|||
} |
|||
|
|||
public ReadOnlySpan<byte> GetExceptionHandlingDocumentContent() |
|||
{ |
|||
return """ |
|||
# Exception Handling |
|||
|
|||
ABP provides a built-in infrastructure and offers a standard model for handling exceptions. |
|||
|
|||
* Automatically **handles all exceptions** and sends a standard **formatted error message** to the client for an API/AJAX request. |
|||
* Automatically hides **internal infrastructure errors** and returns a standard error message. |
|||
* Provides an easy and configurable way to **localize** exception messages. |
|||
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map custom exceptions. |
|||
|
|||
## Automatic Exception Handling |
|||
|
|||
`AbpExceptionFilter` handles an exception if **any of the following conditions** are met: |
|||
|
|||
* Exception is thrown by a **controller action** which returns an **object result** (not a view result). |
|||
* The request is an AJAX request (`X-Requested-With` HTTP header value is `XMLHttpRequest`). |
|||
* Client explicitly accepts the `application/json` content type (via `accept` HTTP header). |
|||
|
|||
If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client. |
|||
|
|||
## Business Exceptions |
|||
|
|||
Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception. |
|||
|
|||
`BusinessException` implements the `IBusinessException` interface in addition to the `IHasErrorCode`, `IHasErrorDetails` and `IHasLogLevel` interfaces. The default log level is `Warning`. |
|||
|
|||
Usually you have an error code related to a particular business exception. For example: |
|||
|
|||
````C# |
|||
throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer); |
|||
```` |
|||
|
|||
### User Friendly Exception |
|||
|
|||
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client. |
|||
|
|||
`UserFriendlyException` class is the built-in implementation of the `IUserFriendlyException` interface. Example usage: |
|||
|
|||
````C# |
|||
throw new UserFriendlyException( |
|||
"Username should be unique!" |
|||
); |
|||
```` |
|||
* The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class. |
|||
|
|||
"""u8; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Running the Application |
|||
|
|||
After completing the setup, you can run the application using the following command: |
|||
|
|||
```bash |
|||
dotnet run --project ./src/Acme.OpenAIIntegration.Web |
|||
``` |
|||
|
|||
Once the application is running, open your browser and navigate to `/Sample`. You should see the `Sample` page we created, which contains sections for Chat, RAG (Retrieval-Augmented Generation), and Image Generation. You can find the screenshot of the page below: |
|||
|
|||
 |
|||
|
|||
## Examples Overview |
|||
|
|||
To showcase the integration of the OpenAI API with the ABP Framework, we implemented three different examples: |
|||
|
|||
1. **Chat Example**: This example demonstrates how to use OpenAI's chat capabilities by allowing users to enter a message and receive an AI-generated response. The implementation involves setting up a simple form on the `Sample` page where users can input their message. The form submission triggers the `OnPostChatAsync` method, which uses the `IChatClient` to generate a response. |
|||
|
|||
 |
|||
|
|||
2. **Retrieval-Augmented Generation (RAG) Example**: In this example, we use OpenAI to answer user queries by referencing custom documents uploaded to the OpenAI API. The implementation involves uploading a document using the `OpenAIFileClient` and creating an assistant with specific instructions to handle the uploaded content. In this case, the document is a section from ABP's Exception Handling documentation, which includes examples on how ABP handles exceptions, user-friendly error messages, and business exceptions. Users can input their query on the `Sample` page, and the `OnPostRAGAsync` method processes the query to generate precise answers based on the document content. If users ask questions that are not covered in the document, the assistant clearly indicates that the information is not available, as per the instructions provided. For example, when asked about `Object Extensions`, the response begins with: "The uploaded document does not contain information about `Object Extensions`...". This demonstrates how the assistant adheres to the provided instructions. You can also find this example illustrated in the GIF below. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
3. **Image Generation Example**: This example leverages the [DALL-E](https://openai.com/index/dall-e-3/) model to generate images based on user-provided prompts. On the `Sample` page, users can provide a description of the image they want to generate, and the `OnPostImageGenerationAsync` method uses the `OpenAIClient` to generate the image. |
|||
|
|||
 |
|||
|
|||
## Conclusion |
|||
|
|||
In this article, we covered how to integrate the OpenAI API with the ABP Framework by creating a sample project, setting up the OpenAI services, and implementing examples for conversational AI, knowledge-based assistance, and image generation. By following these steps, you can add powerful AI-driven capabilities to your application, making it more interactive, intelligent, and capable of meeting user needs effectively. |
|||
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 565 KiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 49 KiB |
@ -0,0 +1,234 @@ |
|||
# The new Unit Test structure in ABP application |
|||
|
|||
A typical ABP modular project usually consists of three main projects: `Application`, `Domain`, and `EntityFrameworkCore/MongoDB`. In these projects, we may provide many services that require unit testing. |
|||
|
|||
Using abstract unit test classes involves first writing tests in the `Application` and `Domain` layers that are independent of the storage technology, ensuring the correctness of core business logic. These abstract tests are then implemented in `EntityFrameworkCore` or `MongoDB`. The benefits of this approach include: |
|||
|
|||
1. **Reduced Coupling**: Core logic tests do not depend on specific storage technologies, so switching databases does not require rewriting test code. |
|||
2. **Better Isolation**: Focuses on verifying business logic correctness, avoiding interference from database operations. |
|||
3. **Increased Reusability**: The same abstract tests can be reused with different storage implementations. |
|||
4. **Easier Maintenance and Extensibility**: Different storage implementations can be extended independently without breaking existing tests. |
|||
5. **Faster and More Reliable Tests**: Reduces dependency on databases, making tests faster and more stable. |
|||
|
|||
## How to migrate old unit tests to the new unit test structure |
|||
|
|||
Assume our project name is `MyCompanyName.MyProjectName`. |
|||
|
|||
### Changes to the `MyCompanyName.MyProjectName.Application.Tests` project: |
|||
|
|||
1. Remove the `MyCompanyName.MyProjectName.Application.Tests` project's `MyProjectNameApplicationCollection` class. |
|||
2. Modify the `MyCompanyName.MyProjectName.Application.Tests` project's `MyProjectNameApplicationTestBase` class. |
|||
|
|||
```csharp |
|||
public abstract class MyProjectNameApplicationTestBase<TStartupModule> : MyProjectNameTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
3. Modify the `MyCompanyName.MyProjectName.Application.Tests` project's unit test classes to become abstract unit test classes, such as: `SampleAppServiceTests`. |
|||
|
|||
```csharp |
|||
public abstract class SampleAppServiceTests<TStartupModule> : MyProjectNameApplicationTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
[Fact] |
|||
public async Task Initial_Data_Should_Contain_Admin_User() |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Changes to the `MyCompanyName.MyProjectName.Domain.Tests` project: |
|||
|
|||
1. Remove the `MyCompanyName.MyProjectName.Domain.Tests` project's `MyProjectNameDomainCollection` class. |
|||
2. Modify the `MyCompanyName.MyProjectName.Domain.Tests` project's `MyProjectNameDomainTestBase` class. |
|||
|
|||
```csharp |
|||
public abstract class MyProjectNameDomainTestBase<TStartupModule> : MyProjectNameTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
3. Modify the `MyCompanyName.MyProjectName.Domain.Tests` project's unit test classes to become abstract unit test classes, such as: `SampleDomainTests`. |
|||
|
|||
```csharp |
|||
public abstract class SampleDomainTests<TStartupModule> : MyProjectNameDomainTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Set_Email_Of_A_User() |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
4. Modify the `MyCompanyName.MyProjectName.Domain.Tests` project's `csproj` and module class. Remove references to `EntityFrameworkCore/MongoDB`. |
|||
|
|||
`MyCompanyName.MyProjectName.Domain.Tests.csproj`: |
|||
```xml |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
//... |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.Domain\MyCompanyName.MyProjectName.Domain.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.TestBase\MyCompanyName.MyProjectName.TestBase.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
``` |
|||
|
|||
`MyProjectNameDomainTestModule.cs`: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(MyProjectNameDomainModule), |
|||
typeof(MyProjectNameTestBaseModule) |
|||
)] |
|||
public class MyProjectNameDomainTestModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### Changes to the `MyCompanyName.MyProjectName.EntityFrameworkCore.Tests` project: |
|||
|
|||
Here, we need to create implementation classes for all abstract unit tests. |
|||
|
|||
```csharp |
|||
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)] |
|||
public class EfCoreSampleAppServiceTests : SampleAppServiceTests<MyProjectNameEntityFrameworkCoreTestModule> |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
```csharp |
|||
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)] |
|||
public class EfCoreSampleDomainTests : SampleDomainTests<MyProjectNameEntityFrameworkCoreTestModule> |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
We also need to modify the project's dependencies and module class, which should directly or indirectly reference the `Application` and `Domain` test projects. |
|||
|
|||
`MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj`: |
|||
|
|||
```xml |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
//... |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.EntityFrameworkCore\MyCompanyName.MyProjectName.EntityFrameworkCore.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" /> |
|||
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
``` |
|||
|
|||
`MyProjectNameEntityFrameworkCoreTestModule.cs`: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(MyProjectNameApplicationTestModule), |
|||
typeof(MyProjectNameEntityFrameworkCoreModule), |
|||
typeof(AbpEntityFrameworkCoreSqliteModule) |
|||
)] |
|||
public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### Changes to the `MyCompanyName.MyProjectName.MongoDB.Tests` project (skip this step if not using MongoDB): |
|||
|
|||
Like the `EntityFrameworkCore` project, we need to create implementation classes for all abstract unit tests and modify the project's dependencies and module class. |
|||
|
|||
```csharp |
|||
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)] |
|||
public class MongoDBSampleAppServiceTests : SampleAppServiceTests<MyProjectNameMongoDbTestModule> |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
```csharp |
|||
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)] |
|||
public class MongoDBSampleDomainTests : SampleDomainTests<MyProjectNameMongoDbTestModule> |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
```xml |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
//... |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
``` |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(MyProjectNameApplicationTestModule), |
|||
typeof(MyProjectNameMongoDbModule) |
|||
)] |
|||
public class MyProjectNameMongoDbTestModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### Changes to the `MyCompanyName.MyProjectName.Web.Tests` project: |
|||
|
|||
We need to reference the `EntityFrameworkCore/MongoDB` test projects in this test project. |
|||
|
|||
```xml |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
//... |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" /> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.Web\MyCompanyName.MyProjectName.Web.csproj" /> |
|||
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
``` |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreTestBaseModule), |
|||
typeof(MyProjectNameWebModule), |
|||
typeof(MyProjectNameApplicationTestModule), |
|||
typeof(MyProjectNameEntityFrameworkCoreTestModule) |
|||
)] |
|||
public class MyProjectNameWebTestModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
We no longer need the `MyProjectNameWebCollection` class in this project. Please delete it and use `[Collection(MyProjectNameTestConsts.CollectionDefinitionName)]` instead. |
|||
|
|||
## Conclusion |
|||
|
|||
This is our new unit test structure. Decoupling unit tests from storage technologies ensures the independence of business logic and allows easy switching between storage implementations. Abstract unit test classes improve test reusability, maintainability, and efficiency, reducing refactoring costs and providing flexibility for future tech updates. |
|||
|
|||
## References |
|||
|
|||
- [Unit Test](https://abp.io/docs/latest/testing/unit-tests) |
|||
- [Abstract all db-related unit tests](https://github.com/abpframework/abp/pull/17880) |
|||
@ -1,83 +1,72 @@ |
|||
# Blazor UI: Managing Global Scripts & Styles |
|||
|
|||
Some modules may require additional styles or scripts that need to be referenced in **index.html** file. It's not easy to find and update these types of references in Blazor apps. ABP offers a simple, powerful, and modular way to manage global style and scripts in Blazor apps. |
|||
You can add your JavaScript and CSS files from your modules or applications to the Blazor global assets system. All the JavaScript and CSS files will be added to the `global.js` and `global.css` files. You can access these files via the following URL in a Blazor WASM project: |
|||
|
|||
To update script & style references without worrying about dependencies, ordering, etc in a project, you can use the [bundle command](../../../cli#bundle). |
|||
- https://localhost/global.js |
|||
- https://localhost/global.css |
|||
|
|||
You can also add custom styles and scripts and let ABP manage them for you. In your Blazor project, you can create a class implementing `IBundleContributor` interface. |
|||
## Add JavaScript and CSS to the global assets system in the module |
|||
|
|||
`IBundleContributor` interface contains two methods. |
|||
Your module project solution will have two related Blazor projects: |
|||
|
|||
* `AddScripts(...)` |
|||
* `AddStyles(...)` |
|||
* `MyModule.Blazor`:This project includes the JavaScript/CSS files required for your Blazor components. The `MyApp.Blazor.Client (Blazor WASM)` project will reference this project. |
|||
* `MyModule.Blazor.WebAssembly.Bundling`:This project is used to add your JavaScript/CSS files to the Blazor global resources. The `MyModule.Blazor (ASP.NET Core)` project will reference this project. |
|||
|
|||
Both methods get `BundleContext` as a parameter. You can add scripts and styles to the `BundleContext` and run [bundle command](../../../cli#bundle). Bundle command detects custom styles and scripts with module dependencies and updates `index.html` file. |
|||
You need to define JavaScript and CSS contributor classes in the `MyModule.Blazor.WebAssembly.Bundling` project to add the files to the global assets system. |
|||
|
|||
## Example Usage |
|||
```csharp |
|||
namespace MyProject.Blazor |
|||
> Please use `BlazorWebAssemblyStandardBundles.Scripts.Global` and `BlazorWebAssemblyStandardBundles.Styles.Global` for the bundle name. |
|||
|
|||
```cs |
|||
public class MyModuleBundleScriptContributor : BundleContributor |
|||
{ |
|||
public class MyProjectBundleContributor : IBundleContributor |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
public void AddScripts(BundleContext context) |
|||
{ |
|||
context.Add("site.js"); |
|||
} |
|||
|
|||
public void AddStyles(BundleContext context) |
|||
{ |
|||
context.Add("main.css"); |
|||
context.Add("custom-styles.css"); |
|||
} |
|||
context.Files.AddIfNotContains("_content/MyModule.Blazor/libs/myscript.js"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> There is a BundleContributor class implementing `IBundleContributor` interface coming by default with the startup templates. So, most of the time, you don't need to add it manually. |
|||
|
|||
## Bundling And Minification |
|||
`abp bundle` command offers bundling and minification support for client-side resources(JavaScript and CSS files). `abp bundle` command reads the `appsettings.json` file inside the Blazor project and bundles the resources according to the configuration. You can find the bundle configurations inside `AbpCli.Bundle` element. |
|||
|
|||
Here are the options that you can control inside the `appsettings.json` file. |
|||
|
|||
`Mode`: Bundling and minification mode. Possible values are |
|||
* `BundleAndMinify`: Bundle all the files into a single file and minify the content. |
|||
* `Bundle`: Bundle all files into a single file, but not minify. |
|||
* `None`: Add files individually, do not bundle. |
|||
|
|||
`Name`: Bundle file name. Default value is `global`. |
|||
|
|||
`Parameters`: You can define additional key/value pair parameters inside this section. `abp bundle` command automatically sends these parameters to the bundle contributors, and you can check these parameters inside the bundle contributor, take some actions according to these values. |
|||
|
|||
Let's say that you want to exclude some resources from the bundle and control this action using the bundle parameters. You can add a parameter to the bundle section like below. |
|||
|
|||
```json |
|||
"AbpCli": { |
|||
"Bundle": { |
|||
"Mode": "BundleAndMinify", /* Options: None, Bundle, BundleAndMinify */ |
|||
"Name": "global", |
|||
"Parameters": { |
|||
"ExcludeThemeFromBundle":"true" |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
You can check this parameter and take action like below. |
|||
|
|||
```csharp |
|||
public class MyProjectNameBundleContributor : IBundleContributor |
|||
```cs |
|||
public class MyModuleBundleStyleContributor : BundleContributor |
|||
{ |
|||
public void AddScripts(BundleContext context) |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("_content/MyModule.Blazor/libs/mystyle.css"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
public void AddStyles(BundleContext context) |
|||
```cs |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreComponentsWebAssemblyThemingBundlingModule) |
|||
)] |
|||
public class MyBlazorWebAssemblyBundlingModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var excludeThemeFromBundle = bool.Parse(context.Parameters.GetValueOrDefault("ExcludeThemeFromBundle")); |
|||
context.Add("mytheme.css", excludeFromBundle: excludeThemeFromBundle); |
|||
context.Add("main.css"); |
|||
Configure<AbpBundlingOptions>(options => |
|||
{ |
|||
// Add script bundle |
|||
options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global) |
|||
.AddContributors(typeof(MyModuleBundleScriptContributor)); |
|||
|
|||
// Add style bundle |
|||
options.StyleBundles.Get(BlazorWebAssemblyStandardBundles.Styles.Global) |
|||
.AddContributors(typeof(MyModuleBundleStyleContributor)); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Add JavaScript and CSS to the global assets system in the application |
|||
|
|||
This is similar to the module. You need to define JavaScript and CSS contributor classes in the `MyApp.Blazor.Client` project to add the files to the global assets system. |
|||
|
|||
## AbpBundlingGlobalAssetsOptions |
|||
|
|||
You can configure the JavaScript and CSS file names in the `GlobalAssets` property of the `AbpBundlingOptions` class. The default values are `global.js` and `global.css`. |
|||
|
|||
## Reference |
|||
|
|||
- [ASP.NET Core MVC Bundling & Minification](../mvc-razor-pages/bundling-minification#bundle-contributorsg) |
|||
- [ABP Global Assets - New way to bundle JavaScript/CSS files in Blazor WebAssembly app](https://github.com/abpframework/abp/blob/dev/docs/en/Community-Articles/2024-11-25-Global-Assets/POST.md) |
|||
|
|||
|
Before Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 62 KiB |
@ -0,0 +1,107 @@ |
|||
# Prerequisites for Developing ABP Applications |
|||
|
|||
This document will guide you through preparing your development environment for ABP based application development. |
|||
|
|||
## Notices |
|||
|
|||
The prerequisites mentioned in this document are not necessary for every project type; |
|||
|
|||
* You don't need to install the EF Core CLI if your application uses MongoDB instead of EF Core. |
|||
* You don't need to install Helm, NGINX Ingress, or mkcert if you are developing a non-microservice application. |
|||
|
|||
`README.MD` files in new solutions contain specific requirements for your solution. Please refer to the `README.MD` file of your solution. |
|||
|
|||
## IDE |
|||
|
|||
You need to use an IDE that supports .NET development. The following IDEs are the most popular ones for .NET development. |
|||
|
|||
### Visual Studio |
|||
|
|||
Visual Studio is Microsoft's IDE and is the de facto tool for developing .NET projects. You can download Visual Studio from the [Visual Studio official website](https://visualstudio.microsoft.com/). It also has a **free Community edition** which is more than enough for ABP projects. |
|||
|
|||
### Visual Studio Code |
|||
|
|||
Visual Studio Code is a **free and cross-platform** lightweight code editor that supports .NET development. You can [download from here](https://code.visualstudio.com/download). |
|||
|
|||
### JetBrains Rider |
|||
|
|||
[JetBrains Rider](https://www.jetbrains.com/rider/download) is a cross-platform IDE by [JetBrains](https://www.jetbrains.com/) that supports .NET development. It is **[free for non-commercial use](https://blog.jetbrains.com/blog/2024/10/24/webstorm-and-rider-are-now-free-for-non-commercial-use/)**. |
|||
|
|||
## .NET SDK |
|||
|
|||
ABP is based on NET, so you need to install the .NET SDK. You can download the .NET SDK from the [.NET official website](https://dotnet.microsoft.com/en-us/download/dotnet/9.0). |
|||
|
|||
> Installing Visual Studio or JetBrains Rider may automatically install the .NET SDK. |
|||
|
|||
### EF Core CLI |
|||
|
|||
If you are using [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/) as your database access provider, you need to install the [EF Core CLI](https://learn.microsoft.com/en-us/ef/core/cli/dotnet). You can install it by running the following command: |
|||
|
|||
```bash |
|||
dotnet tool install --global dotnet-ef |
|||
``` |
|||
|
|||
If you have already installed the `EF Core CLI`, you can update it by running the following command: |
|||
|
|||
```bash |
|||
dotnet tool update --global dotnet-ef |
|||
``` |
|||
|
|||
## Node.js and Yarn |
|||
|
|||
ABP projects include some frontend resource packages, so you need to install Node.js and Yarn to manage these resource packages. You can download Node.js from the [official Node.js website](https://nodejs.org/). We recommend installing version v20.11+. |
|||
|
|||
Using Yarn (classic) to manage frontend resource packages is faster and more stable than using npm. You can download `Yarn` from the [Yarn official website](https://classic.yarnpkg.com/en/docs/install). We recommend installing Yarn v1.22+ (make sure to install the Classic version, not v2+). |
|||
|
|||
To install Yarn using npm, run the following command: |
|||
|
|||
```bash |
|||
npm install --global yarn |
|||
``` |
|||
|
|||
## Docker Engine or Docker Desktop |
|||
|
|||
ABP's [Layered Solution](../solution-templates/layered-web-application/index.md) and [Microservice Solution](../solution-templates/microservice/index.md) use Docker to run infrastructure services (e.g. SQL Server, Redis, RabbitMQ) required by your application. You can install Docker Engine or Docker Desktop (recommended) on Windows, macOS and Linux. |
|||
|
|||
* [Docker Desktop](https://www.docker.com/products/docker-desktop/) (recommended) |
|||
* [Docker Engine](https://docs.docker.com/engine/install/) |
|||
|
|||
### Is Docker Engine or Docker Desktop Free? |
|||
|
|||
Docker Engine is an open-source and free containerization technology for building and containerizing your applications. [`Docker Engine` follows the Apache License 2.0](https://docs.docker.com/engine/#licensing). |
|||
|
|||
Docker Desktop is free [for small businesses (fewer than 250 employees and less than $10 million in annual revenue), personal use, education, and non-commercial open-source projects](https://docs.docker.com/subscription/desktop-license/). |
|||
|
|||
## PowerShell |
|||
|
|||
ABP startup solution templates and tools use some PowerShell scripts (`*.ps1`) to perform certain tasks. You can refer to the [PowerShell documentation](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell) for guidance on how to install PowerShell on Windows, macOS, and Linux. |
|||
|
|||
* [Install PowerShell on Windows](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows) |
|||
* [Install PowerShell on macOS](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos) |
|||
* [Install PowerShell on Linux](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux) |
|||
|
|||
## MicroService Solution |
|||
|
|||
The following tools are only required to develop ABP's [microservice solution](../solution-templates/microservice/index.md) |
|||
|
|||
### Helm |
|||
|
|||
[Helm](https://helm.sh/) is a package manager for Kubernetes. You can install Helm by following the [Helm installation guide](https://helm.sh/docs/intro/install/). |
|||
|
|||
See [Helm Deployment on Local Kubernetes Cluster](../solution-templates/microservice/helm-charts-and-kubernetes.md) for more information. |
|||
|
|||
### NGINX Ingress or NGINX Ingress using Helm |
|||
|
|||
[NGINX Ingress](https://kubernetes.github.io/ingress-nginx/deploy/) is an Ingress controller for Kubernetes. You can install NGINX Ingress by following the [NGINX Ingress installation guide](https://kubernetes.github.io/ingress-nginx/deploy/). |
|||
|
|||
If you are using Helm, you can install NGINX Ingress using the following commands: |
|||
|
|||
```cs |
|||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx |
|||
helm repo update |
|||
helm upgrade --install --version=4.0.19 ingress-nginx ingress-nginx/ingress-nginx |
|||
``` |
|||
|
|||
### mkcert |
|||
|
|||
Use mkcert to generate trusted certificates for local development. You can install mkcert by following the [official mkcert installation guide](https://github.com/FiloSottile/mkcert#installation). |
|||
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 82 KiB |
@ -0,0 +1,5 @@ |
|||
# KB#0003: Can not login with the admin user |
|||
|
|||
* Try username `admin` and Password `1q2w3E*`. |
|||
* Try to migrate database. If you have a `DbMigrator` application in your solution, use it. It will seed initial data and create the admin user for you. |
|||
* If not works, read the README.MD file in your solution, or check the [Getting Started](https://abp.io/docs/latest/get-started) document. |
|||
@ -0,0 +1,7 @@ |
|||
# ABP Knowledge Base |
|||
|
|||
The following documents provide useful information about several topics you might need to know. |
|||
|
|||
* KB#0001: ["Filename too long" error on Windows](windows-path-too-long-fix.md) |
|||
* KB#0002: [When to use a distributed cache server](when-to-use-a-distributed-cache-server.md) |
|||
* KB#0003: [Can not login with the admin user](can-not-login-with-admin-user.md) |
|||
@ -0,0 +1,49 @@ |
|||
# KB#0002: When to Use a Distributed Cache Server |
|||
|
|||
ABP provides a [distributed cache service](../framework/fundamentals/caching.md) that is based on [ASP.NET Core's distributed cache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). This document explains when you need to have a separate cache server for your applications. |
|||
|
|||
## Understanding the Default Cache Service |
|||
|
|||
**Default implementation of the cache service works in-memory**. Memory cache is only useful if you are building a monolith application and you run a single instance of your application. For other cases, **you should use a real distributed cache server**. |
|||
|
|||
Here are a few example cases where you should use a distributed cache server: |
|||
|
|||
* You have a **monolith application**, but you run **multiple instances** of that application concurrently, for example, in a [clustered environment](../deployment/clustered-environment.md) |
|||
* You build a **microservice** or any kind of **distributed** system |
|||
* You have web **multiple applications** in your solution and they should share the same cache |
|||
|
|||
The problem is obvious: If each application instance uses its internal in-memory cache, and if two or more applications cache the same data, it is probable that they will cache different copies of the data. In that case, there is no way to **invalidate/refresh** that data in every application's memory when the data changes. |
|||
|
|||
## What is a Distributed Cache Server |
|||
|
|||
A **distributed cache server** (e.g. [Redis](../framework/fundamentals/redis-cache.md)) stores cache objects in a separate server application and allows multiple applications/processes to share the same cache objects. In that way; |
|||
|
|||
* All applications/services and all their instances use the same cache store and share the same cached objects. Once an application instance refreshes a cached object, all others use the new object. |
|||
* Even if your applications stop and restart, the cached objects are not lost, since they are managed by a separate cache server. |
|||
|
|||
## How to Use a Distributed Cache Server |
|||
|
|||
ABP [solution templates](../solution-templates/index.md) come with Redis configured when it is certainly necessary. For example; |
|||
|
|||
* The [microservice startup template](../solution-templates/microservice/index.md) always comes with [Redis configured](../solution-templates/microservice/distributed-cache.md) and also included as a docker container. |
|||
|
|||
* The application startup template comes with Redis configured when you select multiple applications, tiered architecture, or some other configuration that requires a distributed cache server. |
|||
|
|||
In other cases, to keep the dependencies minimal, they come with the default (in-memory) cache configuration. In those cases, if you need a distributed cache server, you should manually switch to a distributed cache provider for your application. |
|||
|
|||
See the *[Redis Cache](../framework/fundamentals/redis-cache.md)* document if you need to use Redis as the distributed cache server. See [ASP.NET Core's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) to see how to switch to another cache provider. |
|||
|
|||
### Installing a Redis Server to Your Local Environment |
|||
|
|||
If you want to use Redis as your distributed cache provider in your development environment, you can simply use the [official Redis docker image](https://hub.docker.com/_/redis). Once you have [Docker](https://www.docker.com/products/docker-desktop/) in your local machine, you can use the following command to run a Redis container and map the default Redis port: |
|||
|
|||
````bash |
|||
docker run -p 6379:6379 --name RedisServer -d redis |
|||
```` |
|||
|
|||
You can check the [official Redis docker image](https://hub.docker.com/_/redis) document for more options. |
|||
|
|||
## See Also |
|||
|
|||
* [ABP Distributed Cache](../framework/fundamentals/caching.md) |
|||
* [ASP.NET Core Distributed Cache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) |
|||
@ -0,0 +1,19 @@ |
|||
# Idle Session Timeout |
|||
|
|||
The `Idle Session Timeout` feature allows you to automatically log out users after a certain period of inactivity. |
|||
|
|||
## Configure Idle Session Timeout |
|||
|
|||
You can enable/disable the `Idle Session Timeout` feature in the `Setting > Account > Idle Session Timeout` page. |
|||
|
|||
The default idle session timeout is 1 hour. You can change it by selecting a different value from the dropdown list or entering a custom value(in minutes). |
|||
|
|||
 |
|||
|
|||
Once the idle session timeout is reached, the user will see a warning modal before being logged out. if user does not respond for 60 seconds, the user will be logged out automatically. |
|||
|
|||
 |
|||
|
|||
## How it works |
|||
|
|||
There is JavaScript code running in the background to detect user activity. such as mouse movement, key press, click, etc. If there is no activity detected for setting time, The warning modal will be shown to the user. |
|||
@ -0,0 +1,28 @@ |
|||
# OpenIddict 5.x to 6.x Migration Guide |
|||
|
|||
The 6.0 release of OpenIddict is a major release that introduces breaking changes. |
|||
|
|||
Check this blog [OpenIddict 6.0 general availability](https://kevinchalet.com/2024/12/17/openiddict-6-0-general-availability/) for the new features introduced in OpenIddict 6.0. and the [Migrate to OpenIddict 6.0](https://documentation.openiddict.com/guides/migration/50-to-60) for more information about the changes. |
|||
|
|||
In this guide, we will explain the changes you need to make to your ABP application. |
|||
|
|||
## Constant changes |
|||
|
|||
The following constants have been renamed: |
|||
|
|||
| Old Constant Name | New Constant Name | |
|||
|---------------------------------------------------------------|-----------------------------------------------------------------| |
|||
| `OpenIddictConstants.Permissions.Endpoints.Logout` | `OpenIddictConstants.Permissions.Endpoints.EndSession` | |
|||
| `OpenIddictConstants.Permissions.Endpoints.Device` | `OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization` | |
|||
|
|||
|
|||
## IdentityModel packages |
|||
|
|||
If you have a reference to `IdentityModel` directly, please upgrade the necessary package versions to the latest stable version, which is currently 8.3.0: |
|||
|
|||
* [System.IdentityModel.Tokens.Jwt](https://www.nuget.org/packages/System.IdentityModel.Tokens.Jwt/) |
|||
* [Microsoft.IdentityModel.Protocols.OpenIdConnect](https://www.nuget.org/packages/Microsoft.IdentityModel.Protocols.OpenIdConnect/) |
|||
* [Microsoft.IdentityModel.Tokens](https://www.nuget.org/packages/Microsoft.IdentityModel.Tokens/) |
|||
* [Microsoft.IdentityModel.JsonWebTokens](https://www.nuget.org/packages/Microsoft.IdentityModel.JsonWebTokens/) |
|||
|
|||
That's all, it's a simple migration! If you have advanced usage of OpenIddict, please check the [official migration guide](https://documentation.openiddict.com/guides/migration/50-to-60) for more information. |
|||
@ -1,3 +1,8 @@ |
|||
# eShopOnAbp |
|||
|
|||
> ⚠️ **Important Notice** |
|||
> This project, "eshoponabp," is outdated. It served as a reference project for microservice architecture using the ABP Framework, but we now recommend using the [ABP Microservice Solution Template](https://abp.io/docs/latest/solution-templates/microservice) for new projects. |
|||
> |
|||
> The new template offers a modernized and officially supported starting point for building microservices with ABP. Please consider transitioning to the new template for the latest features and improvements. |
|||
|
|||
This document is in progress. Please see the source code for now: https://github.com/abpframework/eShopOnAbp |
|||
|
|||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 59 KiB |