Browse Source

Merge remote-tracking branch 'origin/rel-10.5' into liming/fix-soft-delete-filter-property-name

# Conflicts:
#	framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
#	framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
pull/25568/head
maliming 4 months ago
parent
commit
0240f997ee
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      docs/en/suite/customizing-the-generated-code.md
  2. 5
      docs/en/suite/editing-templates.md
  3. 2
      docs/en/suite/generating-crud-page.md
  4. 14
      docs/en/suite/index.md
  5. 4
      framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs
  6. 48
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DataFiltering/SoftDelete_DbFunction_Bool_Literal_Tests.cs
  7. 11
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
  8. 11
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
  9. 19
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/EntityWithIntSoftDelete.cs

2
docs/en/suite/customizing-the-generated-code.md

@ -84,6 +84,8 @@ As can be seen in the figure above, you can search the related template file, cl
### Customizations on Blazor UI
> **Blazorise vs MudBlazor templates:** Suite ships two parallel template trees for Blazor — `Frontend.Blazor.*` (Blazorise) and `Frontend.Blazor.MudBlazor.*` (MudBlazor) — and picks the right one based on the [Blazor UI library](index.md#blazor-ui-library) detected in your solution. Customized templates are stored under `.suite/customized-templates/` keyed by the full resource name, so a customization made in a MudBlazor solution is never read by a Blazorise solution (or vice versa). If you migrate an existing solution from Blazorise to MudBlazor, you need to re-apply your customizations to the new resource names.
If you have created your application with Blazor UI, you can see the extended classes for _the code-behind classes of your pages_, under the `Pages` folder:
![](../images/custom-code-blazor-folder-structure.png)

5
docs/en/suite/editing-templates.md

@ -31,7 +31,10 @@ There's a naming convention for the template files.
* If the template name has `Server` prefix, it's used for backend code like repositories, application services, localizations, controllers, permissions, mappings, unit tests.
* If the template name has `Frontend.Angular` prefix, it's used for Angular code generation. The Angular code is being generated via [Angular Schematics](https://angular.dev/tools/cli/schematics).
* If the template name has `Frontend.Mvc` prefix, it's used for razor pages, menus, JavaScript, CSS files.
* If the template name has `Frontend.Blazor` prefix, it's used for razor components.
* If the template name has `Frontend.Blazor` prefix (no `.MudBlazor` segment), it's used for Blazorise-based razor components.
* If the template name has `Frontend.Blazor.MudBlazor` prefix, it's used for [MudBlazor](https://mudblazor.com)-based razor components.
> The template list only shows the variant that matches your solution's [Blazor UI library](index.md#blazor-ui-library) (Blazorise or MudBlazor), so you never see both trees side-by-side and the "outdated" check only flags the active set.
## How do I edit the templates?

2
docs/en/suite/generating-crud-page.md

@ -112,6 +112,8 @@ To create a new entity, make sure the *-New entity-* is selected in the **Entity
> **Note:** This option does not generate React UI pages. ABP Suite's UI generation targets Suite-supported UI stacks such as MVC, Blazor and Angular. For React-based solutions, build the UI in the source-owned [React UI](../framework/ui/react/index.md) application. ABP license holders can use [ABP Studio AI Agent](https://abp.io/studio/ai-agent) with predefined AI credits to generate React pages more easily.
> **Blazor UI library:** For Blazor solutions, the generated UI matches the underlying Blazor component library. Solutions that reference `Volo.Abp.MudBlazorUI` produce pages built around `MudDataGrid`, `MudDialog`, `MudForm` and the rest of the [MudBlazor](https://mudblazor.com) controls; Blazorise-based solutions keep emitting the original `DataGrid` / `Modal` / `Validations` markup. ABP Suite picks the variant automatically by scanning the Blazor project `.csproj` and shows the resolved value in the solution info tooltip — no extra switch is required. See the [Blazor UI library](index.md#blazor-ui-library) section for details.
* **Excel export**: Creates a button that exports a list of all the data that were added to the entity to an Excel file.
* **Bulk delete**: Performs bulk deletion of records based on specified criteria.

14
docs/en/suite/index.md

@ -26,3 +26,17 @@ ABP Suite is a complementary tool to the ABP Platform. ABP Suite allows you to b
It's a .NET Core Global tool that can be installed from the command line. If you are using [ABP Studio](../studio/index.md), you don't even need to install it because it should already be installed, when you first installed the [ABP Studio](../studio/index.md).
By using the ABP Suite, you can generate CRUD pages from the database to the front-end and directly get a kickstart for your application. ABP Suite is actively developed and new features are being added version by version according to the roadmap and your feedback.
## Blazor UI library
When the target solution uses any Blazor UI framework (Blazor, BlazorServer, BlazorWebApp or MAUIBlazor), ABP Suite detects the underlying Blazor component library by scanning the Blazor project `.csproj` for known package references:
* A reference to `Volo.Abp.MudBlazorUI`, `Volo.Abp.AspNetCore.Components.*.Theming.MudBlazor` or `*.Blazor.MudBlazor*` selects the **MudBlazor** templates.
* Otherwise ABP Suite falls back to the **Blazorise** templates that have always shipped with Suite.
The detected value is shown in the *solution info* tooltip of the CRUD Page Generator screen and is reused for two things:
1. Template routing — `Frontend.Blazor.*` resource names are resolved to `Frontend.Blazor.MudBlazor.*` for MudBlazor solutions, so the generated `.razor` / `.razor.cs` files use `MudCard`, `MudDataGrid`, `MudDialog`, `MudForm` and the corresponding Mud input controls instead of the Blazorise `Card`, `DataGrid`, `Modal`, `Validations` markup.
2. Template Management UI — the "Manage Templates" screen and the "outdated templates" check only list the variant that matches the current solution, so the Blazorise and MudBlazor template trees never appear side-by-side.
Customized templates are stored under `.suite/customized-templates/` keyed by the full resource name, so MudBlazor customizations (`Frontend.Blazor.MudBlazor.*`) are physically isolated from Blazorise customizations (`Frontend.Blazor.*`) and never clash.

4
framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreModelBuilderExtensions.cs

@ -21,11 +21,11 @@ public static class AbpEfCoreModelBuilderExtensions
if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled<ISoftDelete>() == true)
{
// IsDeleted == false
// IsDeleted == false. Use the column's TypeMapping so any ValueConverter is applied to the literal.
return new SqlBinaryExpression(
ExpressionType.Equal,
isDeleted,
new SqlConstantExpression(false, typeof(bool), boolParam.TypeMapping),
new SqlConstantExpression(false, typeof(bool), isDeleted.TypeMapping ?? boolParam.TypeMapping),
boolParam.Type,
boolParam.TypeMapping);
}

48
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DataFiltering/SoftDelete_DbFunction_Bool_Literal_Tests.cs

@ -0,0 +1,48 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.EntityFrameworkCore;
using Volo.Abp.TestApp.Testing;
using Xunit;
namespace Volo.Abp.EntityFrameworkCore.DataFiltering;
// Regression for the soft-delete DbFunction translator hardcoding a bool false literal
// with the bool TypeMapping instead of routing the literal through the IsDeleted
// property's ValueConverter. EntityWithIntSoftDelete maps false->5, true->9, so the
// buggy SQL "is_deleted = 0" matches no row (returns 0) while the correct SQL
// "is_deleted = 5" matches the not-deleted row (returns 1).
public class SoftDelete_DbFunction_Bool_Literal_Tests : TestAppTestBase<AbpEntityFrameworkCoreTestModule>
{
private readonly IRepository<EntityWithIntSoftDelete, Guid> _repository;
private readonly IDataFilter<ISoftDelete> _softDeleteFilter;
public SoftDelete_DbFunction_Bool_Literal_Tests()
{
_repository = GetRequiredService<IRepository<EntityWithIntSoftDelete, Guid>>();
_softDeleteFilter = GetRequiredService<IDataFilter<ISoftDelete>>();
}
[Fact]
public async Task SoftDelete_Filter_Should_Route_False_Literal_Through_ValueConverter()
{
await _repository.InsertAsync(new EntityWithIntSoftDelete { Name = "kept" });
await _repository.InsertAsync(new EntityWithIntSoftDelete { Name = "removed", IsDeleted = true });
var visible = await _repository.GetListAsync();
visible.Count.ShouldBe(1);
visible[0].Name.ShouldBe("kept");
using (_softDeleteFilter.Disable())
{
var all = await _repository.GetListAsync();
all.Count.ShouldBe(2);
}
}
}

11
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs

@ -31,6 +31,8 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
public DbSet<EntityWithCustomTenantIdColumn> EntityWithCustomTenantIdColumns { get; set; }
public DbSet<EntityWithIntSoftDelete> EntityWithIntSoftDeletes { get; set; }
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
@ -119,6 +121,15 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
b.HasAbpQueryFilter(e => e.Name.StartsWith("abp"));
});
modelBuilder.Entity<EntityWithIntSoftDelete>(b =>
{
b.Property(x => x.IsDeleted)
.HasColumnName(EntityWithIntSoftDelete.IsDeletedColumnName)
.HasConversion(
v => v ? EntityWithIntSoftDelete.DeletedProviderValue : EntityWithIntSoftDelete.NotDeletedProviderValue,
i => i == EntityWithIntSoftDelete.DeletedProviderValue);
});
modelBuilder.Entity<AppEntityWithNavigations>(b =>
{
b.ConfigureByConvention();

11
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs

@ -39,6 +39,8 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
public DbSet<EntityWithCustomTenantIdColumn> EntityWithCustomTenantIdColumns { get; set; }
public DbSet<EntityWithIntSoftDelete> EntityWithIntSoftDeletes { get; set; }
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
@ -135,6 +137,15 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
b.HasAbpQueryFilter(e => e.Name.StartsWith("abp"));
});
modelBuilder.Entity<EntityWithIntSoftDelete>(b =>
{
b.Property(x => x.IsDeleted)
.HasColumnName(EntityWithIntSoftDelete.IsDeletedColumnName)
.HasConversion(
v => v ? EntityWithIntSoftDelete.DeletedProviderValue : EntityWithIntSoftDelete.NotDeletedProviderValue,
i => i == EntityWithIntSoftDelete.DeletedProviderValue);
});
modelBuilder.Entity<AppEntityWithNavigations>(b =>
{
b.ConfigureByConvention();

19
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/EntityWithIntSoftDelete.cs

@ -0,0 +1,19 @@
using System;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.TestApp.Domain;
// Uses a custom bool<->int converter where false maps to a non-zero value, so the
// soft-delete DbFunction translator (which hardcodes a bool false literal with the
// bool TypeMapping) is observably wrong on every provider: the generated SQL
// compares against 0/FALSE instead of the converter's actual provider value (5).
public class EntityWithIntSoftDelete : AggregateRoot<Guid>, ISoftDelete
{
public const string IsDeletedColumnName = "is_deleted";
public const int NotDeletedProviderValue = 5;
public const int DeletedProviderValue = 9;
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
Loading…
Cancel
Save