From 320a3829adab66bb4bed29ed4b97caa5b490c95b Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 14:20:10 +0300 Subject: [PATCH 01/13] Initial state of Bulk Operations article --- .../2022-11-28-ef-bulk-operations/POST.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md new file mode 100644 index 0000000000..3f679abf98 --- /dev/null +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -0,0 +1,57 @@ +# Entity Framework Core Bulk Operations +Entity Framework tracks all the entity changes and applies those changes to database one by one when `SaveChanges()` method called. There was no way to execute bulk operations in Entity Framework Core without a dependency. + +As you know [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library were doing it but it was not free. + +There was no other solution until now. [Bulk Operations](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) are now available in Entity Framework Core with .NET 7. + +With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with Entity Framework Core Extensions library if you're familiar with it. + +You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it. + + +## Bulk Operations in ABP Framework + +ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications. But there is another feature that ABP Framework provides: `IEfCoreBulkOperationProvider`. You can visit the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core#customize-bulk-operations) to learn more about it. You always have full control of execution of bulk operations in your ABP applications. When you call `InsertMany`, `DeleteMany` or `UpdateMany` methods of repository, that provider will be invoked and you can customize all the queries from one point. Mostly you don't need to implement & customize that interface, but it's good to know that you can do it if you need. + +## Usage in Repositories + +Most of real world case scenarios, you need custom queries to execute bulk operations. You can't manage all the cases with overriding `IEfCoreBulkOperationProvider` even you can't identify entities in this provider because it's not aware of the properties of entities. Best way to use this new feature is inside the repositories. + +Let me show an example below: + +```csharp +public class BookEntityFrameworkCoreRepository : EfCoreRepository, IBookRepository +{ + public BookEntityFrameworkCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task UpdateListingAsync() + { + var dbSet = await GetDbSetAsync(); + + dbSet + .Where(x => x.IsListed && x.PublishedOn.Year <= 2022) + .ExecuteUpdate(s => s.SetProperty(x => x.IsListed, x => false)); + } + + public async Task DeleteOldBooksAsync() + { + var dbSet = await GetDbSetAsync(); + + dbSet.Where(x => x.PublishedOn.Year <= 2000) + .ExecuteDelete(); + } + + public async Task ImportAsync(List books) + { + var dbSet = await GetDbSetAsync(); + + // No need an action for bulk inserting. + await base.InsertManyAsync(books); + } +} +``` + +> There is no need to take an action for bulk inserting. You can use `InsertManyAsync` method of repository instead of creating a new method for it if you don't have a custom logic. It'll use new bulk inserting feature autmatically since it's available in EF Core 7.0. \ No newline at end of file From a359b6a16d83b2e384fe653aeef90f9decf6823b Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 14:27:46 +0300 Subject: [PATCH 02/13] Spellcheck fix --- .../en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index 3f679abf98..d439edd070 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -54,4 +54,4 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository There is no need to take an action for bulk inserting. You can use `InsertManyAsync` method of repository instead of creating a new method for it if you don't have a custom logic. It'll use new bulk inserting feature autmatically since it's available in EF Core 7.0. \ No newline at end of file +> There is no need to take an action for bulk inserting. You can use `InsertManyAsync` method of repository instead of creating a new method for it if you don't have a custom logic. It'll use new bulk inserting feature automatically since it's available in EF Core 7.0. \ No newline at end of file From 84b7b34211193a056a7fd43eaa3f7a71d26a319f Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Mon, 28 Nov 2022 14:45:51 +0300 Subject: [PATCH 03/13] quick fix --- .../2022-11-28-ef-bulk-operations/POST.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index d439edd070..1aa356bf3e 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -1,22 +1,22 @@ # Entity Framework Core Bulk Operations -Entity Framework tracks all the entity changes and applies those changes to database one by one when `SaveChanges()` method called. There was no way to execute bulk operations in Entity Framework Core without a dependency. +Entity Framework tracks all the entity changes and applies those changes to the database one by one when the `SaveChanges()` method is called. There was no way to execute bulk operations in Entity Framework Core without a dependency. -As you know [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library were doing it but it was not free. +As you know the [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library was doing it but it was not free. There was no other solution until now. [Bulk Operations](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) are now available in Entity Framework Core with .NET 7. -With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with Entity Framework Core Extensions library if you're familiar with it. +With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with the Entity Framework Core Extensions library if you're familiar with it. You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it. ## Bulk Operations in ABP Framework -ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications. But there is another feature that ABP Framework provides: `IEfCoreBulkOperationProvider`. You can visit the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core#customize-bulk-operations) to learn more about it. You always have full control of execution of bulk operations in your ABP applications. When you call `InsertMany`, `DeleteMany` or `UpdateMany` methods of repository, that provider will be invoked and you can customize all the queries from one point. Mostly you don't need to implement & customize that interface, but it's good to know that you can do it if you need. +ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications. But there is another feature that the ABP Framework provides: `IEfCoreBulkOperationProvider`. You can visit the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core#customize-bulk-operations) to learn more about it. You always have full control of the execution of bulk operations in your ABP applications. When you call the `InsertMany`, `DeleteMany` or `UpdateMany` methods of the repository, that provider will be invoked and you can customize all the queries from one point. You don't mostly need to implement & customize that interface, but it's good to know that you can do it if you need. ## Usage in Repositories -Most of real world case scenarios, you need custom queries to execute bulk operations. You can't manage all the cases with overriding `IEfCoreBulkOperationProvider` even you can't identify entities in this provider because it's not aware of the properties of entities. Best way to use this new feature is inside the repositories. +In most of the real world case scenarios, you need custom queries to execute bulk operations. You can't manage all the cases by overriding `IEfCoreBulkOperationProvider` and you can't even identify the entities in this provider because it's not aware of the properties of the entities. The best way to use this new feature is inside the repositories. Let me show an example below: @@ -48,10 +48,10 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository There is no need to take an action for bulk inserting. You can use `InsertManyAsync` method of repository instead of creating a new method for it if you don't have a custom logic. It'll use new bulk inserting feature automatically since it's available in EF Core 7.0. \ No newline at end of file +> There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. From dcf42700b231d5f4e627b9821794b5c9d14743d5 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 15:30:11 +0300 Subject: [PATCH 04/13] Update POST.md --- .../en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index 1aa356bf3e..84666a59da 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -55,3 +55,5 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. + +> `ExecuteDeleteAsync` breaks the soft delete and auditing since deleted entities are removed from the change tracker. ABP can't handle it. So, you have to be careful while using it. \ No newline at end of file From 95bd723edf2b0e3f58bb2a3a1e7dcbe8cb275b17 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 15:54:41 +0300 Subject: [PATCH 05/13] Update title of article --- .../en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index 84666a59da..fe4f677c4a 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -1,4 +1,4 @@ -# Entity Framework Core Bulk Operations +# Bulk Operations with Entity Framework Core 7.0 Entity Framework tracks all the entity changes and applies those changes to the database one by one when the `SaveChanges()` method is called. There was no way to execute bulk operations in Entity Framework Core without a dependency. As you know the [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library was doing it but it was not free. From b902b182c975d7002669e7d1cef4a6e1e5ed9767 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 17:41:11 +0300 Subject: [PATCH 06/13] Update docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Halil İbrahim Kalkan --- .../en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index fe4f677c4a..f138b7ef91 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -56,4 +56,4 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. -> `ExecuteDeleteAsync` breaks the soft delete and auditing since deleted entities are removed from the change tracker. ABP can't handle it. So, you have to be careful while using it. \ No newline at end of file +> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully. \ No newline at end of file From 1f5f615c65c33c2ff7cbad22bfc65baea407ad70 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 17:41:59 +0300 Subject: [PATCH 07/13] Update POST.md --- .../2022-11-28-ef-bulk-operations/POST.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index f138b7ef91..27b8022fac 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -31,17 +31,17 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository x.IsListed && x.PublishedOn.Year <= 2022) - .ExecuteUpdate(s => s.SetProperty(x => x.IsListed, x => false)); + .ExecuteUpdateAsync(s => s.SetProperty(x => x.IsListed, x => false)); } public async Task DeleteOldBooksAsync() { var dbSet = await GetDbSetAsync(); - dbSet.Where(x => x.PublishedOn.Year <= 2000) - .ExecuteDelete(); + await dbSet.Where(x => x.PublishedOn.Year <= 2000) + .ExecuteDeleteAsync(); } public async Task ImportAsync(List books) From 2db6d5516216ee1f4bd56a5b9b546383b243e323 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 17:42:37 +0300 Subject: [PATCH 08/13] Update POST.md --- .../2022-11-28-ef-bulk-operations/POST.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index 27b8022fac..5b01cbcdd8 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -43,14 +43,6 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository x.PublishedOn.Year <= 2000) .ExecuteDeleteAsync(); } - - public async Task ImportAsync(List books) - { - var dbSet = await GetDbSetAsync(); - - // No need for an action for bulk inserting. - await base.InsertManyAsync(books); - } } ``` From e54a6e47271c45d92816a4858979fa88688c67ed Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 28 Nov 2022 17:45:57 +0300 Subject: [PATCH 09/13] Remove IEfCoreBulkOperationProvider reference from documentation --- .../2022-11-28-ef-bulk-operations/POST.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index 5b01cbcdd8..edf88bfcaa 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -9,16 +9,11 @@ With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelet You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it. - -## Bulk Operations in ABP Framework - -ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications. But there is another feature that the ABP Framework provides: `IEfCoreBulkOperationProvider`. You can visit the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core#customize-bulk-operations) to learn more about it. You always have full control of the execution of bulk operations in your ABP applications. When you call the `InsertMany`, `DeleteMany` or `UpdateMany` methods of the repository, that provider will be invoked and you can customize all the queries from one point. You don't mostly need to implement & customize that interface, but it's good to know that you can do it if you need. +ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications without any limitation. ## Usage in Repositories -In most of the real world case scenarios, you need custom queries to execute bulk operations. You can't manage all the cases by overriding `IEfCoreBulkOperationProvider` and you can't even identify the entities in this provider because it's not aware of the properties of the entities. The best way to use this new feature is inside the repositories. - -Let me show an example below: +You can use these methods in your repositories as below: ```csharp public class BookEntityFrameworkCoreRepository : EfCoreRepository, IBookRepository @@ -40,7 +35,8 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository x.PublishedOn.Year <= 2000) + await dbSet + .Where(x => x.PublishedOn.Year <= 2000) .ExecuteDeleteAsync(); } } From 506779db7747934a4087e2d7a9e9dfbfbbe36cbd Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 29 Nov 2022 15:24:08 +0800 Subject: [PATCH 10/13] CLI supports MAUI Blazor UI --- .../Volo/Abp/Cli/Commands/NewCommand.cs | 2 +- .../Commands/ProjectCreationCommandBase.cs | 20 +++++--- .../ProjectBuilding/Building/UiFramework.cs | 3 +- .../Templates/App/AppTemplateBase.cs | 40 ++++++++++++++- .../MauiBlazorChangeApplicationIdGuidStep.cs | 34 +++++++++++++ ...orPortChangeForSeparatedAuthServersStep.cs | 50 +++++++++++++++++++ .../Templates/Maui/MauiChangePortStep.cs | 1 + 7 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 8421eb656b..ee0b6a90da 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -111,7 +111,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien var skipBundling = commandLineArgs.Options.ContainsKey(Options.SkipBundling.Long) || commandLineArgs.Options.ContainsKey(Options.SkipBundling.Short); if (!skipBundling) { - await RunBundleForBlazorWasmTemplateAsync(projectArgs); + await RunBundleForBlazorWasmOrMauiBlazorTemplateAsync(projectArgs); } await ConfigurePwaSupportForAngular(projectArgs); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs index 2d5c9856cf..e0cbfe7d07 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs @@ -118,7 +118,7 @@ public abstract class ProjectCreationCommandBase Logger.LogInformation("DBMS: " + databaseManagementSystem); } - var uiFramework = GetUiFramework(commandLineArgs); + var uiFramework = GetUiFramework(commandLineArgs, template); if (uiFramework != UiFramework.NotSpecified) { Logger.LogInformation("UI Framework: " + uiFramework); @@ -413,22 +413,24 @@ public abstract class ProjectCreationCommandBase } } - protected async Task RunBundleForBlazorWasmTemplateAsync(ProjectBuildArgs projectArgs) + protected async Task RunBundleForBlazorWasmOrMauiBlazorTemplateAsync(ProjectBuildArgs projectArgs) { - if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework == UiFramework.Blazor) + if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework is UiFramework.Blazor or UiFramework.MauiBlazor) { - Logger.LogInformation("Generating bundles for Blazor Wasm..."); + var isWebassembly = projectArgs.UiFramework == UiFramework.Blazor; + var message = isWebassembly ? "Generating bundles for Blazor Wasm" : "Generating bundles for MAUI Blazor"; + Logger.LogInformation($"{message}..."); await EventBus.PublishAsync(new ProjectCreationProgressEvent { - Message = "Generating bundles for Blazor Wasm" + Message = message }, false); var directory = Path.GetDirectoryName( - Directory.GetFiles(projectArgs.OutputFolder, "*.Blazor.csproj", SearchOption.AllDirectories).First() + Directory.GetFiles(projectArgs.OutputFolder, isWebassembly? "*.Blazor.csproj" :"*.MauiBlazor.csproj", SearchOption.AllDirectories).First() ); - await _bundlingService.BundleAsync(directory, true); + await _bundlingService.BundleAsync(directory, true, projectType: isWebassembly ? BundlingConsts.WebAssembly : BundlingConsts.MauiBlazor); } } @@ -531,7 +533,7 @@ public abstract class ProjectCreationCommandBase } } - protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs) + protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs, string template = "app") { if (commandLineArgs.Options.ContainsKey("no-ui")) { @@ -554,6 +556,8 @@ public abstract class ProjectCreationCommandBase return UiFramework.Blazor; case "blazor-server": return UiFramework.BlazorServer; + case "maui-blazor" when template == AppProTemplate.TemplateName: + return UiFramework.MauiBlazor; default: throw new CliUsageException(ExceptionMessageHelper.GetInvalidOptionExceptionMessage("UI Framework")); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs index b1ffe8cb11..5b1ebb56f4 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs @@ -7,5 +7,6 @@ public enum UiFramework Mvc = 2, Angular = 3, Blazor = 4, - BlazorServer = 5 + BlazorServer = 5, + MauiBlazor = 6 } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index 2ffa12bca7..b3bdf2d7e9 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -132,7 +132,11 @@ public abstract class AppTemplateBase : TemplateInfo case UiFramework.BlazorServer: ConfigureWithBlazorServerUi(context, steps); break; - + + case UiFramework.MauiBlazor: + ConfigureWithMauiBlazorUi(context, steps); + break; + case UiFramework.Mvc: case UiFramework.NotSpecified: ConfigureWithMvcUi(context, steps); @@ -510,6 +514,37 @@ public abstract class AppTemplateBase : TemplateInfo } } + protected void ConfigureWithMauiBlazorUi(ProjectBuildContext context, List steps) + { + context.Symbols.Add("ui:maui-blazor"); + + steps.Add(new MauiBlazorChangeApplicationIdGuidStep()); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests")); + + if (context.BuildArgs.ExtraProperties.ContainsKey("separate-identity-server") || + context.BuildArgs.ExtraProperties.ContainsKey("separate-auth-server")) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds")); + steps.Add(new MauiBlazorPortChangeForSeparatedAuthServersStep()); + steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300")); + + if (context.BuildArgs.MobileApp == MobileApp.ReactNative) + { + steps.Add(new ReactEnvironmentFilePortChangeForSeparatedAuthServersStep()); + } + } + else + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); + steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); + steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); + } + } + protected void RemoveUnnecessaryPorts(ProjectBuildContext context, List steps) { steps.Add(new RemoveUnnecessaryPortsStep()); @@ -599,7 +634,8 @@ public abstract class AppTemplateBase : TemplateInfo { if ((context.BuildArgs.UiFramework == UiFramework.Mvc || context.BuildArgs.UiFramework == UiFramework.Blazor - || context.BuildArgs.UiFramework == UiFramework.BlazorServer) && + || context.BuildArgs.UiFramework == UiFramework.BlazorServer + || context.BuildArgs.UiFramework == UiFramework.MauiBlazor) && context.BuildArgs.MobileApp == MobileApp.None) { steps.Add(new MoveFolderStep("/aspnet-core/", "/")); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs new file mode 100644 index 0000000000..953447cfa5 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Xml; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.Utils; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.App; + +public class MauiBlazorChangeApplicationIdGuidStep: ProjectBuildPipelineStep +{ + public override void Execute(ProjectBuildContext context) + { + var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.MauiBlazor.csproj")); + + if (projectFile == null) + { + return; + } + + using (var stream = StreamHelper.GenerateStreamFromString(projectFile.Content)) + { + var doc = new XmlDocument { PreserveWhitespace = true }; + doc.Load(stream); + + var node = doc.SelectSingleNode("/Project/PropertyGroup/ApplicationIdGuid"); + if (node != null) + { + node.InnerText = Guid.NewGuid().ToString(); + } + + projectFile.SetContent(doc.OuterXml); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs new file mode 100644 index 0000000000..92b59de7f3 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.App; + +public class MauiBlazorPortChangeForSeparatedAuthServersStep: ProjectBuildPipelineStep +{ + public override void Execute(ProjectBuildContext context) + { + + var appsettingsFile = context.Files.FirstOrDefault(x => + !x.IsDirectory && + x.Name.EndsWith("aspnet-core/src/MyCompanyName.MyProjectName.MauiBlazor/appsettings.json", + StringComparison.InvariantCultureIgnoreCase) + ); + + if(appsettingsFile == null) + { + return; + } + + appsettingsFile.NormalizeLineEndings(); + var lines = appsettingsFile.GetLines(); + + for (var i = 1; i < lines.Length; i++) + { + var line = lines[i]; + + var previousLine = lines[i-1]; + + if (line.Contains("Authority") && line.Contains("localhost")) + { + line = line.Replace("44305", "44301"); + } + else if (previousLine.Contains("AbpAccountPublic") && line.Contains("BaseUrl") && line.Contains("localhost")) + { + line = line.Replace("44305", "44301"); + } + else if (previousLine.Contains("Default") && line.Contains("BaseUrl") && line.Contains("localhost")) + { + line = line.Replace("44305", "44300"); + } + + lines[i] = line; + } + + appsettingsFile.SetLines(lines); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs index 2b45d64b19..41b9393573 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs @@ -53,6 +53,7 @@ public class MauiChangePortStep : ProjectBuildPipelineStep { case UiFramework.Angular: case UiFramework.Blazor: + case UiFramework.MauiBlazor: authServerPort = "44305"; apiHostPort = "44305"; break; From 07a921513e07acb087d97c7c34ac87204705c26a Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Tue, 29 Nov 2022 11:31:06 +0300 Subject: [PATCH 11/13] ADd CmsKit breaking-change into the docs --- docs/en/Migration-Guides/Abp-7_0.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/en/Migration-Guides/Abp-7_0.md b/docs/en/Migration-Guides/Abp-7_0.md index 3b6b1c5e6a..7c3a0d90b5 100644 --- a/docs/en/Migration-Guides/Abp-7_0.md +++ b/docs/en/Migration-Guides/Abp-7_0.md @@ -101,3 +101,11 @@ See https://github.com/abpframework/abp/pull/13644 for more info. We've already done this for our themes. See https://github.com/abpframework/abp/pull/13845 for more info. + +## Replaced `BlogPostPublicDto` with `BlogPostCommonDto` + +- In the CMS Kit Module, `BlogPostPublicDto` has been moved to `Volo.CmsKit.Common.Application.Contracts` from `Volo.CmsKit.Public.Application.Contracts` and renamed to `BlogPostCommonDto`. + +- See the [PR#13499](https://github.com/abpframework/abp/pull/13499) for more information. + +> You can ignore this if you don't use CMS Kit Module. \ No newline at end of file From 0d891aca7a3d240defef1bd3bfdf0465313e1e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 29 Nov 2022 16:31:27 +0300 Subject: [PATCH 12/13] Revision for the dependency injection document --- docs/en/Dependency-Injection.md | 53 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index bfce8a71bf..147ef1689a 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -2,7 +2,7 @@ ABP's Dependency Injection system is developed based on Microsoft's [dependency injection extension](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) library (Microsoft.Extensions.DependencyInjection nuget package). So, it's documentation is valid in ABP too. -> While ABP has no core dependency to any 3rd-party DI provider, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with Autofac installed. See [Autofac integration](Autofac-Integration.md) document for more information. +> While ABP has no core dependency to any 3rd-party DI provider. However, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with [Autofac](https://autofac.org/) installed. See [Autofac integration](Autofac-Integration.md) document for more information. ## Modularity @@ -20,24 +20,24 @@ public class BlogModule : AbpModule ## Conventional Registration -ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` by overriding the `PreConfigureServices` method. +ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` in the constructor of your module class. Example: ````C# public class BlogModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) + public BlogModule() { SkipAutoServiceRegistration = true; } } ```` -Once you skip auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example: +Once you skip the auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example: ````c# public class BlogModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) + public BlogModule() { SkipAutoServiceRegistration = true; } @@ -105,9 +105,7 @@ Example: [Dependency(ServiceLifetime.Transient, ReplaceServices = true)] public class TaxCalculator { - } - ```` ``Dependency`` attribute has a higher priority than other dependency interfaces if it defines the ``Lifetime`` property. @@ -120,7 +118,6 @@ public class TaxCalculator [ExposeServices(typeof(ITaxCalculator))] public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency { - } ```` @@ -179,7 +176,11 @@ public class MyModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { //Replacing the IConnectionStringResolver service - context.Services.Replace(ServiceDescriptor.Transient()); + context.Services.Replace( + ServiceDescriptor.Transient< + IConnectionStringResolver, + MyConnectionStringResolver + >()); } } ```` @@ -202,7 +203,7 @@ public class TaxAppService : ApplicationService _taxCalculator = taxCalculator; } - public void DoSomething() + public async Task DoSomethingAsync() { //...use _taxCalculator... } @@ -227,7 +228,7 @@ public class MyService : ITransientDependency Logger = NullLogger.Instance; } - public void DoSomething() + public async Task DoSomethingAsync() { //...use Logger to write logs... } @@ -244,17 +245,21 @@ One restriction of property injection is that you cannot use the dependency in y Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class. -#### DisablePropertyInjectionAttribute +#### DisablePropertyInjection Attribute -You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties. +You can use `[DisablePropertyInjection]` attribute on classes or their properties to disable property injection for the whole class or some specific properties. ````C# +// Disabling for all properties of the MyService class [DisablePropertyInjection] public class MyService : ITransientDependency { + public ILogger Logger { get; set; } + public ITaxCalculator TaxCalculator { get; set; } } +// Disabling only for the TaxCalculator property public class MyService : ITransientDependency { public ILogger Logger { get; set; } @@ -262,17 +267,21 @@ public class MyService : ITransientDependency [DisablePropertyInjection] public ITaxCalculator TaxCalculator { get; set; } } - ```` ### Resolve Service from IServiceProvider -You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below: +You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject `IServiceProvider` into your class and use the ``GetService`` or the `GetRequiredService` method as shown below: ````C# public class MyService : ITransientDependency { - public ILogger Logger { get; set; } + private readonly ITaxCalculator _taxCalculator; + + public MyService(IServiceProvider serviceProvider) + { + _taxCalculator = serviceProvider.GetRequiredService(); + } } ```` @@ -374,15 +383,15 @@ IEnumerable services = _serviceProvider.GetServices Date: Tue, 29 Nov 2022 23:42:45 +0300 Subject: [PATCH 13/13] Update POST.md --- .../2022-11-28-ef-bulk-operations/POST.md | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md index edf88bfcaa..ac517506ec 100644 --- a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -9,11 +9,17 @@ With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelet You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it. -ABP Framework supports Entity Framework Core as its ORM. So, you can use these new methods in your repository classes in your ABP applications without any limitation. -## Usage in Repositories +It can be easily used with the DbContext. -You can use these methods in your repositories as below: +```csharp +await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync(); +``` + +## Using with ABP Framework +ABP Framework provides an abstraction over database operations and implements generic repository pattern. So, DbContext can't be accessed outside of [repositories](https://docs.abp.io/en/abp/latest/Repositories). + +You can use the `ExecuteUpdate` and `ExecuteDelete` methods inside a repository. ```csharp public class BookEntityFrameworkCoreRepository : EfCoreRepository, IBookRepository @@ -42,6 +48,24 @@ public class BookEntityFrameworkCoreRepository : EfCoreRepository There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. +There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. + +```csharp +public class MyDomainService : DomainService +{ + protected IRepository BookRepository { get; } + + public MyDomainService(IRepository bookRepository) + { + BookRepository = bookRepository; + } + + public async Task CreateBooksAsync(List books) + { + // It'll use bulk inserting automatically. + await BookRepository.InsertManyAsync(books); + } +} +``` -> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully. \ No newline at end of file +> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully.