Browse Source

Merge branch 'rel-10.0' into EngincanV/task-panel-docs

pull/24681/head
EngincanV 2 weeks ago
parent
commit
7c95152dcb
  1. 1
      .github/workflows/auto-pr.yml
  2. 2
      Directory.Packages.props
  3. 6
      docs/en/docs-nav.json
  4. BIN
      docs/en/get-started/images/abp-studio-background-tasks.png
  5. BIN
      docs/en/get-started/images/abp-studio-created-microservice-solution-explorer.png
  6. BIN
      docs/en/get-started/images/abp-studio-created-new-microservice-solution.png
  7. BIN
      docs/en/get-started/images/abp-studio-microservice-kubernetes-build-docker-images.png
  8. BIN
      docs/en/get-started/images/abp-studio-microservice-kubernetes-install-helm-chart.png
  9. BIN
      docs/en/get-started/images/abp-studio-microservice-kubernetes-tab.png
  10. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-applications.png
  11. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-browse-microservice.png
  12. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-browse.png
  13. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-1.png
  14. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-2.png
  15. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-external-service.png
  16. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-watch-enabled-icon.png
  17. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner.png
  18. BIN
      docs/en/get-started/images/abp-studio-new-microservice-helm-charts.png
  19. BIN
      docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png
  20. BIN
      docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png
  21. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png
  22. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png
  23. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-admin-password.png
  24. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png
  25. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png
  26. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png
  27. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png
  28. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png
  29. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png
  30. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png
  31. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png
  32. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png
  33. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png
  34. BIN
      docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png
  35. BIN
      docs/en/get-started/images/abp-studio-open-module-folder.png
  36. BIN
      docs/en/get-started/images/abp-studio-welcome-screen.png
  37. 6
      docs/en/get-started/microservice.md
  38. 128
      docs/en/studio/custom-commands.md
  39. BIN
      docs/en/studio/images/custom-commands/create-edit-command.png
  40. BIN
      docs/en/studio/images/custom-commands/management-window.png
  41. 48
      docs/en/studio/kubernetes.md
  42. 6
      docs/en/studio/running-applications.md
  43. 64
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  44. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs
  45. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs
  46. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs
  47. 69
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

1
.github/workflows/auto-pr.yml

@ -26,7 +26,6 @@ jobs:
branch: auto-merge/rel-10-0/${{github.run_number}}
title: Merge branch rel-10.1 with rel-10.0
body: This PR generated automatically to merge rel-10.1 with rel-10.0. Please review the changed files before merging to prevent any errors that may occur.
reviewers: maliming
draft: true
token: ${{ github.token }}
- name: Merge Pull Request

2
Directory.Packages.props

@ -140,7 +140,7 @@
<PackageVersion Include="Polly" Version="8.6.3" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.0-rc" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="10.0.1" />
<PackageVersion Include="Quartz" Version="3.15.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.15.0" />

6
docs/en/docs-nav.json

