From 5433fe6dbcc119d19fbd07c50830ea3d207432ec Mon Sep 17 00:00:00 2001 From: Jakub Baranowski Date: Wed, 26 Oct 2022 13:25:14 +0200 Subject: [PATCH 01/34] PoC of Closure Allocation remove --- .../Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 18 ++++++++++++++++++ .../Volo/Abp/MultiTenancy/CurrentTenant.cs | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index ba0b61eed7..67fd64cae1 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -27,3 +27,21 @@ public class DisposeAction : IDisposable _action(); } } + +public class DisposeActionWithoutClosureAlloc : IDisposable +{ + private readonly Action _action; + [CanBeNull] private readonly T _parameter; + public DisposeActionWithoutClosureAlloc(Action action, T parameter) + { + Check.NotNull(action, nameof(action)); + + _action = action; + _parameter = parameter; + } + public void Dispose() + { + _action(_parameter); + } +} + diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs index a86ce2a40c..013503d871 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs @@ -27,9 +27,10 @@ public class CurrentTenant : ICurrentTenant, ITransientDependency { var parentScope = _currentTenantAccessor.Current; _currentTenantAccessor.Current = new BasicTenantInfo(tenantId, name); - return new DisposeAction(() => + return new DisposeActionWithoutClosureAlloc>(static (state) => { - _currentTenantAccessor.Current = parentScope; - }); + var (currentTenantAccessor, parentScope) = state; + currentTenantAccessor.Current = parentScope; + }, (_currentTenantAccessor, parentScope)); } } From 190e31d93997e3fa7119c3ef3afed89e385444c1 Mon Sep 17 00:00:00 2001 From: Jakub Baranowski Date: Wed, 26 Oct 2022 13:44:45 +0200 Subject: [PATCH 02/34] Better naming convention --- framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 4 ++-- .../Volo/Abp/MultiTenancy/CurrentTenant.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index 67fd64cae1..c6610efb44 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -28,11 +28,11 @@ public class DisposeAction : IDisposable } } -public class DisposeActionWithoutClosureAlloc : IDisposable +public class DisposeAction : IDisposable { private readonly Action _action; [CanBeNull] private readonly T _parameter; - public DisposeActionWithoutClosureAlloc(Action action, T parameter) + public DisposeAction(Action action, T parameter) { Check.NotNull(action, nameof(action)); diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs index 013503d871..e5579a1e0a 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs @@ -27,7 +27,7 @@ public class CurrentTenant : ICurrentTenant, ITransientDependency { var parentScope = _currentTenantAccessor.Current; _currentTenantAccessor.Current = new BasicTenantInfo(tenantId, name); - return new DisposeActionWithoutClosureAlloc>(static (state) => + return new DisposeAction>(static (state) => { var (currentTenantAccessor, parentScope) = state; currentTenantAccessor.Current = parentScope; From ee3b53ab7f616b06f221fe1f30184f1d1cb259dd Mon Sep 17 00:00:00 2001 From: Jakub Baranowski Date: Wed, 26 Oct 2022 13:51:29 +0200 Subject: [PATCH 03/34] Some comments --- framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index c6610efb44..cee793b41f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -28,10 +28,21 @@ public class DisposeAction : IDisposable } } +/// +/// This class can be used to provide an action when +/// Dipose method is called. +/// public class DisposeAction : IDisposable { private readonly Action _action; [CanBeNull] private readonly T _parameter; + + /// + /// Creates a new object. + /// + /// Action to be executed when this object is disposed. + /// /// The parameter of the action. + /// The type of the parameter of the action. public DisposeAction(Action action, T parameter) { Check.NotNull(action, nameof(action)); From e7d8e84a2f0df3453114050536f77f86c090d964 Mon Sep 17 00:00:00 2001 From: Jakub Baranowski Date: Wed, 26 Oct 2022 18:37:35 +0200 Subject: [PATCH 04/34] Miss click in comments --- framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index cee793b41f..b2c9cb66e6 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -5,7 +5,7 @@ namespace Volo.Abp; /// /// This class can be used to provide an action when -/// Dipose method is called. +/// Dispose method is called. /// public class DisposeAction : IDisposable { @@ -30,7 +30,7 @@ public class DisposeAction : IDisposable /// /// This class can be used to provide an action when -/// Dipose method is called. +/// Dispose method is called. /// public class DisposeAction : IDisposable { From 6fade058334979c858c70d6ac6665334fd6702ea Mon Sep 17 00:00:00 2001 From: Jakub Baranowski Date: Wed, 26 Oct 2022 20:51:54 +0200 Subject: [PATCH 05/34] XML repair --- framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index b2c9cb66e6..1bdf0d0686 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -30,7 +30,8 @@ public class DisposeAction : IDisposable /// /// This class can be used to provide an action when -/// Dispose method is called. +/// Dispose method is called. +/// The type of the parameter of the action. /// public class DisposeAction : IDisposable { @@ -41,8 +42,8 @@ public class DisposeAction : IDisposable /// Creates a new object. /// /// Action to be executed when this object is disposed. - /// /// The parameter of the action. - /// The type of the parameter of the action. + /// The parameter of the action. + public DisposeAction(Action action, T parameter) { Check.NotNull(action, nameof(action)); From d9b913759832e38ef3648dafc66e844cac6a9860 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 10 Nov 2022 14:38:14 +0800 Subject: [PATCH 06/34] Use `DisposeAction` with parameter. --- .../Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs | 5 +++-- .../src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs | 11 ++++++----- .../Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs | 2 +- .../Volo/Abp/Localization/CultureHelper.cs | 5 +++-- .../Volo/Abp/Threading/SemaphoreSlimExtensions.cs | 4 ++-- .../Volo/Abp/MultiTenancy/CurrentTenant.cs | 1 + .../Security/Claims/CurrentPrincipalAccessorBase.cs | 8 +++++--- .../AmbientDataContextAmbientScopeProvider.cs | 13 ++++++++----- .../Threading/AsyncLocalSimpleScopeExtensions.cs | 5 +++-- 9 files changed, 32 insertions(+), 22 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs index c137f1fc9c..9007b334b4 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs @@ -60,10 +60,11 @@ public static class AbpCrossCuttingConcerns public static IDisposable Applying(object obj, params string[] concerns) { AddApplied(obj, concerns); - return new DisposeAction(() => + return new DisposeAction>(static (state) => { + var (obj, concerns) = state; RemoveApplied(obj, concerns); - }); + }, (obj, concerns)); } public static string[] GetApplieds(object obj) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs index 1bdf0d0686..0bb9cf46a8 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs @@ -31,19 +31,20 @@ public class DisposeAction : IDisposable /// /// This class can be used to provide an action when /// Dispose method is called. -/// The type of the parameter of the action. +/// The type of the parameter of the action. /// public class DisposeAction : IDisposable { private readonly Action _action; - [CanBeNull] private readonly T _parameter; - + + [CanBeNull] + private readonly T _parameter; + /// /// Creates a new object. /// /// Action to be executed when this object is disposed. /// The parameter of the action. - public DisposeAction(Action action, T parameter) { Check.NotNull(action, nameof(action)); @@ -51,9 +52,9 @@ public class DisposeAction : IDisposable _action = action; _parameter = parameter; } + public void Dispose() { _action(_parameter); } } - diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs index bb30087056..9743898496 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs @@ -83,6 +83,6 @@ public static class DirectoryHelper Directory.SetCurrentDirectory(targetDirectory); - return new DisposeAction(() => { Directory.SetCurrentDirectory(currentDirectory); }); + return new DisposeAction(Directory.SetCurrentDirectory, currentDirectory); } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs index adf2890b21..d2e15fdf25 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs @@ -29,11 +29,12 @@ public static class CultureHelper CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = uiCulture ?? culture; - return new DisposeAction(() => + return new DisposeAction>(static (state) => { + var (currentCulture, currentUiCulture) = state; CultureInfo.CurrentCulture = currentCulture; CultureInfo.CurrentUICulture = currentUiCulture; - }); + }, (currentCulture, currentUiCulture)); } public static bool IsRtl => CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs index 79693a9acb..7eaed3821b 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs @@ -80,9 +80,9 @@ public static class SemaphoreSlimExtensions private static IDisposable GetDispose(this SemaphoreSlim semaphoreSlim) { - return new DisposeAction(() => + return new DisposeAction(static (semaphoreSlim) => { semaphoreSlim.Release(); - }); + }, semaphoreSlim); } } diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs index e5579a1e0a..fb5d2b147c 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs @@ -27,6 +27,7 @@ public class CurrentTenant : ICurrentTenant, ITransientDependency { var parentScope = _currentTenantAccessor.Current; _currentTenantAccessor.Current = new BasicTenantInfo(tenantId, name); + return new DisposeAction>(static (state) => { var (currentTenantAccessor, parentScope) = state; diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs index 9c27fd166a..1d3e296e38 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs @@ -21,9 +21,11 @@ public abstract class CurrentPrincipalAccessorBase : ICurrentPrincipalAccessor { var parent = Principal; _currentPrincipal.Value = principal; - return new DisposeAction(() => + + return new DisposeAction, ClaimsPrincipal>>(static (state) => { - _currentPrincipal.Value = parent; - }); + var (currentPrincipal, parent) = state; + currentPrincipal.Value = parent; + }, (_currentPrincipal, parent)); } } diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs index 7d5c81abb4..06b71d16f3 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs @@ -46,18 +46,21 @@ public class AmbientDataContextAmbientScopeProvider : IAmbientScopeProvider + return new DisposeAction, ScopeItem, IAmbientDataContext, string>>(static (state) => { - ScopeDictionary.TryRemove(item.Id, out item); + var (scopeDictionary, item, dataContext, contextKey) = state; + + scopeDictionary.TryRemove(item.Id, out item); if (item.Outer == null) { - _dataContext.SetData(contextKey, null); + dataContext.SetData(contextKey, null); return; } - _dataContext.SetData(contextKey, item.Outer.Id); - }); + dataContext.SetData(contextKey, item.Outer.Id); + + }, (ScopeDictionary, item, _dataContext, contextKey)); } private ScopeItem GetCurrentItem(string contextKey) diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs index dd33bd1ba1..3d353921d9 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs @@ -9,9 +9,10 @@ public static class AsyncLocalSimpleScopeExtensions { var previousValue = asyncLocal.Value; asyncLocal.Value = value; - return new DisposeAction(() => + return new DisposeAction, T>>(static (state) => { + var (asyncLocal, previousValue) = state; asyncLocal.Value = previousValue; - }); + }, (asyncLocal, previousValue)); } } From 113fa157fadbe82b1efa255b7b097b2d832d53a9 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 21 Nov 2022 16:23:31 +0800 Subject: [PATCH 07/34] Use Default ComponentActivator for Blazorise --- framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs index 5a66e1b247..198dce54fc 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs @@ -1,5 +1,7 @@ using Blazorise; +using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Application; using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.Authorization; @@ -11,7 +13,7 @@ namespace Volo.Abp.BlazoriseUI; typeof(AbpAspNetCoreComponentsWebModule), typeof(AbpDddApplicationContractsModule), typeof(AbpAuthorizationModule) - )] +)] public class AbpBlazoriseUIModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) @@ -27,6 +29,7 @@ public class AbpBlazoriseUIModule : AbpModule options.DebounceInterval = 800; }); + context.Services.Replace(ServiceDescriptor.Scoped()); context.Services.AddSingleton(typeof(AbpBlazorMessageLocalizerHelper<>)); } -} +} \ No newline at end of file From a4b5ddfc53a510266a5b7a9fd9bef28fd943ef28 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Wed, 30 Nov 2022 17:07:48 +0300 Subject: [PATCH 08/34] docs: Add distributed entity caching --- docs/en/Caching.md | 11 +- docs/en/Deployment/Clustered-Environment.md | 2 +- docs/en/Entities.md | 8 + docs/en/Entity-Cache.md | 183 ++++++++++++++++++++ docs/en/docs-nav.json | 4 + 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 docs/en/Entity-Cache.md diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 8fafd725be..50e8ed0e4e 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -10,7 +10,7 @@ ABP Framework extends the [ASP.NET Core distributed cache](https://docs.microsof [Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package of the caching system. You can install it a project using the add-package command of the [ABP CLI](CLI.md): -``` +```bash abp add-package Volo.Abp.Caching ``` @@ -252,6 +252,14 @@ ABP's distributed cache interfaces provide methods to perform batch methods thos > These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` ... methods (called once for each item). +## Caching Entities + +ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database. + +It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted. + +> See [Entity Cache](Entity-Cache.md) documentation for more information. + ## Advanced Topics ### Unit Of Work Level Cache @@ -272,4 +280,5 @@ You can [replace](Dependency-Injection.md) this service by your own implementati ## See Also +* [Entity Cache](Entity-Cache.md) * [Redis Cache](Redis-Cache.md) \ No newline at end of file diff --git a/docs/en/Deployment/Clustered-Environment.md b/docs/en/Deployment/Clustered-Environment.md index 2dace9a7f7..e3294b3469 100644 --- a/docs/en/Deployment/Clustered-Environment.md +++ b/docs/en/Deployment/Clustered-Environment.md @@ -63,7 +63,7 @@ The [Database BLOB provider](../Blob-Storing-Database) is the easiest way since > [ABP Commercial](https://commercial.abp.io/) startup solution templates come with the database BLOB provider as pre-installed, and stores BLOBs in the application's database. -Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOG storage providers. +Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOB storage providers. ## Configuring Background Jobs diff --git a/docs/en/Entities.md b/docs/en/Entities.md index ef8f52a04f..3eac4b2164 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -316,6 +316,14 @@ All these base classes also have non-generic versions to take `AuditedEntity` an All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser` and `FullAuditedAggregateRootWithUser`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern). Also, if you add navigation properties to the AppUser class that comes with the startup template, consider to handle (ignore/map) it on the migration dbcontext (see [the EF Core migration document](Entity-Framework-Core-Migrations.md)). +## Caching Entities + +ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database. + +It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted. + +> See [Entity Cache](Entity-Cache.md) documentation for more information. + ## Extra Properties ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API. diff --git a/docs/en/Entity-Cache.md b/docs/en/Entity-Cache.md new file mode 100644 index 0000000000..e285ff8039 --- /dev/null +++ b/docs/en/Entity-Cache.md @@ -0,0 +1,183 @@ +# Entity Cache + +ABP Framework provides **Distributed Entity Caching System** for caching entities. + +You can use this caching mechanism if you want to cache your entity objects automatically and retrieve them from a cache instead of querying it from a database repeatedly. + +## How Distributed Entity Caching System Works? + +ABP's Entity Caching System does the following operations on behalf of you: + +* It gets the entity from the database (by using the [Repositories](Repositories.md)) in its first call and then gets from the cache in subsequent calls. +* It automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached. +* It uses the cache class's **FullName** as a cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. + +## Installation + +[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package for the ABP's caching system and it's already installed in [the application startup template](Startup-Templates/Index.md). So, you don't need to install it manually. + +## Usage + +`IEntityCache` is a simple service provided by the ABP Framework for caching entities. It's designed as read-only and contains two methods: `FindAsync` and `GetAsync`. + +### Caching Entities + +**Example: `Product` entity** + +```csharp +[CacheName("Products")] +public class Product : AggregateRoot +{ + public string Name { get; set; } + public string Description { get; set; } + public float Price { get; set; } + public int StockCount { get; set; } +} +``` + +* This example uses the `CacheName` attribute for the `Product` class to set the cache name. By default, the cache class's **FullName** is used for the cache name. + +If you want to cache this entity, first you should configure the [dependency injection](Dependency-Injection.md) to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): + +```csharp +context.Services.AddEntityCache(); +``` + +Then configure the [object mapper](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping) (for `Product` to `ProductDto` mapping): + +```csharp +public class MyProjectNameAutoMapperProfile : Profile +{ + public MyProjectNameAutoMapperProfile() + { + //other mappings... + + CreateMap(); + } +} +``` + +Now you can inject the `IEntityCache` service wherever you need: + +```csharp +public class ProductAppService : ApplicationService, IProductAppService +{ + private readonly IEntityCache _productCache; + + public ProductAppService(IEntityCache productCache) + { + _productCache = productCache; + } + + public async Task GetAsync(Guid id) + { + var product = await _productCache.GetAsync(id); + return ObjectMapper.Map(product); + } +} +``` + +* Here, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable. Sometimes this might not be possible and you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity is not serializable. + +### Caching Cache Item Classes + +`IEntityCache` service can be used for caching other cache item classes if the entity is not serializable. + +**Example: `ProductDto` class** + +```csharp +public class ProductDto : EntityDto +{ + public string Name { get; set; } + public string Description { get; set; } + public float Price { get; set; } + public int StockCount { get; set; } +} +``` + +Register the entity cache services to [dependency injection](Dependency-Injection.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): + +```csharp +context.Services.AddEntityCache(); +``` + +Configure the [object mapper](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping) (for `Product` to `ProductDto` mapping): + +```csharp +public class MyProjectNameAutoMapperProfile : Profile +{ + public MyProjectNameAutoMapperProfile() + { + //other mappings... + + CreateMap(); + } +} +``` + +Then, you can inject the `IEntityCache` service wherever you want: + +```csharp +public class ProductAppService : ApplicationService, IProductAppService +{ + private readonly IEntityCache _productCache; + + public ProductAppService(IEntityCache productCache) + { + _productCache = productCache; + } + + public async Task GetAsync(Guid id) + { + return await _productCache.GetAsync(id); + } +} +``` + +## Configurations + +### Registering the Entity Cache Services + +You can use one of the `AddEntityCache` methods to register entity cache services to the [Dependency Injection](Dependency-Injection.md) system. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var configuration = context.Services.GetConfiguration(); + + //other configurations... + + //directly cache the entity object (Basket) + context.Services.AddEntityCache(); + + //cache the ProductDto class + context.Services.AddEntityCache(); +} +``` + +* You can register entity cache by using the `context.Services.AddEntityCache()` method for directly cache the entity object. +* Or alternatively, you can use the `context.Services.AddEntityCache()` method to configure entities that are mapped to a cache item. + +### Caching Options + +All of the `context.Services.AddEntityCache()` methods get an optional `DistributedCacheEntryOptions` parameter where you can easily configure the caching options: + +```csharp +context.Services.AddEntityCache( + new DistributedCacheEntryOptions + { + SlidingExpiration = TimeSpan.FromMinutes(30) + } +); +``` + +> The default cache duration is **2 minutes** with the `AbsoluteExpirationRelativeToNow` configuration and by configuring the `DistributedCacheEntryOptions` you can change it easily. + +## Additonal Notes + +* Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as mentioned in the *Usage* section above. +* Entity Caching System is designed as **read-only**. So, you shouldn't make changes to the same entity when you use the entity cache. Instead, you should always read it from the database to ensure transactional consistency. + +## See Also + +* [Caching](Caching.md) \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index ef6b0cad07..a0a50766f7 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -193,6 +193,10 @@ "text": "Caching", "path": "Caching.md", "items": [ + { + "text": "Entity Cache", + "path": "Entity-Cache.md" + }, { "text": "Redis Cache", "path": "Redis-Cache.md" From 811e21ba73e3de1cd61441404ec66600868ebe01 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Wed, 30 Nov 2022 19:13:19 +0300 Subject: [PATCH 09/34] Update Entity-Cache.md --- docs/en/Entity-Cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Entity-Cache.md b/docs/en/Entity-Cache.md index e285ff8039..4ab83c9a01 100644 --- a/docs/en/Entity-Cache.md +++ b/docs/en/Entity-Cache.md @@ -10,7 +10,6 @@ ABP's Entity Caching System does the following operations on behalf of you: * It gets the entity from the database (by using the [Repositories](Repositories.md)) in its first call and then gets from the cache in subsequent calls. * It automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached. -* It uses the cache class's **FullName** as a cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. ## Installation @@ -177,6 +176,7 @@ context.Services.AddEntityCache( * Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as mentioned in the *Usage* section above. * Entity Caching System is designed as **read-only**. So, you shouldn't make changes to the same entity when you use the entity cache. Instead, you should always read it from the database to ensure transactional consistency. +* Entity Caching System uses the cache class's **FullName** as the cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. ## See Also From 13f14fd1aba79993e3903ff76c1c05087de769bf Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Thu, 1 Dec 2022 10:23:45 +0300 Subject: [PATCH 10/34] tiny fix --- docs/en/Caching.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 50e8ed0e4e..e39e0ead24 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -258,7 +258,7 @@ ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted. -> See [Entity Cache](Entity-Cache.md) documentation for more information. +> See the [Entity Cache](Entity-Cache.md) documentation for more information. ## Advanced Topics @@ -281,4 +281,4 @@ You can [replace](Dependency-Injection.md) this service by your own implementati ## See Also * [Entity Cache](Entity-Cache.md) -* [Redis Cache](Redis-Cache.md) \ No newline at end of file +* [Redis Cache](Redis-Cache.md) From f09271c830b969947a5805f32970a8a7f97a43a9 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Thu, 1 Dec 2022 10:24:52 +0300 Subject: [PATCH 11/34] tiny fix --- docs/en/Entities.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Entities.md b/docs/en/Entities.md index 3eac4b2164..a1558a73ae 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -322,7 +322,7 @@ ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted. -> See [Entity Cache](Entity-Cache.md) documentation for more information. +> See the [Entity Cache](Entity-Cache.md) documentation for more information. ## Extra Properties From 3b090918652cb9e1061006736017497201c13ef7 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Thu, 1 Dec 2022 10:39:35 +0300 Subject: [PATCH 12/34] quick fix --- docs/en/Entity-Cache.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/en/Entity-Cache.md b/docs/en/Entity-Cache.md index 4ab83c9a01..d0e5fcc06b 100644 --- a/docs/en/Entity-Cache.md +++ b/docs/en/Entity-Cache.md @@ -1,19 +1,19 @@ # Entity Cache -ABP Framework provides **Distributed Entity Caching System** for caching entities. +ABP Framework provides a **Distributed Entity Caching System** for caching entities. You can use this caching mechanism if you want to cache your entity objects automatically and retrieve them from a cache instead of querying it from a database repeatedly. -## How Distributed Entity Caching System Works? +## How Does the Distributed Entity Caching System Work? ABP's Entity Caching System does the following operations on behalf of you: -* It gets the entity from the database (by using the [Repositories](Repositories.md)) in its first call and then gets from the cache in subsequent calls. +* It gets the entity from the database (by using the [Repositories](Repositories.md)) in its first call and then gets it from the cache in subsequent calls. * It automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached. ## Installation -[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package for the ABP's caching system and it's already installed in [the application startup template](Startup-Templates/Index.md). So, you don't need to install it manually. +[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package for ABP's caching system and it's already installed in [the application startup template](Startup-Templates/Index.md). So, you don't need to install it manually. ## Usage @@ -36,7 +36,7 @@ public class Product : AggregateRoot * This example uses the `CacheName` attribute for the `Product` class to set the cache name. By default, the cache class's **FullName** is used for the cache name. -If you want to cache this entity, first you should configure the [dependency injection](Dependency-Injection.md) to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): +If you want to cache this entity, you should first configure the [dependency injection](Dependency-Injection.md) to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): ```csharp context.Services.AddEntityCache(); @@ -78,9 +78,9 @@ public class ProductAppService : ApplicationService, IProductAppService * Here, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable. Sometimes this might not be possible and you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity is not serializable. -### Caching Cache Item Classes +### Caching the Cache Item Classes -`IEntityCache` service can be used for caching other cache item classes if the entity is not serializable. +The `IEntityCache` service can be used for caching other cache item classes if the entity is not serializable. **Example: `ProductDto` class** @@ -154,7 +154,7 @@ public override void ConfigureServices(ServiceConfigurationContext context) } ``` -* You can register entity cache by using the `context.Services.AddEntityCache()` method for directly cache the entity object. +* You can register the entity cache by using the `context.Services.AddEntityCache()` method to directly cache the entity object. * Or alternatively, you can use the `context.Services.AddEntityCache()` method to configure entities that are mapped to a cache item. ### Caching Options @@ -176,8 +176,8 @@ context.Services.AddEntityCache( * Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as mentioned in the *Usage* section above. * Entity Caching System is designed as **read-only**. So, you shouldn't make changes to the same entity when you use the entity cache. Instead, you should always read it from the database to ensure transactional consistency. -* Entity Caching System uses the cache class's **FullName** as the cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. +* The Entity Caching System uses the cache class's **FullName** as the cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. ## See Also -* [Caching](Caching.md) \ No newline at end of file +* [Caching](Caching.md) From 1885d865d48b2f227ec599058fb8504cf6dd746c Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 6 Dec 2022 18:02:32 +0300 Subject: [PATCH 13/34] Filter controller by service-type --- npm/ng-packs/package.json | 2 +- .../schematics/src/commands/api/index.ts | 18 ++++++++++-- .../src/commands/proxy-add/schema.json | 28 +++++++++++++++++++ .../packages/schematics/src/enums/index.ts | 1 + .../schematics/src/enums/service-types.ts | 7 +++++ .../schematics/src/models/api-definition.ts | 2 ++ .../src/models/generate-proxy-schema.ts | 3 ++ 7 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 npm/ng-packs/packages/schematics/src/enums/service-types.ts diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index caf8f6b67d..b540cb4d10 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -36,7 +36,7 @@ "lerna": "lerna", "migrate-nx": "yarn nx migrate --run-migrations", "mock:schematics": "cd scripts/mock-schematic && yarn && yarn start", - "debug:schematics": "./node_modules/.bin/ng g ./packages/schematics/src/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url http://localhost:4300" + "debug:schematics": "./node_modules/.bin/ng g ./packages/schematics/src/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url https://localhost:44305 --serviceType application" }, "private": true, "devDependencies": { diff --git a/npm/ng-packs/packages/schematics/src/commands/api/index.ts b/npm/ng-packs/packages/schematics/src/commands/api/index.ts index a344381b77..fda8117c07 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/api/index.ts @@ -8,8 +8,8 @@ import { Tree, url, } from '@angular-devkit/schematics'; -import { Exception } from '../../enums'; -import { GenerateProxySchema, ServiceGeneratorParams } from '../../models'; +import { defaultEServiceType, eServiceType, Exception } from '../../enums'; +import { Controller, GenerateProxySchema, ServiceGeneratorParams } from '../../models'; import { applyWithOverwrite, buildTargetPath, @@ -47,6 +47,7 @@ export default function (schema: GenerateProxySchema) { const data = readProxyConfig(tree); const types = data.types; const modules = data.modules; + const serviceType = schema.serviceType || defaultEServiceType; if (!types || !modules) throw new SchematicsException(Exception.InvalidApiDefinition); const definition = data.modules[moduleName]; @@ -54,7 +55,7 @@ export default function (schema: GenerateProxySchema) { throw new SchematicsException(interpolate(Exception.InvalidModule, moduleName)); const apiName = definition.remoteServiceName; - const controllers = Object.values(definition.controllers || {}); + const controllers = filterControllersByServiceType(serviceType, definition.controllers); const serviceImports: Record = {}; const generateServices = createServiceGenerator({ targetPath, @@ -170,3 +171,14 @@ function createServiceGenerator(params: ServiceGeneratorParams) { }), ); } + +function filterControllersByServiceType( + serviceType: eServiceType, + controllers: Record, +): Controller[] { + const itShouldBeIntegratedService = serviceType === eServiceType.Integration; + const skipFilter = serviceType === eServiceType.All; + return Object.values(controllers || {}).filter( + x => x.isIntegrationService === itShouldBeIntegratedService || skipFilter, + ); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json index 2cf27f9c33..0458c7c7ab 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json @@ -48,6 +48,34 @@ "index": 4 }, "x-prompt": "Please enter URL for API definition (default: API Name's url in environment file)" + }, + "serviceType": { + "description": "Service type to the generated code", + "type": "string", + "$default": "application", + "enum": [ + "application", + "integration", + "all" + ], + "x-prompt": { + "message": "Specifies the service type to generate. `application`, `integration` and `all`, Default value: `application`", + "type": "list", + "items": [ + { + "value": "all", + "label": "All" + }, + { + "value": "application", + "label": "Application" + }, + { + "value": "integration", + "label": "Integration" + } + ] + } } }, "entryPoint": { diff --git a/npm/ng-packs/packages/schematics/src/enums/index.ts b/npm/ng-packs/packages/schematics/src/enums/index.ts index aee862a5f5..9b2d376cc3 100644 --- a/npm/ng-packs/packages/schematics/src/enums/index.ts +++ b/npm/ng-packs/packages/schematics/src/enums/index.ts @@ -2,3 +2,4 @@ export * from './binding-source-id'; export * from './exception'; export * from './import-keyword'; export * from './method-modifier'; +export * from './service-types'; diff --git a/npm/ng-packs/packages/schematics/src/enums/service-types.ts b/npm/ng-packs/packages/schematics/src/enums/service-types.ts new file mode 100644 index 0000000000..1616aaca31 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/enums/service-types.ts @@ -0,0 +1,7 @@ +export enum eServiceType { + All = 'all', + Application = 'application', + Integration = 'integration', +} + +export const defaultEServiceType = eServiceType.Application; diff --git a/npm/ng-packs/packages/schematics/src/models/api-definition.ts b/npm/ng-packs/packages/schematics/src/models/api-definition.ts index 15f6ef1d0b..01af33ac3e 100644 --- a/npm/ng-packs/packages/schematics/src/models/api-definition.ts +++ b/npm/ng-packs/packages/schematics/src/models/api-definition.ts @@ -31,6 +31,8 @@ export interface Module { export interface Controller { controllerName: string; type: string; + isRemoteService: boolean; + isIntegrationService: boolean; interfaces: InterfaceDef[]; actions: Record; } diff --git a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts index 682cdda9fe..6d4c67bcd8 100644 --- a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts +++ b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts @@ -1,3 +1,5 @@ +import { eServiceType } from '../enums'; + export interface GenerateProxySchema { /** * Backend module name @@ -28,4 +30,5 @@ export interface GenerateProxySchema { * Secondary entrypoint for proxy generation */ entryPoint?: string; + serviceType?: eServiceType; } From ebf15bbc414661b18ddb1c520bea045906d3b0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 7 Dec 2022 11:21:13 +0300 Subject: [PATCH 14/34] Application-Startup --- docs/en/Application-Startup.md | 82 ++++++++++++++----- .../en/Deployment/Distributed-Microservice.md | 2 +- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/docs/en/Application-Startup.md b/docs/en/Application-Startup.md index 5ca971e733..bd693c0761 100644 --- a/docs/en/Application-Startup.md +++ b/docs/en/Application-Startup.md @@ -1,10 +1,10 @@ ## ABP Application Startup -You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application, how it is configured and initialized. The startup template comes with many fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you. +You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application, how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you. -> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read that document only if you want to understand the details or you need to modify how the application starts. +> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or you need to modify how the ABP Framework starts. -While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of hundreds of NuGet and NMP packages, so you can use only the features you need to. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you see how easy to install the ABP Framework into an empty ASP.NET Core project from scratch. You only install a single NuGet package and make a few small changes. +While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NMP packages](https://abp.io/packages), so you can use only the features you need to. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you see how easy to install the ABP Framework into an empty ASP.NET Core project from scratch. You only install a single NuGet package and make a few small changes. This document is for who want to better understand how the ABP Framework is initialized and configured on startup. @@ -16,7 +16,9 @@ If you [create a new console application with Visual Studio](https://learn.micro ![app-startup-console-initial](images/app-startup-console-initial.png) -This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code. The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio: +This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code. + +The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio: ````bash Install-Package Volo.Abp.Core @@ -49,15 +51,15 @@ As the second and the last step, change the `Program.cs` as shown in the followi using MyConsoleDemo; using Volo.Abp; -// 1: Create the application container +// 1: Create the ABP application container using var application = await AbpApplicationFactory.CreateAsync(); -// 2: Initialize/start the ABP Framework and all the modules +// 2: Initialize/start the ABP Framework (and all the modules) await application.InitializeAsync(); Console.WriteLine("ABP Framework has been started..."); -// 3: Stop the ABP Framework and all the modules +// 3: Stop the ABP Framework (and all the modules) await application.ShutdownAsync(); ```` @@ -65,7 +67,7 @@ That's all. Now, ABP Framework is installed, integrated, started and stopped in ## Installing a Framework Package -For example, if you want to send emails from your application, you can use .NET's standard [SmtpClient class](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient). ABP also provides an `IEmailSender` service that simplifies [sending emails](Emailing.md) and configuring the email settings in a central place. If you want to use it, you should install the [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) NuGet package to your project: +If you want to send emails from your .NET application, you can use .NET's standard [SmtpClient class](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient). ABP also provides an `IEmailSender` service that simplifies [sending emails](Emailing.md) and configuring the email settings in a central place. If you want to use it, you should install the [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) NuGet package to your project: ````bash dotnet add package Volo.Abp.Emailing @@ -79,14 +81,14 @@ using Volo.Abp.Modularity; namespace MyConsoleDemo { - [DependsOn(typeof(AbpEmailingModule))] + [DependsOn(typeof(AbpEmailingModule))] // Added the module dependency public class MyConsoleDemoModule : AbpModule { } } ```` -I've just added a `DependsOn` attribute to declare that I want to use the ABP Emailing Module (`AbpEmailingModule`). Now, I can use the `IEmailSender` service in my `Program.cs`: +I've just added a `[DependsOn]` attribute to declare that I want to use the ABP Emailing Module (`AbpEmailingModule`). Now, I can use the `IEmailSender` service in my `Program.cs`: ````csharp using Microsoft.Extensions.DependencyInjection; @@ -108,21 +110,21 @@ await emailsender.SendAsync( await application.ShutdownAsync(); ```` -> If you run this application, you get a runtime error indicating that the email sending settings hasn't been done yet. You can check the [Email Sending](Emailing.md) document to learn how to configure it. +> If you run that application, you get a runtime error indicating that the email sending settings hasn't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it. -That's all. Install an ABP NuGet package, add the module dependency (using the `DependsOn` attribute) and use any service inside the NuGet package. +That's all. Install an ABP NuGet package, add the module dependency (using the `[DependsOn]` attribute) and use any service inside the NuGet package. -The [ABP CLI](CLI.md) already has a special command to perform adding an ABP NuGet and also adding the `DependsOn` attribute to your module class for you with a single command: +The [ABP CLI](CLI.md) already has a special command to perform adding an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command: ````bash abp add-package Volo.Abp.Emailing ```` -We suggest you to use the `add-package` command instead of manually doing it. +We suggest you to use the `abp add-package` command instead of manually doing it. ## AbpApplicationFactory -`AbpApplicationFactory` is the main class that creates an ABP application container. It provides a single `CreateAsync` (and `Create` if you can't use asynchronous programming) method with multiple overloads. Let's investigate these overloads to understand where you can use them. +`AbpApplicationFactory` is the main class that creates an ABP application container. It provides a single static `CreateAsync` (and `Create` if you can't use asynchronous programming) method with multiple overloads. Let's investigate these overloads to understand where you can use them. The first overload gets a generic module class parameter as we've used before in this document: @@ -130,7 +132,9 @@ The first overload gets a generic module class parameter as we've used before in AbpApplicationFactory.CreateAsync(); ```` -The second overload gets the module class as a `Type` parameter, instead of a generic parameter. So, the previous code block can be re-written as shown below: +The generic class parameter should be the root module class of your application. All the other modules are resolved as dependencies of that module. + +The second overload gets the module class as a `Type` parameter, instead of the generic parameter. So, the previous code block could be re-written as shown below: ````csharp AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule)); @@ -138,9 +142,9 @@ AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule)); Both overloads works exactly same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime. -If you create one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to automate the [dependency injection](Dependency-Injection.md) setup from your application code. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system. +If you create one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to setup the [dependency injection](Dependency-Injection.md) system internally. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system. -The next overload gets an `IServiceCollection` parameter from you to allow you to set up the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also setups the dependency injection system internally. +The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also setups the dependency injection system internally. We can change the `Program.cs` as shown below to externally manage the dependency injection setup: @@ -167,13 +171,13 @@ Console.WriteLine("ABP Framework has been started..."); await application.ShutdownAsync(); ```` -In this example, we've used .NET's standard dependency injection container. The `services.BuildServiceProvider()` call creates the standard container. However, ABP provides an alternative extension method, `BuildServiceProviderFromFactory()`, that gracefully work even if you are using another DI container: +In this example, we've used .NET's standard dependency injection container. The `services.BuildServiceProvider()` call creates the standard container. However, ABP provides an alternative extension method, `BuildServiceProviderFromFactory()`, that properly works even if you are using another dependency injection container: ````csharp IServiceProvider serviceProvider = services.BuildServiceProviderFromFactory(); ```` -You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework. +> You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework. Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as like in the following code block: @@ -196,4 +200,40 @@ using var application = await AbpApplicationFactory }); ```` -We've passed a lambda method to configure the `ApplicationName` option. \ No newline at end of file +We've passed a lambda method to configure the `ApplicationName` option. Here, a list of all standard options: + +* `ApplicationName`: A human-readable name for the application. It is a unique value for an application. +* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with internal service provider, you can use this option to configure how the application configuration is built. +* `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins. +* `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class. + +#### The ApplicationName option + +As defined above, the `ApplicationName` option is a human-readable name for the application. It is a unique value for an application. + +`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. + +The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name. + +There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file: + +````json +{ + "ApplicationName": "Services.Ordering" +} +```` + +Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below: + +````csharp +await builder.AddApplicationAsync(options => +{ + options.ApplicationName = "Services.Ordering"; +}); +```` + +#### IApplicationInfoAccessor + +If you need to access the `ApplicationName` later in your solution, you can inject the `IApplicationInfoAccessor` service and get the value from its `ApplicationName` property. + +`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other. \ No newline at end of file diff --git a/docs/en/Deployment/Distributed-Microservice.md b/docs/en/Deployment/Distributed-Microservice.md index 758a41ba0d..450d968d84 100644 --- a/docs/en/Deployment/Distributed-Microservice.md +++ b/docs/en/Deployment/Distributed-Microservice.md @@ -9,7 +9,7 @@ ABP provides `IApplicationInfoAccessor` service that provides the following prop * `ApplicationName`: A human-readable name for an application. It is a unique value for an application. * `InstanceId`: A random (GUID) value generated by the ABP Framework each time you start the application. -These values are used by the ABP Framework for several places to distinguish the application and the application instance (process) in the system. For example, [audit logging](../Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. +These values are used by the ABP Framework in several places to distinguish the application and the application instance (process) in the system. For example, the [audit logging](../Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name. From fdbe1fefd17ea4b410bcd87393fa28eab0bccaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 7 Dec 2022 11:45:02 +0300 Subject: [PATCH 15/34] Added final sections to the startup doc --- docs/en/Application-Startup.md | 32 ++++++++++++++++++- .../Volo.Abp.Core/Volo/Abp/IAbpApplication.cs | 4 +-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/en/Application-Startup.md b/docs/en/Application-Startup.md index bd693c0761..d87760788a 100644 --- a/docs/en/Application-Startup.md +++ b/docs/en/Application-Startup.md @@ -236,4 +236,34 @@ await builder.AddApplicationAsync(options => If you need to access the `ApplicationName` later in your solution, you can inject the `IApplicationInfoAccessor` service and get the value from its `ApplicationName` property. -`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other. \ No newline at end of file +`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other. + +## IAbpApplication + +`AbpApplicationFactory` returns an `IAbpApplication` object from its `CreateAsync` (or `Create`) method. `IAbpApplication` is the main container for an ABP application. It is also registered to the [dependency injection](Dependency-Injection.md) system, so you can inject `IAbpApplication` in your services to use its properties and methods. + +Here, a list of `IAbpApplication` properties you may want to know: + +* `StartupModuleType`: Gets the root module of the application that was used while creating the application container (on the `AbpApplicationFactory.CreateAsync` method). +* `Services`: List of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialize (actually you can add, but it won't have any effect). +* `ServiceProvider`: Reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes. +* `Modules`: A read-only list of all the modules loaded into the current application. Alternatively, you can inject the `IModuleContainer` service if you need to access the module list in your application code. + +The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface, so you can get the `ApplicationName` and `InstanceId` values from it. However, if you only need to access these properties, inject and use the `IApplicationInfoAccessor` service instead. + +`IAbpApplication` is disposable. Always dispose it before exiting from your application. + +## .NET Generic Host & ASP.NET Core Integrations + +`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create ABP application container that is well-integrated to these systems. + +The [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document clearly explains how you can create an ABP application container in an ASP.NET Core application. + +You can also [create a console application](Startup-Templates/Console) to see how it is integrated with .NET Generic Host. + +> Most of the times, you will directly create ABP applications using the ABP CLI's `new` command. So, you don't need to care about these integration details. + +## See Also + +* [Dependency injection](Dependency-Injection.md) +* [Modularity](Module-Development-Basics.md) \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs index 9543e18bd3..485a9882f5 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs @@ -16,14 +16,14 @@ public interface IAbpApplication : Type StartupModuleType { get; } /// - /// List of services registered to this application. + /// List of all service registrations. /// Can not add new services to this collection after application initialize. /// IServiceCollection Services { get; } /// /// Reference to the root service provider used by the application. - /// This can not be used before initialize the application. + /// This can not be used before initializing the application. /// IServiceProvider ServiceProvider { get; } From ae628d736c419e4b83dcde0d903e786f16a7e55d Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 7 Dec 2022 11:49:52 +0300 Subject: [PATCH 16/34] Set default value for service-type on Abp cli --- .../Angular/AngularServiceProxyGenerator.cs | 8 +++----- npm/ng-packs/scripts/copy-packages-to-templates.ts | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs index 0376828fc2..d17545f4cc 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs @@ -76,11 +76,9 @@ public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase', 'template dirs', false); program.option('-p, --template-path ', 'root template path', false); From 51427f288bbace186bab7ae29e488ae4169a5dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 7 Dec 2022 11:55:35 +0300 Subject: [PATCH 17/34] Fix docs-nav.json --- docs/en/docs-nav.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 7e748b3cbc..0c56ec9989 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -1317,7 +1317,7 @@ }, { "text": "Distributed / Microservice Solutions", - "path": "Distributed-Microservice.md" + "path": "Deployment/Distributed-Microservice.md" }, { "text": "Optimizing for Production", From 24b309011c3a54057e948b782371d3d5301769d6 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 7 Dec 2022 13:06:40 +0300 Subject: [PATCH 18/34] Set default value for service-type on Abp cli --- npm/ng-packs/packages/schematics/package.json | 2 +- .../schematics/src/commands/api/schema.json | 34 ++++++++++++++++++- .../src/commands/proxy-add/schema.json | 5 ++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index 9feee2401d..0f77fae4cb 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,7 +1,7 @@ { "name": "@abp/ng.schematics", "version": "7.0.0-rc.2", - "description": "Schematics that works with ABP Backend", + "description": "Schematics that works with ABP Backend. powered by HOLY Odin!", "keywords": [ "schematics" ], diff --git a/npm/ng-packs/packages/schematics/src/commands/api/schema.json b/npm/ng-packs/packages/schematics/src/commands/api/schema.json index ddaf06f001..b5fde3ad79 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/api/schema.json @@ -45,10 +45,42 @@ "type": "string", "$default": { "$source": "argv", - "index": 5 + "index": 4 }, "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + }, + "serviceType": { + "description": "Service type to the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "enum": [ + "application", + "integration", + "all" + ], + "x-prompt": { + "message": "Specifies the service type to generate. `application`, `integration` and `all`, Default value: `application`", + "type": "list", + "items": [ + { + "value": "all", + "label": "All" + }, + { + "value": "application", + "label": "Application" + }, + { + "value": "integration", + "label": "Integration" + } + ] + } } + }, "required": [] } diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json index 0458c7c7ab..ef0c125262 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json @@ -52,7 +52,10 @@ "serviceType": { "description": "Service type to the generated code", "type": "string", - "$default": "application", + "$default": { + "$source": "argv", + "index": 5 + }, "enum": [ "application", "integration", From 6c27dd27676c09398e68c726c0cd9dd3b47dff5b Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:59:42 +0300 Subject: [PATCH 19/34] quick fix --- docs/en/Application-Startup.md | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/en/Application-Startup.md b/docs/en/Application-Startup.md index d87760788a..752afd9c2f 100644 --- a/docs/en/Application-Startup.md +++ b/docs/en/Application-Startup.md @@ -1,16 +1,16 @@ ## ABP Application Startup -You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application, how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you. +You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application or how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you. -> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or you need to modify how the ABP Framework starts. +> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or if you need to modify how the ABP Framework starts. -While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NMP packages](https://abp.io/packages), so you can use only the features you need to. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you see how easy to install the ABP Framework into an empty ASP.NET Core project from scratch. You only install a single NuGet package and make a few small changes. +While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NMP packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only install a single NuGet package and make a few small changes. -This document is for who want to better understand how the ABP Framework is initialized and configured on startup. +This document is for who wants to better understand how the ABP Framework is initialized and configured on startup. ## Installing to a Console Application -A .NET Console application is the minimalist .NET application. So, it is best to show installing the ABP Framework to a console application as a minimalist example. +A .NET Console application is the minimalist .NET application. So, it is best to show the installing of the ABP Framework to a console application as a minimalist example. If you [create a new console application with Visual Studio](https://learn.microsoft.com/en-us/dotnet/core/tutorials/with-visual-studio) (for .NET 7.0 or later), you will see the following solution structure (I named the solution as `MyConsoleDemo`): @@ -18,7 +18,7 @@ If you [create a new console application with Visual Studio](https://learn.micro This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code. -The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio: +The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using the [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio: ````bash Install-Package Volo.Abp.Core @@ -43,7 +43,7 @@ namespace MyConsoleDemo } ```` -This is an empty class deriving from the `AbpModule` class. It is the main class that you will control your application's dependencies and implement your configuration, startup and shutdown logic. For more information, please check the [Modularity](Module-Development-Basics.md) document. +This is an empty class deriving from the `AbpModule` class. It is the main class that you will control your application's dependencies with, and implement your configuration and startup/shutdown logic. For more information, please check the [Modularity](Module-Development-Basics.md) document. As the second and the last step, change the `Program.cs` as shown in the following code block: @@ -110,11 +110,11 @@ await emailsender.SendAsync( await application.ShutdownAsync(); ```` -> If you run that application, you get a runtime error indicating that the email sending settings hasn't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it. +> If you run that application, you get a runtime error indicating that the email sending settings haven't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it. That's all. Install an ABP NuGet package, add the module dependency (using the `[DependsOn]` attribute) and use any service inside the NuGet package. -The [ABP CLI](CLI.md) already has a special command to perform adding an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command: +The [ABP CLI](CLI.md) already has a special command to perform the addition of an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command: ````bash abp add-package Volo.Abp.Emailing @@ -140,11 +140,11 @@ The second overload gets the module class as a `Type` parameter, instead of the AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule)); ```` -Both overloads works exactly same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime. +Both overloads work exactly the same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime. If you create one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to setup the [dependency injection](Dependency-Injection.md) system internally. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system. -The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also setups the dependency injection system internally. +The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also sets up the dependency injection system internally. We can change the `Program.cs` as shown below to externally manage the dependency injection setup: @@ -179,7 +179,7 @@ IServiceProvider serviceProvider = services.BuildServiceProviderFromFactory(); > You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework. -Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as like in the following code block: +Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as in the following code block: ````csharp using var application = await AbpApplicationFactory @@ -190,7 +190,7 @@ using var application = await AbpApplicationFactory ### AbpApplicationCreationOptions -All of the `CreateAsync` overloads can get an optional `Action` parameter to configure the options that is used on the application creation. See the following example: +All of the `CreateAsync` overloads can get an optional `Action` parameter to configure the options that are used on the application creation. See the following example: ````csharp using var application = await AbpApplicationFactory @@ -200,10 +200,10 @@ using var application = await AbpApplicationFactory }); ```` -We've passed a lambda method to configure the `ApplicationName` option. Here, a list of all standard options: +We've passed a lambda method to configure the `ApplicationName` option. Here's a list of all standard options: * `ApplicationName`: A human-readable name for the application. It is a unique value for an application. -* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with internal service provider, you can use this option to configure how the application configuration is built. +* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with an internal service provider, you can use this option to configure how the application configuration is built. * `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins. * `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class. @@ -211,7 +211,7 @@ We've passed a lambda method to configure the `ApplicationName` option. Here, a As defined above, the `ApplicationName` option is a human-readable name for the application. It is a unique value for an application. -`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. +`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) that are saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name. @@ -242,20 +242,20 @@ If you need to access the `ApplicationName` later in your solution, you can inje `AbpApplicationFactory` returns an `IAbpApplication` object from its `CreateAsync` (or `Create`) method. `IAbpApplication` is the main container for an ABP application. It is also registered to the [dependency injection](Dependency-Injection.md) system, so you can inject `IAbpApplication` in your services to use its properties and methods. -Here, a list of `IAbpApplication` properties you may want to know: +Here's a list of `IAbpApplication` properties you may want to know: * `StartupModuleType`: Gets the root module of the application that was used while creating the application container (on the `AbpApplicationFactory.CreateAsync` method). -* `Services`: List of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialize (actually you can add, but it won't have any effect). -* `ServiceProvider`: Reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes. +* `Services`: A list of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialization (you can actually add, but it won't have any effect). +* `ServiceProvider`: A reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes. * `Modules`: A read-only list of all the modules loaded into the current application. Alternatively, you can inject the `IModuleContainer` service if you need to access the module list in your application code. The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface, so you can get the `ApplicationName` and `InstanceId` values from it. However, if you only need to access these properties, inject and use the `IApplicationInfoAccessor` service instead. -`IAbpApplication` is disposable. Always dispose it before exiting from your application. +`IAbpApplication` is disposable. Always dispose of it before exiting your application. ## .NET Generic Host & ASP.NET Core Integrations -`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create ABP application container that is well-integrated to these systems. +`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create an ABP application container that is well-integrated to these systems. The [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document clearly explains how you can create an ABP application container in an ASP.NET Core application. @@ -266,4 +266,4 @@ You can also [create a console application](Startup-Templates/Console) to see ho ## See Also * [Dependency injection](Dependency-Injection.md) -* [Modularity](Module-Development-Basics.md) \ No newline at end of file +* [Modularity](Module-Development-Basics.md) From b134f456abcf3f084f9f9f3bf5f353cf7cae6c64 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Wed, 7 Dec 2022 14:18:04 +0300 Subject: [PATCH 20/34] tiny fix --- docs/en/Deployment/Distributed-Microservice.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/Deployment/Distributed-Microservice.md b/docs/en/Deployment/Distributed-Microservice.md index 450d968d84..9af4a1c839 100644 --- a/docs/en/Deployment/Distributed-Microservice.md +++ b/docs/en/Deployment/Distributed-Microservice.md @@ -1,10 +1,10 @@ # Deploying Distributed / Microservice Solutions -The ABP Framework is designed to consider distributed and microservice systems, where you have multiple applications and/or services communicating internally. All of its features are compatible with distributed scenarios. This document highlights some points you should care when you deploy your distributed or microservice solution. +The ABP Framework is designed to consider distributed and microservice systems, where you have multiple applications and/or services communicating internally. All of its features are compatible with distributed scenarios. This document highlights some points you should care about when you deploy your distributed or microservice solution. ## Application Name & Instance Id -ABP provides `IApplicationInfoAccessor` service that provides the following properties: +ABP provides the `IApplicationInfoAccessor` service that provides the following properties: * `ApplicationName`: A human-readable name for an application. It is a unique value for an application. * `InstanceId`: A random (GUID) value generated by the ABP Framework each time you start the application. @@ -32,7 +32,7 @@ await builder.AddApplicationAsync(options => ## Using a Distributed Event Bus -ABP's [Distributed Event Bus](../Distributed-Event-Bus.md) system provides a standard interface to communicate to other applications and services. While the name is "distributed", the default implementation is in-process. That means, your applications / services can not communicate to each other unless you explicitly configure a distributed event bus provider. +ABP's [Distributed Event Bus](../Distributed-Event-Bus.md) system provides a standard interface to communicate with other applications and services. While the name is "distributed", the default implementation is in-process. That means, your applications / services can not communicate with each other unless you explicitly configure a distributed event bus provider. If you are building a distributed system, then the applications should communicate through an external distributed messaging server. Please follow the [Distributed Event Bus](../Distributed-Event-Bus.md) document to learn how to install and configure your distributed event bus provider. From b74d3fb83bd87ed3671dfa7c00d3966c8c2c3c25 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Wed, 7 Dec 2022 14:30:08 +0300 Subject: [PATCH 21/34] quick fix --- docs/en/Deployment/Optimizing-Production.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/en/Deployment/Optimizing-Production.md b/docs/en/Deployment/Optimizing-Production.md index 596dbd41bb..7e896f9b1c 100644 --- a/docs/en/Deployment/Optimizing-Production.md +++ b/docs/en/Deployment/Optimizing-Production.md @@ -4,14 +4,14 @@ ABP Framework and the startup solution templates are configured well to get the ## Caching Static Contents -The following items are contents those can be cached in the client side (typically in the Browser) or in a CDN server: +The following items are contents that can be cached in the client side (typically in the Browser) or in a CDN server: -* **Static images** can always be cached. Here, you should be careful that if you change an image, use a different file name, or use a versioning query-string parameter, so the browser (or CDN) understands it is changed. -* **CSS and JavaScript files**. ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system always uses query-string versioning parameter and a hash value in the files names for CSS & JavaScript files for [MVC (Razor Pages)](../UI/AspNetCore/Overall.md) UI. So, you can safely cache these files in the client side or in a CDN server. +* **Static images** can always be cached. Here, you should be careful that if you change an image, use a different file name, or use a versioning query-string parameter, so the browser (or CDN) understands it's been changed. +* **CSS and JavaScript files**. ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system always uses a query-string versioning parameter and a hash value in the files names of the CSS & JavaScript files for the [MVC (Razor Pages)](../UI/AspNetCore/Overall.md) UI. So, you can safely cache these files in the client side or in a CDN server. * **Application bundle files** of an [Angular UI](../UI/Angular/Quick-Start.md) application. -* **[Application Localization Endpoint](../API/Application-Localization.md)** can be cached per culture (it already has a `cultureName` query string parameter) if you don't use dynamic localization on the server-side. ABP Commercial's [Language Management](https://commercial.abp.io/modules/Volo.LanguageManagement) module provides dynamic localization. If you are using it, you can't cache that endpoint forever. However, you can still cache it for a while. Applying dynamic localization text changes to the application can delay for a few minutes, even for a few hours in a real life scenario. +* **[Application Localization Endpoint](../API/Application-Localization.md)** can be cached per culture (it already has a `cultureName` query string parameter) if you don't use dynamic localization on the server-side. ABP Commercial's [Language Management](https://commercial.abp.io/modules/Volo.LanguageManagement) module provides dynamic localization. If you're using it, you can't cache that endpoint forever. However, you can still cache it for a while. Applying dynamic localization text changes to the application can delay for a few minutes, even for a few hours in a real life scenario. -There may be more based on your solution structure and deployment environment, but these are the essential points you should consider to client-side cache in a production environment. +There may be more ways based on your solution structure and deployment environment, but these are the essential points you should consider to client-side cache in a production environment. ## Bundling & Minification for MVC (Razor Pages) UI @@ -19,4 +19,4 @@ ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) syste ## Background Jobs -ABP's [Background Jobs](../Background-Jobs.md) system provides an abstraction with a basic implementation to enqueue works and execute them in a background thread. ABP's Default Background Job Manager may not be enough if you are adding too many jobs to the queue and want them to be executed in parallel by multiple servers with a high performance. If you need these, you should consider to configure a dedicated background job software, like [Hangfire](https://www.hangfire.io/). ABP has a pre-built [Hangfire integration](../Background-Jobs-Hangfire.md), so you can switch to Hangfire without changing your application code. +ABP's [Background Jobs](../Background-Jobs.md) system provides an abstraction with a basic implementation to enqueue jobs and execute them in a background thread. ABP's Default Background Job Manager may not be enough if you are adding too many jobs to the queue and want them to be executed in parallel by multiple servers with a high performance. If you need these, you should consider to configure a dedicated background job software, like [Hangfire](https://www.hangfire.io/). ABP has a pre-built [Hangfire integration](../Background-Jobs-Hangfire.md), so you can switch to Hangfire without changing your application code. From eebf351c0e8e1610bcc3bd1b01f6fd8ca84df03a Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 7 Dec 2022 15:01:23 +0300 Subject: [PATCH 22/34] Update package.json --- npm/ng-packs/packages/schematics/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index 0f77fae4cb..6f6ad23d0f 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,7 +1,7 @@ { "name": "@abp/ng.schematics", "version": "7.0.0-rc.2", - "description": "Schematics that works with ABP Backend. powered by HOLY Odin!", + "description": "Schematics that works with ABP Backend.", "keywords": [ "schematics" ], From 08c2d18b1488521f261682e8db7309d269069ab6 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 7 Dec 2022 15:01:38 +0300 Subject: [PATCH 23/34] Update package.json --- npm/ng-packs/packages/schematics/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index 6f6ad23d0f..9feee2401d 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,7 +1,7 @@ { "name": "@abp/ng.schematics", "version": "7.0.0-rc.2", - "description": "Schematics that works with ABP Backend.", + "description": "Schematics that works with ABP Backend", "keywords": [ "schematics" ], From b0e966027c30c0cf4e13d063e5fcff0fbdff8fb5 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Wed, 7 Dec 2022 15:50:49 +0300 Subject: [PATCH 24/34] Cli: Add MauiBlazor to NuGetPackageTargets --- .../Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs index c9b2473bcb..b05a3a08c0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs @@ -16,5 +16,6 @@ public enum NuGetPackageTarget : byte Blazor = 11, IdentityServer = 12, //todo: Rename to AuthServer BlazorServer = 13, - BlazorWebAssembly = 14 + BlazorWebAssembly = 14, + MauiBlazor = 15 } From dbf18d85d11980bfd77d4e883abf00b9923fe831 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Wed, 7 Dec 2022 15:51:18 +0300 Subject: [PATCH 25/34] Cli: Match MauiBlazor when module adding --- .../Volo/Abp/Cli/ProjectModification/ProjectFinder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs index fbd8cdb599..1a72e3d4e8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs @@ -49,7 +49,8 @@ public static class ProjectFinder FindProjectEndsWith(projectFiles, assemblyNames, ".Web") ?? FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Host"); case NuGetPackageTarget.Blazor: - return FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor"); + return FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor") + ?? FindProjectEndsWith(projectFiles, assemblyNames, ".MauiBlazor");; case NuGetPackageTarget.BlazorWebAssembly: var BlazorWebAssemblyTargetProject = FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor"); return BlazorWebAssemblyTargetProject != null && @@ -62,6 +63,8 @@ public static class ProjectFinder BlazorProjectTypeChecker.IsBlazorServerProject(BlazorServerTargetProject) ? BlazorServerTargetProject : null; + case NuGetPackageTarget.MauiBlazor: + return FindProjectEndsWith(projectFiles, assemblyNames, ".MauiBlazor"); default: return null; } From a9eda803cfc87bdb44d7d809d2cee346111cedeb Mon Sep 17 00:00:00 2001 From: malik masis Date: Wed, 7 Dec 2022 16:22:12 +0300 Subject: [PATCH 26/34] Update DefaultHomePageMiddleware.cs --- .../DefaultHomePageMiddleware.cs | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs index bb30a5276c..297f765bbc 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs @@ -1,28 +1,24 @@ -using System.Threading.Tasks; +using System.Net; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; using Volo.CmsKit.Pages; using Volo.CmsKit.Public.Pages; namespace Volo.CmsKit.Public.Web; -public class DefaultHomePageMiddleware +public class DefaultHomePageMiddleware : IMiddleware, ITransientDependency { - private readonly RequestDelegate _next; - private readonly IPagePublicAppService _pagePublicAppService; - - public DefaultHomePageMiddleware(RequestDelegate next, IPagePublicAppService pagePublicAppService) - { - _next = next; - _pagePublicAppService = pagePublicAppService; - } - public async Task InvokeAsync(HttpContext httpContext) + public async Task InvokeAsync(HttpContext context, RequestDelegate next) { + var _pagePublicAppService = context.RequestServices.GetRequiredService(); var page = await _pagePublicAppService.FindDefaultHomePageAsync(); - if (page is not null && httpContext.Request.Path.Value == "/") + if (page is not null && context.Request.Path.Value == "/") { - httpContext.Request.Path = $"{PageConsts.UrlPrefix}{page.Slug}"; + context.Request.Path = $"{PageConsts.UrlPrefix}{page.Slug}"; } - - await _next(httpContext); + + await next(context); } } \ No newline at end of file From 69fa432e333fc1d11f14b6a89f9dd17f2a868bb8 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:49:00 +0000 Subject: [PATCH 27/34] Update Application-Startup.md --- docs/en/Application-Startup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Application-Startup.md b/docs/en/Application-Startup.md index 752afd9c2f..1c5bf9b139 100644 --- a/docs/en/Application-Startup.md +++ b/docs/en/Application-Startup.md @@ -4,7 +4,7 @@ You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Get > It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or if you need to modify how the ABP Framework starts. -While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NMP packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only install a single NuGet package and make a few small changes. +While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NPM packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only need to install a single NuGet package and make a few small changes. This document is for who wants to better understand how the ABP Framework is initialized and configured on startup. From 3e05ed735f018947d986188deda14239b92ee969 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 8 Dec 2022 09:32:54 +0800 Subject: [PATCH 28/34] Register `HttpClient` in the necessary modules. Resolve #14998 --- .../AbpAspNetCoreAuthenticationOpenIdConnectModule.cs | 8 ++++++-- .../Server/AbpAspNetCoreComponentsServerModule.cs | 1 + .../AbpAspNetCoreComponentsWebAssemblyModule.cs | 1 + .../Volo/Abp/Http/Client/AbpHttpClientModule.cs | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs index 6b66a105d3..ba21ef11fd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.AspNetCore.Authentication.OAuth; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Authentication.OAuth; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.RemoteServices; @@ -12,5 +13,8 @@ namespace Volo.Abp.AspNetCore.Authentication.OpenIdConnect; )] public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClient(); + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs index dfa515c1b8..d39a633e0e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs @@ -26,6 +26,7 @@ public class AbpAspNetCoreComponentsServerModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + context.Services.AddHttpClient(); var serverSideBlazorBuilder = context.Services.AddServerSideBlazor(options => { if (context.Services.GetHostingEnvironment().IsDevelopment()) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs index 229831141e..12877f0c31 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs @@ -35,6 +35,7 @@ public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { + context.Services.AddHttpClient(); context.Services .GetHostBuilder().Logging .AddProvider(new AbpExceptionHandlingLoggerProvider(context.Services)); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs index 8bca3702c5..a164f9587e 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs @@ -23,6 +23,7 @@ public class AbpHttpClientModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + context.Services.AddHttpClient(); context.Services.AddTransient(typeof(DynamicHttpProxyInterceptorClientProxy<>)); } } From 87bf3211d1a23a8ef7da701334f8f3f179b36236 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 8 Dec 2022 10:08:19 +0800 Subject: [PATCH 29/34] Upgrade OpenIddict to 4.0 rc1. --- .../Volo.Abp.OpenIddict.AspNetCore.csproj | 6 ++--- .../AbpOpenIddictAspNetCoreModule.cs | 22 +++++++++---------- .../Volo.Abp.OpenIddict.Domain.Shared.csproj | 2 +- .../Volo.Abp.OpenIddict.Domain.csproj | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj index ea725aa21f..b095446804 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj @@ -20,8 +20,8 @@ - - - + + + diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs index e8196627ce..d7eacfa498 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs @@ -54,19 +54,19 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule .AddServer(builder => { builder - .SetAuthorizationEndpointUris("/connect/authorize", "/connect/authorize/callback") - // /.well-known/oauth-authorization-server - // /.well-known/openid-configuration + .SetAuthorizationEndpointUris("connect/authorize", "connect/authorize/callback") + // .well-known/oauth-authorization-server + // .well-known/openid-configuration //.SetConfigurationEndpointUris() - // /.well-known/jwks + // .well-known/jwks //.SetCryptographyEndpointUris() - .SetDeviceEndpointUris("/device") - .SetIntrospectionEndpointUris("/connect/introspect") - .SetLogoutEndpointUris("/connect/logout") - .SetRevocationEndpointUris("/connect/revocat") - .SetTokenEndpointUris("/connect/token") - .SetUserinfoEndpointUris("/connect/userinfo") - .SetVerificationEndpointUris("/connect/verify"); + .SetDeviceEndpointUris("device") + .SetIntrospectionEndpointUris("connect/introspect") + .SetLogoutEndpointUris("connect/logout") + .SetRevocationEndpointUris("connect/revocat") + .SetTokenEndpointUris("connect/token") + .SetUserinfoEndpointUris("connect/userinfo") + .SetVerificationEndpointUris("connect/verify"); builder .AllowAuthorizationCodeFlow() diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj index 040d7a986f..e71bfbb209 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj @@ -14,7 +14,7 @@ - + diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj index 3da97bf272..66a5626545 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj @@ -17,7 +17,7 @@ - + From 0f500d56fd12271bf699dfa3fa838602a399d82a Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 8 Dec 2022 13:07:57 +0800 Subject: [PATCH 30/34] Rename address to uris. --- .../AbpOpenIddictApplicationStore.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs index 1f740ecfcd..eb0c4033f9 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs @@ -75,31 +75,31 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase FindByPostLogoutRedirectUriAsync(string address, [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable FindByPostLogoutRedirectUriAsync(string uris, [EnumeratorCancellation] CancellationToken cancellationToken) { - Check.NotNullOrEmpty(address, nameof(address)); + Check.NotNullOrEmpty(uris, nameof(uris)); - var applications = await Repository.FindByPostLogoutRedirectUriAsync(address, cancellationToken); + var applications = await Repository.FindByPostLogoutRedirectUriAsync(uris, cancellationToken); foreach (var application in applications) { var addresses = await GetPostLogoutRedirectUrisAsync(application.ToModel(), cancellationToken); - if (addresses.Contains(address, StringComparer.Ordinal)) + if (addresses.Contains(uris, StringComparer.Ordinal)) { yield return application.ToModel(); } } } - public async IAsyncEnumerable FindByRedirectUriAsync(string address, [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable FindByRedirectUriAsync(string uri, [EnumeratorCancellation] CancellationToken cancellationToken) { - Check.NotNullOrEmpty(address, nameof(address)); + Check.NotNullOrEmpty(uri, nameof(uri)); - var applications = await Repository.FindByRedirectUriAsync(address, cancellationToken); + var applications = await Repository.FindByRedirectUriAsync(uri, cancellationToken); foreach (var application in applications) { - var addresses = await GetRedirectUrisAsync(application.ToModel(), cancellationToken); - if (addresses.Contains(address, StringComparer.Ordinal)) + var uris = await GetRedirectUrisAsync(application.ToModel(), cancellationToken); + if (uris.Contains(uri, StringComparer.Ordinal)) { yield return application.ToModel(); } @@ -423,12 +423,12 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase addresses, + public virtual ValueTask SetPostLogoutRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray uris, CancellationToken cancellationToken) { Check.NotNull(application, nameof(application)); - if (addresses.IsDefaultOrEmpty) + if (uris.IsDefaultOrEmpty) { application.PostLogoutRedirectUris = null; return default; @@ -437,9 +437,9 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase { writer.WriteStartArray(); - foreach (var address in addresses) + foreach (var uri in uris) { - writer.WriteStringValue(address); + writer.WriteStringValue(uri); } writer.WriteEndArray(); }); @@ -472,12 +472,12 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase addresses, + public virtual ValueTask SetRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray uris, CancellationToken cancellationToken) { Check.NotNull(application, nameof(application)); - if (addresses.IsDefaultOrEmpty) + if (uris.IsDefaultOrEmpty) { application.RedirectUris = null; return default; @@ -486,9 +486,9 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase { writer.WriteStartArray(); - foreach (var address in addresses) + foreach (var uri in uris) { - writer.WriteStringValue(address); + writer.WriteStringValue(uri); } writer.WriteEndArray(); }); From 467ee14fc67b5fe86f8f3e46dd5ebe162d1573db Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 8 Dec 2022 13:18:11 +0800 Subject: [PATCH 31/34] Update `.NET 6.0` to `.NET 7.0`. --- docs/en/Getting-Started-Setup-Environment.md | 2 +- docs/en/Tutorials/Todo/Index.md | 2 +- docs/en/Tutorials/Todo/Single-Layer/Index.md | 2 +- docs/zh-Hans/Getting-Started-Setup-Environment.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/Getting-Started-Setup-Environment.md b/docs/en/Getting-Started-Setup-Environment.md index 1a47c67d34..f6df09badf 100644 --- a/docs/en/Getting-Started-Setup-Environment.md +++ b/docs/en/Getting-Started-Setup-Environment.md @@ -19,7 +19,7 @@ First things first! Let's setup your development environment before creating the The following tools should be installed on your development machine: -* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. +* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development. {{ if UI != "Blazor" }} * [Node v16 or v18](https://nodejs.org/) * [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [1](#f-yarn) or npm v6+ (already installed with Node) diff --git a/docs/en/Tutorials/Todo/Index.md b/docs/en/Tutorials/Todo/Index.md index c036b2068b..c51a0206f3 100644 --- a/docs/en/Tutorials/Todo/Index.md +++ b/docs/en/Tutorials/Todo/Index.md @@ -16,7 +16,7 @@ You can find the source code of the completed application [here](https://github. ## Pre-Requirements -* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. +* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development. * [Node v16.x](https://nodejs.org/) {{if DB=="Mongo"}} diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index b257ebbe01..bc7fbb9a05 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -16,7 +16,7 @@ You can find the source code of the completed application [here](https://github. ## Pre-Requirements -* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. +* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development. * [Node v16.x](https://nodejs.org/) {{if DB=="Mongo"}} diff --git a/docs/zh-Hans/Getting-Started-Setup-Environment.md b/docs/zh-Hans/Getting-Started-Setup-Environment.md index 8b6312d2da..0543af92b4 100644 --- a/docs/zh-Hans/Getting-Started-Setup-Environment.md +++ b/docs/zh-Hans/Getting-Started-Setup-Environment.md @@ -19,7 +19,7 @@ 开发计算机上应安装以下工具: -* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) 的开发. +* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) 的开发. {{ if UI != "Blazor" }} * [Node v16 或 v18](https://nodejs.org/) * [Yarn v1.20+ (不是v2)](https://classic.yarnpkg.com/en/docs/install) [1](#f-yarn) 或 npm v6+ (已跟随Node一起安装) From 4cc7b62499de58a7924a953f46592742d938f363 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 8 Dec 2022 13:46:23 +0800 Subject: [PATCH 32/34] Remove `JsonInclude`. --- .../Volo/Abp/TestApp/Testing/EntityCache_Tests.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs index 72492238d4..e61af2dbfb 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs @@ -102,9 +102,6 @@ public class Product : FullAuditedAggregateRoot Price = price; } - [JsonInclude] - public override Guid Id { get; protected set; } - public string Name { get; set; } public decimal Price { get; set; } @@ -119,4 +116,4 @@ public class ProductCacheItem public string Name { get; set; } public decimal Price { get; set; } -} \ No newline at end of file +} From 2f6d886dc2549ac09b1372e4a49f5f8f4504abc6 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 8 Dec 2022 15:02:35 +0800 Subject: [PATCH 33/34] Change theme for MauiBlazor --- .../Building/Steps/ChangeThemeStep.cs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs index 08aafba89a..39bda320fb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs @@ -225,6 +225,12 @@ public class ChangeThemeStep : ProjectBuildPipelineStep ChangeThemeToLeptonForNoLayersBlazorServerProjects(context); #endregion + + #region MyCompanyName.MyProjectName.MauiBlazor + + ChangeThemeToLeptonForMauiBlazorProjects(context); + + #endregion } private void ConfigureLeptonManagementPackagesForNoLayersMvc(ProjectBuildContext context, string targetProjectPath, string[] projectNames) @@ -488,6 +494,33 @@ public class ChangeThemeStep : ProjectBuildPipelineStep file.SetLines(lines); } + protected void ChangeKeyword( + ProjectBuildContext context, + string targetModuleFilePath, + string oldKeyword, + string newKeyword) + { + var file = context.Files.FirstOrDefault(x => x.Name.Contains(targetModuleFilePath)); + if (file == null) + { + return; + } + + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + + for (var i = 0; i < lines.Length; i++) + { + if (lines[i].Contains(oldKeyword)) + { + lines[i] = lines[i].Replace(oldKeyword, newKeyword); + } + } + + file.SetLines(lines); + } + protected void ReplaceImportPackage( ProjectBuildContext context, string filePath, @@ -973,4 +1006,53 @@ public class ChangeThemeStep : ProjectBuildPipelineStep ); } } + + private void ChangeThemeToLeptonForMauiBlazorProjects(ProjectBuildContext context) + { + ReplacePackageReferenceWithProjectReference( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/MyCompanyName.MyProjectName.MauiBlazor.csproj", + "Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme", + @"..\..\..\..\..\lepton-theme\src\Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme\Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme.csproj" + ); + + ChangeNamespaceAndKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/MyProjectNameMauiBlazorModule.cs", + "Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme", + "Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme", + "AbpAspNetCoreComponentsMauiBlazorLeptonXThemeModule", + "AbpAspNetCoreComponentsMauiBlazorLeptonThemeModule" + ); + + ChangeKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/MainPage.xaml", + "clr-namespace:Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components;assembly=Volo.Abp.AspNetCore.Components.Web.LeptonXTheme", + "clr-namespace:Volo.Abp.AspNetCore.Components.Web.LeptonTheme.Components;assembly=Volo.Abp.AspNetCore.Components.Web.LeptonTheme"); + + ChangeKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/MainPage.xaml", + "leptonXTheme", + "leptonTheme"); + + ChangeKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/Login.razor", + "Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme.Components.AccountLayout", + "Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme.Components.AccountLayout"); + + ChangeKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/RedirectToLogout.razor", + "LeptonXResource", + "LeptonThemeManagementResource"); + + ChangeKeyword( + context, + "/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/RedirectToLogout.razor", + "Volo.Abp.LeptonX.Shared.Localization", + "Volo.Abp.LeptonTheme.Management.Localization"); + } } From acc74b3ac208c7f465a92acee809161edda8c372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 8 Dec 2022 10:02:51 +0300 Subject: [PATCH 34/34] Update Entity-Cache.md --- docs/en/Entity-Cache.md | 104 ++++++++++------------------------------ 1 file changed, 26 insertions(+), 78 deletions(-) diff --git a/docs/en/Entity-Cache.md b/docs/en/Entity-Cache.md index d0e5fcc06b..834a0d5313 100644 --- a/docs/en/Entity-Cache.md +++ b/docs/en/Entity-Cache.md @@ -1,30 +1,15 @@ # Entity Cache -ABP Framework provides a **Distributed Entity Caching System** for caching entities. +ABP Framework provides an entity caching system that works on top of the [distributed caching](Caching.md) system. It does the following operations on behalf of you: -You can use this caching mechanism if you want to cache your entity objects automatically and retrieve them from a cache instead of querying it from a database repeatedly. +* Gets the entity from the database (by using the [repositories](Repositories.md)) in its first call and then gets it from the cache in subsequent calls. +* Automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached. -## How Does the Distributed Entity Caching System Work? +## Caching Entity Objects -ABP's Entity Caching System does the following operations on behalf of you: - -* It gets the entity from the database (by using the [Repositories](Repositories.md)) in its first call and then gets it from the cache in subsequent calls. -* It automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached. - -## Installation - -[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package for ABP's caching system and it's already installed in [the application startup template](Startup-Templates/Index.md). So, you don't need to install it manually. - -## Usage - -`IEntityCache` is a simple service provided by the ABP Framework for caching entities. It's designed as read-only and contains two methods: `FindAsync` and `GetAsync`. - -### Caching Entities - -**Example: `Product` entity** +`IEntityCache` is a simple service provided by the ABP Framework for caching entities. Assume that you have a `Product` entity as shown below: ```csharp -[CacheName("Products")] public class Product : AggregateRoot { public string Name { get; set; } @@ -34,28 +19,12 @@ public class Product : AggregateRoot } ``` -* This example uses the `CacheName` attribute for the `Product` class to set the cache name. By default, the cache class's **FullName** is used for the cache name. - -If you want to cache this entity, you should first configure the [dependency injection](Dependency-Injection.md) to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): +If you want to cache this entity, you should first configure the [dependency injection](Dependency-Injection.md) system to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): ```csharp context.Services.AddEntityCache(); ``` -Then configure the [object mapper](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping) (for `Product` to `ProductDto` mapping): - -```csharp -public class MyProjectNameAutoMapperProfile : Profile -{ - public MyProjectNameAutoMapperProfile() - { - //other mappings... - - CreateMap(); - } -} -``` - Now you can inject the `IEntityCache` service wherever you need: ```csharp @@ -76,13 +45,15 @@ public class ProductAppService : ApplicationService, IProductAppService } ``` -* Here, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable. Sometimes this might not be possible and you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity is not serializable. +> Note that we've used the `ObjectMapper` service to map from `Product` to `ProductDto`. You should configure that [object mapping](Object-To-Object-Mapping.md) to make that example service properly works. -### Caching the Cache Item Classes +That's all. The cache name (in the distributed cache server) will be full name (with namespace) of the `Product` class. You can use the `[CacheName]` attribute to change it. Please refer to the [caching document](Caching.md) for details. -The `IEntityCache` service can be used for caching other cache item classes if the entity is not serializable. +## Using a Cache Item Class -**Example: `ProductDto` class** +In the previous section, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable to JSON (and deserializable from JSON). Sometimes that might not be possible or you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity. + +Assume that we've created a `ProductDto` class as shown below: ```csharp public class ProductDto : EntityDto @@ -94,27 +65,25 @@ public class ProductDto : EntityDto } ``` -Register the entity cache services to [dependency injection](Dependency-Injection.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md): +Now, we can register the entity cache services to [dependency injection](Dependency-Injection.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md) with three generic parameters, as shown below: ```csharp context.Services.AddEntityCache(); ``` -Configure the [object mapper](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping) (for `Product` to `ProductDto` mapping): +Since the entity cache system will perform the [object mapping](Object-To-Object-Mapping.md) (from `Product` to `ProductDto`), we should configure the object map. Here, an example configuration with [AutoMapper](https://automapper.org/): ```csharp -public class MyProjectNameAutoMapperProfile : Profile +public class MyMapperProfile : Profile { - public MyProjectNameAutoMapperProfile() + public MyMapperProfile() { - //other mappings... - CreateMap(); } } ``` -Then, you can inject the `IEntityCache` service wherever you want: +Now, you can inject the `IEntityCache` service wherever you want: ```csharp public class ProductAppService : ApplicationService, IProductAppService @@ -133,31 +102,9 @@ public class ProductAppService : ApplicationService, IProductAppService } ``` -## Configurations - -### Registering the Entity Cache Services - -You can use one of the `AddEntityCache` methods to register entity cache services to the [Dependency Injection](Dependency-Injection.md) system. - -```csharp -public override void ConfigureServices(ServiceConfigurationContext context) -{ - var configuration = context.Services.GetConfiguration(); - - //other configurations... - - //directly cache the entity object (Basket) - context.Services.AddEntityCache(); - - //cache the ProductDto class - context.Services.AddEntityCache(); -} -``` - -* You can register the entity cache by using the `context.Services.AddEntityCache()` method to directly cache the entity object. -* Or alternatively, you can use the `context.Services.AddEntityCache()` method to configure entities that are mapped to a cache item. +Notice that the `_productCache.GetAsync` method already returns a `ProductDto` object, so we could directly return it from out application service. -### Caching Options +## Configuration All of the `context.Services.AddEntityCache()` methods get an optional `DistributedCacheEntryOptions` parameter where you can easily configure the caching options: @@ -170,14 +117,15 @@ context.Services.AddEntityCache( ); ``` -> The default cache duration is **2 minutes** with the `AbsoluteExpirationRelativeToNow` configuration and by configuring the `DistributedCacheEntryOptions` you can change it easily. +> The default cache duration is **2 minutes** with the `AbsoluteExpirationRelativeToNow` configuration. -## Additonal Notes +## Additional Notes -* Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as mentioned in the *Usage* section above. -* Entity Caching System is designed as **read-only**. So, you shouldn't make changes to the same entity when you use the entity cache. Instead, you should always read it from the database to ensure transactional consistency. -* The Entity Caching System uses the cache class's **FullName** as the cache name by default. You can use the `CacheName` attribute on the cache item class to set the cache name. +* Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as explained before. +* Entity Caching System is designed as **read-only**. You should use the standard [repository](Repositories.md) methods to manipulate the entity if you need. If you need to manipulate (update) the entity, do not get it from the entity cache. Instead, read it from the repository, change it and update using the repository. ## See Also -* [Caching](Caching.md) +* [Distributed caching](Caching.md) +* [Entities](Entities.md) +* [Repositories](Repositories.md)