@ -347,6 +347,10 @@
{
"text": "Working with ABP Suite",
"path": "studio/working-with-suite.md"
},
{
"text": "Custom Commands",
"path": "studio/custom-commands.md"
}
]
},
@ -1276,7 +1280,7 @@
},
{
"text": "LeptonX Lite",
"path": "ui-themes/lepton-x-lite/mvc.md"
"path": "ui-themes/lepton-x-lite/asp-net-core.md"
},
{
"text": "LeptonX",

BIN
docs/en/get-started/images/abp-studio-background-tasks.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
docs/en/get-started/images/abp-studio-created-microservice-solution-explorer.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/en/get-started/images/abp-studio-created-new-microservice-solution.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-kubernetes-build-docker-images.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-kubernetes-install-helm-chart.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-kubernetes-tab.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-applications.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-browse-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-browse.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-external-service.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-watch-enabled-icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 49 KiB

BIN
docs/en/get-started/images/abp-studio-new-microservice-helm-charts.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-admin-password.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/en/get-started/images/abp-studio-open-module-folder.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/en/get-started/images/abp-studio-welcome-screen.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 152 KiB

6
docs/en/get-started/microservice.md

@ -126,6 +126,12 @@ Click the Next button to see *Additional Services* screen:
On that screen, allows you to include extra microservices in your ABP solution during the creation process. This feature lets you extend your solution with business-specific services right from the start.
Click the Next button to see *Admin Password* screen:
![abp-studio-new-solution-dialog-admin-password](images/abp-studio-new-solution-dialog-admin-password.png)
Here, you can set the initial password for the `admin` user of your application. By default, it is set to `1q2w3E*`, but you can change it to a more secure password of your choice.
Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you. After clicking the *Create* button, the dialog is closed and your solution is loaded into ABP Studio:
![abp-studio-created-new-microservice-solution](images/abp-studio-created-new-microservice-solution.png)

128
docs/en/studio/custom-commands.md

@ -0,0 +1,128 @@
```json
//[doc-seo]
{
"Description": "Learn how to create and manage custom commands in ABP Studio to automate build, deployment, and other workflows."
}
```
# Custom Commands
````json
//[doc-nav]
{
"Next": {
"Name": "Working with ABP Suite",
"Path": "studio/working-with-suite"
}
}
````
Custom commands allow you to define reusable terminal commands that appear in context menus throughout ABP Studio. You can use them to automate repetitive tasks such as building Docker images, installing Helm charts, running deployment scripts, or executing any custom workflow.
> **Note:** This is an advanced feature primarily intended for teams working with Kubernetes deployments or complex build/deployment workflows. If you're developing a standard application without custom DevOps requirements, you may not need this feature.
## Opening the Management Window
To manage custom commands, right-click on the solution root in *Solution Explorer* and select *Manage Custom Commands*.
![Custom Commands Management Window](images/custom-commands/management-window.png)
The management window displays all defined commands with options to add, edit, or delete them.
## Creating a New Command
Click the *Add New Command* button to open the command editor dialog.
![Create/Edit Custom Command](images/custom-commands/create-edit-command.png)
## Command Properties
| Property | Description |
|----------|-------------|
| **Command Name** | A unique identifier for the command (used internally) |
| **Display Name** | The text shown in context menus |
| **Terminal Command** | The PowerShell command to execute. Use `&&&` to chain multiple commands |
| **Working Directory** | Optional. The directory where the command runs (relative to solution path) |
| **Condition** | Optional. A [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) expression that determines when the command is visible |
| **Require Confirmation** | When enabled, shows a confirmation dialog before execution |
| **Confirmation Text** | The message shown in the confirmation dialog |
## Trigger Targets
Trigger targets determine where your command appears in context menus. You can select multiple targets for a single command.
| Target | Location |
|--------|----------|
| **Helm Charts Root** | *Kubernetes* panel > *Helm* tab > root node |
| **Helm Main Chart** | *Kubernetes* panel > *Helm* tab > main chart |
| **Helm Sub Chart** | *Kubernetes* panel > *Helm* tab > sub chart |
| **Kubernetes Service** | *Kubernetes* panel > *Kubernetes* tab > service |
| **Solution Runner Root** | *Solution Runner* panel > profile root |
| **Solution Runner Folder** | *Solution Runner* panel > folder |
| **Solution Runner Application** | *Solution Runner* panel > application |
## Execution Targets
Execution targets define where the command actually runs. This enables cascading execution:
- When you trigger a command from a **root or parent item**, it can recursively execute on all matching children
- For example: trigger from *Helm Charts Root* with execution target *Helm Sub Chart* → the command runs on each sub chart
## Template Variables
Commands support [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) template syntax for dynamic values. Use `{%{{{variable}}}%}` to insert context-specific data.
### Available Variables by Context
**Helm Charts:**
| Variable | Description |
|----------|-------------|
| `profile.name` | Kubernetes profile name |
| `profile.namespace` | Kubernetes namespace |
| `chart.name` | Current chart name |
| `chart.path` | Chart directory path |
| `metadata.*` | Hierarchical metadata values (e.g., `metadata.imageName`) |
| `secrets.*` | Secret values (e.g., `secrets.registryPassword`) |
**Kubernetes Service:**
| Variable | Description |
|----------|-------------|
| `name` | Service name |
| `profile.name` | Kubernetes profile name |
| `profile.namespace` | Kubernetes namespace |
| `mainChart.name` | Parent main chart name |
| `chart.name` | Related sub chart name |
| `chart.metadata.*` | Chart-specific metadata |
**Solution Runner (Root, Folder, Application):**
| Variable | Description |
|----------|-------------|
| `profile.name` | Run profile name |
| `profile.path` | Profile file path |
| `application.name` | Application name (Application context only) |
| `application.baseUrl` | Application URL (Application context only) |
| `folder.name` | Folder name (Folder/Application context) |
| `metadata.*` | Profile metadata values |
| `secrets.*` | Profile secret values |
## Example: Build Docker Image
Here's an example command that builds a Docker image for Helm charts:
**Command Properties:**
- **Command Name:** `buildDockerImage`
- **Display Name:** `Build Docker Image`
- **Terminal Command:** `./build-image.ps1 -ProjectPath {%{{{metadata.projectPath}}}%} -ImageName {%{{{metadata.imageName}}}%}`
- **Working Directory:** `etc/helm`
- **Trigger Targets:** Helm Charts Root, Helm Main Chart, Helm Sub Chart
- **Execution Targets:** Helm Main Chart, Helm Sub Chart
- **Condition:** `{%{{{metadata.projectPath}}}%}`
This command:
1. Appears in the context menu of Helm charts root and all chart nodes
2. Executes on main charts and sub charts (cascading from root if triggered there)
3. Only shows for charts that have `projectPath` metadata defined
4. Runs the `build-image.ps1` script with dynamic parameters from metadata

BIN
docs/en/studio/images/custom-commands/create-edit-command.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/studio/images/custom-commands/management-window.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

48
docs/en/studio/kubernetes.md

@ -11,8 +11,8 @@
//[doc-nav]
{
"Next": {
"Name": "Working with ABP Suite",
"Path": "studio/working-with-suite"
"Name": "Custom Commands",
"Path": "studio/custom-commands"
}
}
````
@ -210,46 +210,12 @@ When you connect to a Kubernetes cluster, it uses the selected profile for Kuber
## Advanced Topics
### Adding a Custom Command
Custom commands can be added to both the *Helm* and *Kubernetes* tabs within the *Kubernetes* panel. For instance, when [redeploy](#redeploy-a-chart) a chart, it involves building the Docker image and reinstalling it. However, if you are working with a different Kubernetes cluster than Docker Desktop, you'll need to push the Docker image to the registry before the installation process. This can be achieved by incorporating a custom command into the *Kubernetes services*. Custom commands can be added to the *Chart Root*, *Main Chart*, and *Subchart* in the *Helm* tab, as well as to the *Service* in the *Kubernetes* tab.
To do that, open the ABP Solution (*.abpsln*) file with *Visual Studio Code* it's a JSON file and you'll see the existing commands in the `commands` section. Before adding a new command, create a powershell script in the `abp-solution-path/etc/helm` folder. For example, we create a `push-image.ps1` script to push the docker image to the registry. Then, add the following command to the `commands` section.
```JSON
"kubernetesRedeployWithPushImage": {
"triggerTargets": [
"KUBERNETES_SERVICE"
],
"executionTargets": [
"KUBERNETES_SERVICE"
],
"displayName": " Redeploy with Push Image",
"workingDirectory": "etc/helm",
"terminalCommand": "./build-image.ps1 -ProjectPath {%{{{chart.metadata.projectPath}}}%} -ImageName {%{{{chart.metadata.imageName}}}%} -ProjectType {%{{{chart.metadata.projectType}}}%} &&& ./push-image.ps1 -ImageName {%{{{chart.metadata.imageName}}}%} &&& ./install.ps1 -ChartName {%{{{mainChart.name}}}%} -Namespace {%{{{profile.namespace}}}%} -ReleaseName {%{{{mainChart.name}}}%}-{%{{{profile.name}}}%} -DotnetEnvironment {%{{{mainChart.metadata.dotnetEnvironment}}}%}",
"requireConfirmation": "true",
"confirmationText": "Are you sure to redeploy with push image the related chart '{%{{{chart.name}}}%}' for the service '{%{{{name}}}%}'?",
"condition": "{%{{{chart != null && chart.metadata.projectPath != null && chart.metadata.imageName != null && chart.metadata.projectType != null}}}%}"
}
```
### Custom Commands
You can add custom commands to context menus in both the *Helm* and *Kubernetes* tabs. This is useful for automating workflows like pushing Docker images to a registry before installation, or running custom deployment scripts.
Once the command is added, reload the solution from *File* -> *Reload Solution* in the toolbar. After reloading, you will find the *Redeploy with Push Image* command in the context-menu of the service.
Custom commands can be added to the *Chart Root*, *Main Chart*, and *Sub Chart* in the *Helm* tab, as well as to the *Service* in the *Kubernetes* tab.
![redeploy-push-image](./images/kubernetes/redeploy-push-image.png)
The JSON object has the following properties:
- `triggerTargets`: Specifies the trigger targets for the command. The added command will appear in these targets. You can add one or more trigger targets, accepting values such as *HELM_CHARTS_ROOT*, *HELM_MAIN_CHART*, *HELM_SUB_CHART* and *KUBERNETES_SERVICE*.
- `executionTargets`: Specifies the execution targets for the command. When executing the command on a root item, it will recursively execute the command for all children. Acceptable values include *HELM_CHARTS_ROOT*, *HELM_MAIN_CHART*, *HELM_SUB_CHART*, and *KUBERNETES_SERVICE*.
- `displayName`: Specifies the display name of the command.
- `workingDirectory`: Specifies the working directory of the command. It's relative to the solution path.
- `terminalCommand`: Specifies the terminal command for the custom command. The `&&&` operator can be used to run multiple commands in the terminal. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
- `requireConfirmation`: Specifies whether the command requires confirmation message before execution. Acceptable values include *true* and *false*.
- `confirmationText`: Specifies the confirmation text for the command. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
- `condition`: Specifies the condition for the command. If the condition returns *false*, it skips the current item and attempts to execute the command for the next item or child item. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
You can use the following variables in the scriban syntax based on the execution target:
- `HELM_CHARTS_ROOT`: *profile*, *metadata*, *secrets*
- `HELM_MAIN_CHART`: *profile*, *chart*, *metadata*, *secret*
- `HELM_SUB_CHART`: *profile*, *chart*, *metadata*, *secret*
- `KUBERNETES_SERVICE`: *name*, *profile*, *mainChart*, *chart*, *metadata*, *secret*
For detailed information on creating and managing custom commands, see the [Custom Commands](custom-commands.md) documentation.

6
docs/en/studio/running-applications.md

@ -250,7 +250,11 @@ The *Open with* submenu provides options to open the application project in exte
- **Terminal**: Opens a terminal window in the project directory.
- **Explorer / Finder**: Opens the project folder in the system file explorer.
#### Miscellaneous
### Custom Commands
You can add custom commands that appear in the context menu of Solution Runner items (root, folders, and applications). These commands allow you to automate custom workflows and scripts. For details on creating and managing custom commands, see the [Custom Commands](custom-commands.md) documentation.
### Miscellaneous
- We can copy the selected application *Browse* URL with *Copy URL*. It copies the *Browse* URL instead of *Launch URL* since we could be connected to a *Kubernetes* service.
- You can change the target framework by right-click the selected application and change the *Target Framework* option. This option visible if the project has multiple target framework such as MAUI applications.

64
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -19,7 +19,7 @@ namespace Volo.Abp.Caching;
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// </summary>
/// <typeparam name="TCacheItem">The type of cache item being cached.</typeparam>
public class DistributedCache<TCacheItem> :
public class DistributedCache<TCacheItem> :
IDistributedCache<TCacheItem>
where TCacheItem : class
{
@ -683,35 +683,30 @@ public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheI
result = cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
if (result.All(x => x.Value != null))
{
return result!;
}
var resultMap = result
.Where(x => x.Value != null)
.ToDictionary(x => x.Key, x => x.Value);
var missingKeys = new List<TCacheKey>();
var missingValuesIndex = new List<int>();
for (var i = 0; i < keyArray.Length; i++)
if (resultMap.Count == keyArray.Length)
{
if (result[i].Value != null)
{
continue;
}
missingKeys.Add(keyArray[i]);
missingValuesIndex.Add(i);
return keyArray
.Select(key => new KeyValuePair<TCacheKey, TCacheItem?>(key, resultMap[key]))
.ToArray();
}
var missingKeys = keyArray.Where(key => !resultMap.ContainsKey(key)).ToList();
var missingValues = factory.Invoke(missingKeys).ToArray();
var valueQueue = new Queue<KeyValuePair<TCacheKey, TCacheItem>>(missingValues);
SetMany(missingValues, optionsFactory?.Invoke(), hideErrors, considerUow);
foreach (var index in missingValuesIndex)
foreach (var pair in missingValues)
{
result[index] = valueQueue.Dequeue()!;
resultMap[pair.Key] = pair.Value;
}
return result;
return keyArray
.Select(key => new KeyValuePair<TCacheKey, TCacheItem?>(key, resultMap.GetOrDefault(key)))
.ToArray();
}
@ -779,35 +774,30 @@ public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheI
result = cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
if (result.All(x => x.Value != null))
{
return result;
}
var resultMap = result
.Where(x => x.Value != null)
.ToDictionary(x => x.Key, x => x.Value);
var missingKeys = new List<TCacheKey>();
var missingValuesIndex = new List<int>();
for (var i = 0; i < keyArray.Length; i++)
if (resultMap.Count == keyArray.Length)
{
if (result[i].Value != null)
{
continue;
}
missingKeys.Add(keyArray[i]);
missingValuesIndex.Add(i);
return keyArray
.Select(key => new KeyValuePair<TCacheKey, TCacheItem?>(key, resultMap[key]))
.ToArray();
}
var missingKeys = keyArray.Where(key => !resultMap.ContainsKey(key)).ToList();
var missingValues = (await factory.Invoke(missingKeys)).ToArray();
var valueQueue = new Queue<KeyValuePair<TCacheKey, TCacheItem>>(missingValues);
await SetManyAsync(missingValues, optionsFactory?.Invoke(), hideErrors, considerUow, token);
foreach (var index in missingValuesIndex)
foreach (var pair in missingValues)
{
result[index] = valueQueue.Dequeue()!;
resultMap[pair.Key] = pair.Value;
}
return result;
return keyArray
.Select(key => new KeyValuePair<TCacheKey, TCacheItem?>(key, resultMap.GetOrDefault(key)))
.ToArray();
}
/// <summary>

8
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs

@ -53,6 +53,14 @@ public class RecreateInitialMigrationCommand : IConsoleCommand, ITransientDepend
Directory.Delete(Path.Combine(projectDir, "TenantMigrations"), true);
separateDbContext = true;
}
CmdHelper.RunCmd("dotnet build", workingDirectory: projectDir, exitCode: out var exitCode);
if (exitCode != 0)
{
Logger.LogError("Build failed for project {Project}. Skipping migration recreation.", csprojFile);
continue;
}
if (!separateDbContext)
{
CmdHelper.RunCmd($"dotnet ef migrations add Initial", workingDirectory: projectDir);

16
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs

@ -14,7 +14,7 @@ public class InitialMigrationCreator : ITransientDependency
public ICmdHelper CmdHelper { get; }
public DotnetEfToolManager DotnetEfToolManager { get; }
public ILogger<InitialMigrationCreator> Logger { get; set; }
public InitialMigrationCreator(ICmdHelper cmdHelper, DotnetEfToolManager dotnetEfToolManager)
{
CmdHelper = cmdHelper;
@ -30,11 +30,11 @@ public class InitialMigrationCreator : ITransientDependency
Logger.LogError($"This path doesn't exist: {targetProjectFolder}");
return false;
}
Logger.LogInformation("Creating initial migrations...");
await DotnetEfToolManager.BeSureInstalledAsync();
var tenantDbContextName = FindTenantDbContextName(targetProjectFolder);
var dbContextName = tenantDbContextName != null ?
FindDbContextName(targetProjectFolder)
@ -60,7 +60,7 @@ public class InitialMigrationCreator : ITransientDependency
return migrationSuccess;
}
private string FindTenantDbContextName(string projectFolder)
{
var tenantDbContext = Directory.GetFiles(projectFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories)
@ -93,6 +93,12 @@ public class InitialMigrationCreator : ITransientDependency
private string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext, string outputDirectory)
{
var output = CmdHelper.RunCmdAndGetOutput("dotnet build", out int buildExitCode, dbMigrationsFolder);
if (buildExitCode != 0)
{
return output;
}
var dbContextOption = string.IsNullOrWhiteSpace(dbContext)
? string.Empty
: $"--context {dbContext}";
@ -108,4 +114,4 @@ public class InitialMigrationCreator : ITransientDependency
output.Contains("To undo this action") &&
output.Contains("ef migrations remove"));
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs

@ -44,13 +44,20 @@ public class EfCoreMigrationManager : ITransientDependency
string dbContext,
string outputDirectory)
{
CmdHelper.RunCmd($"dotnet build", workingDirectory: dbMigrationsProjectFolder, exitCode: out var buildExitCode);
if (buildExitCode != 0)
{
Logger.LogWarning("Dotnet build failed for project folder {ProjectFolder}. Skipping EF Core migration command.", dbMigrationsProjectFolder);
return;
}
var dbContextOption = string.IsNullOrWhiteSpace(dbContext)
? string.Empty
: $"--context {dbContext}";
CmdHelper.RunCmd($"dotnet ef migrations add {migrationName}" +
$" --output-dir {outputDirectory}" +
$" {dbContextOption}",
$" {dbContextOption}",
workingDirectory: dbMigrationsProjectFolder);
}

69
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Testing;
@ -751,6 +752,74 @@ public class DistributedCache_Tests : AbpIntegratedTest<AbpCachingTestModule>
cacheValue[1].Value.Name.ShouldBe("jack");
}
[Fact]
public async Task GetOrAddManyAsync_Should_Return_Values_By_Key_With_Uow()
{
var key1 = "testkey";
var key2 = "testkey2";
var keys = new[] { key1, key2 };
using (GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(key2, new PersonCacheItem("cached"), considerUow: true);
var result = await personCache.GetOrAddManyAsync(keys, missingKeys =>
{
missingKeys.ToArray().ShouldBe(new[] { key1 });
return Task.FromResult(new List<KeyValuePair<string, PersonCacheItem>>
{
new(key1, new PersonCacheItem("factory"))
});
}, considerUow: true);
result.Length.ShouldBe(2);
result[0].Key.ShouldBe(key1);
result[0].Value.ShouldNotBeNull();
result[0].Value.Name.ShouldBe("factory");
result[1].Key.ShouldBe(key2);
result[1].Value.ShouldNotBeNull();
result[1].Value.Name.ShouldBe("cached");
}
}
[Fact]
public async Task GetOrAddManyAsync_Should_Map_By_Key_Under_Concurrency()
{
var key1 = "testkey";
var key2 = "testkey2";
var keys = new[] { key1, key2 };
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
async Task<List<KeyValuePair<string, PersonCacheItem>>> Factory(IEnumerable<string> missingKeys)
{
await Task.Yield();
return missingKeys
.Reverse()
.Select(x => new KeyValuePair<string, PersonCacheItem>(x, new PersonCacheItem(x == key1 ? "v1" : "v2")))
.ToList();
}
var task1 = personCache.GetOrAddManyAsync(keys, Factory);
var task2 = personCache.GetOrAddManyAsync(keys, Factory);
var results = await Task.WhenAll(task1, task2);
foreach (var result in results)
{
result.Length.ShouldBe(2);
result[0].Key.ShouldBe(key1);
result[0].Value!.Name.ShouldBe("v1");
result[1].Key.ShouldBe(key2);
result[1].Value!.Name.ShouldBe("v2");
}
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetOrAddManyAsync()
{

Loading…
Cancel
Save