diff --git a/docs/en/Domain-Driven-Design-Implementation-Guide.md b/docs/en/Domain-Driven-Design-Implementation-Guide.md
index 0d001c0b58..84b7f186a8 100644
--- a/docs/en/Domain-Driven-Design-Implementation-Guide.md
+++ b/docs/en/Domain-Driven-Design-Implementation-Guide.md
@@ -108,7 +108,7 @@ The Application Layer is also splitted into two projects;
#### The Presentation Layer
-* `IssueTracking.Web` is an ASP.NET Core MVC / Razor Pages application for this example.
+* `IssueTracking.Web` is an ASP.NET Core MVC / Razor Pages application for this example. This is the only executable application that serves the application and the APIs.
> ABP Framework also supports different kind of UI frameworks including [Angular](UI/Angular/Quick-Start.md) and [Blazor](UI/Blazor/Overall.md). In these cases, the `IssueTracking.Web` doesn't exist in the solution. Instead, an `IssueTracking.HttpApi.Host` application will be in the solution to serve the HTTP APIs as a standalone endpoint to be consumed by the UI applications via HTTP API calls.
@@ -130,7 +130,7 @@ We suggest a balanced approach; Create separate projects for main infrastructure
ABP's startup solution has two projects for the Entity Framework Core integration;
-* `IssueTracking.EntityFrameworkCore` is the essential integration package for the EF Core. Your application's `DbContext`, database mappings and other EF Core related stuff are located here.
+* `IssueTracking.EntityFrameworkCore` is the essential integration package for the EF Core. Your application's `DbContext`, database mappings, implementations of the repositories and other EF Core related stuff are located here.
* `IssueTracking.EntityFrameworkCore.DbMigrations` is a special project to manage the Code First database migrations. There is a separate `DbContext` in this project to track the migrations. You typically don't touch this project much except you need to create a new database migration or add an [application module](Modules/Index.md) that has some database tables and naturally requires to create a new database migration.
> You may wonder why there are two projects for the EF Core. It is mostly related to [modularity](Module-Development-Basics.md). Each module has its own independent `DbContext` and your application has also one `DbContext`. `DbMigrations` project contains a **union** of the modules to track and apply a **single migration path**. While most of the times you don't need to know it, you can see the [EF Core migrations](Entity-Framework-Core-Migrations.md) document for more information.
@@ -141,7 +141,31 @@ There is one more project, `IssueTracking.DbMigrator`, that is a simple Console
### Dependencies of the Projects in the Solution
-TODO
+The diagram below shows the essential dependencies (project references) between the projects in the solution (`IssueTracking.` part is not shown to be simple)
+
+
+
+The projects have been explained before. Now, we can explain the reasons of the dependencies;
+
+* `Domain.Shared` is the project all other projects directly or indirectly depend on. So, all the types in this project are available to all projects.
+* `Domain` only depends on the `Domain.Shared` because it is already a (shared) part of the domain. For example, an `IssueType` enum in the `Domain.Shared` can be used by an `Issue` entity in the `Domain` project.
+* `Application.Contracts` depends on the `Domain.Shared`. In this way, you can reuse these types in the DTOs. For example, the same `IssueType` enum in the `Domain.Shared` can be used by a `CreateIssueDto` as a property.
+* `Application` depends on the `Application.Contracts` since it implements the Application Service interfaces and uses the DTOs inside it. It also depends on the `Domain` since the Application Services are implemented using the Domain Objects defined inside it.
+* `EntityFrameworkCore` depends on the `Domain` since it maps the Domain Objects (entities and value types) to database tables (as it is an ORM) and implements the repository interfaces defined in the `Domain`.
+* `HttpApi` depends on the `Application.Contacts` since the Controllers inside it inject and use the Application Service interfaces as explained before.
+* `HttpApi.Client` depends on the `Application.Contacts` since it can consume the Application Services as explained before.
+* `Web` depends on the `HttpApi` since it serves the HTTP APIs defined inside it. Also, in this way, it indirectly depends on the `Application.Contacts` project to consume the Application Services in the Pages/Components.
+
+#### Dashed Dependencies
+
+When you investigate the solution, you will see two more dependencies shown with the dashed lines in the figure above. `Web` project depends on the `Application` and `EntityFrameworkCore` projects which *theoretically* should not be like that but actually it is.
+
+This is because the `Web` is the final project that runs and hosts the application and the **application needs to the implementations of the Application Services and the Repositories** while running.
+
+This design decision potentially allows you to use Entities and EF Core objects in the Presentation Layer which is **should be strictly avoided**. However, we find the alternative designs over complicated. Here, two of the alternatives if you want to remove this dependency;
+
+* Convert `Web` project to a razor class library and create a new project, like `Web.Host`, that depends on the `Web`, `Application` and `EntityFrameworkCore` projects and hosts the application. You don't write any UI code here, but use **only for hosting**.
+* Remove `Application` and `EntityFrameworkCore` dependencies from the `Web` project and load their assemblies on application initialization. You can use ABP's [Plug-In Modules](PlugIn-Modules.md) system for that purpose.
### Execution Flow a DDD Based Application
diff --git a/docs/en/PlugIn-Modules.md b/docs/en/PlugIn-Modules.md
new file mode 100644
index 0000000000..19aa72734d
--- /dev/null
+++ b/docs/en/PlugIn-Modules.md
@@ -0,0 +1,3 @@
+# Plug-In Modules
+
+TODO
\ No newline at end of file
diff --git a/docs/en/_resources/Diagrams.docx b/docs/en/_resources/Diagrams.docx
index 424e281470..15cb38bab2 100644
Binary files a/docs/en/_resources/Diagrams.docx and b/docs/en/_resources/Diagrams.docx differ
diff --git a/docs/en/images/domain-driven-design-project-relations.png b/docs/en/images/domain-driven-design-project-relations.png
new file mode 100644
index 0000000000..b7878d1385
Binary files /dev/null and b/docs/en/images/domain-driven-design-project-relations.png differ
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor
index 8edd037e15..a1a60b2c9c 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor
+++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs
index 7413b59009..0b527d6de2 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs
+++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
+using Blazorise;
using Blazorise.Snackbar;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
@@ -47,7 +48,7 @@ namespace Volo.Abp.BlazoriseUI.Components
UiNotificationService.NotificationReceived += OnNotificationReceived;
}
- protected virtual void OnNotificationReceived(object sender, UiNotificationEventArgs e)
+ protected virtual async void OnNotificationReceived(object sender, UiNotificationEventArgs e)
{
NotificationType = e.NotificationType;
Message = e.Message;
@@ -56,7 +57,11 @@ namespace Volo.Abp.BlazoriseUI.Components
var okButtonText = Options?.OkButtonText?.Localize(StringLocalizerFactory);
- SnackbarStack.Push(Message, GetSnackbarColor( e.NotificationType ), okButtonText);
+ await SnackbarStack.PushAsync(Message, Title, GetSnackbarColor( e.NotificationType ), (options) =>
+ {
+ options.CloseButtonIcon = IconName.Times;
+ options.ActionButtonText = okButtonText;
+ });
}
public virtual void Dispose()
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
index bd69ecdf35..b87f698e0b 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
+++ b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
@@ -12,9 +12,9 @@
-
-
-
+
+
+
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs
index 4f7d1396af..93beb2b073 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs
@@ -1,23 +1,27 @@
using System;
using System.Collections.Generic;
-using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending.Modularity
{
public static class ModuleExtensionConfigurationHelper
{
+ private static object SyncLock = new object();
+
public static void ApplyEntityConfigurationToEntity(
string moduleName,
string entityName,
Type entityType)
{
- foreach (var propertyConfig in GetPropertyConfigurations(moduleName, entityName))
+ lock (SyncLock)
{
- if (propertyConfig.Entity.IsAvailable &&
- entityType != null)
+ foreach (var propertyConfig in GetPropertyConfigurations(moduleName, entityName))
{
- ApplyPropertyConfigurationToTypes(propertyConfig, new[] { entityType });
+ if (propertyConfig.Entity.IsAvailable &&
+ entityType != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, new[] { entityType });
+ }
}
}
}
@@ -29,29 +33,32 @@ namespace Volo.Abp.ObjectExtending.Modularity
Type[] createApiTypes = null,
Type[] updateApiTypes = null)
{
- foreach (var propertyConfig in GetPropertyConfigurations(moduleName, objectName))
+ lock (SyncLock)
{
- if (!propertyConfig.IsAvailableToClients)
+ foreach (var propertyConfig in GetPropertyConfigurations(moduleName, objectName))
{
- continue;
- }
+ if (!propertyConfig.IsAvailableToClients)
+ {
+ continue;
+ }
- if (propertyConfig.Api.OnGet.IsAvailable &&
- getApiTypes != null)
- {
- ApplyPropertyConfigurationToTypes(propertyConfig, getApiTypes);
- }
+ if (propertyConfig.Api.OnGet.IsAvailable &&
+ getApiTypes != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, getApiTypes);
+ }
- if (propertyConfig.Api.OnCreate.IsAvailable &&
- createApiTypes != null)
- {
- ApplyPropertyConfigurationToTypes(propertyConfig, createApiTypes);
- }
+ if (propertyConfig.Api.OnCreate.IsAvailable &&
+ createApiTypes != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, createApiTypes);
+ }
- if (propertyConfig.Api.OnUpdate.IsAvailable &&
- updateApiTypes != null)
- {
- ApplyPropertyConfigurationToTypes(propertyConfig, updateApiTypes);
+ if (propertyConfig.Api.OnUpdate.IsAvailable &&
+ updateApiTypes != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, updateApiTypes);
+ }
}
}
}
@@ -62,23 +69,26 @@ namespace Volo.Abp.ObjectExtending.Modularity
Type[] createFormTypes = null,
Type[] editFormTypes = null)
{
- foreach (var propertyConfig in GetPropertyConfigurations(moduleName, entityName))
+ lock (SyncLock)
{
- if (!propertyConfig.IsAvailableToClients)
+ foreach (var propertyConfig in GetPropertyConfigurations(moduleName, entityName))
{
- continue;
- }
+ if (!propertyConfig.IsAvailableToClients)
+ {
+ continue;
+ }
- if (propertyConfig.UI.OnCreateForm.IsVisible &&
- createFormTypes != null)
- {
- ApplyPropertyConfigurationToTypes(propertyConfig, createFormTypes);
- }
+ if (propertyConfig.UI.OnCreateForm.IsVisible &&
+ createFormTypes != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, createFormTypes);
+ }
- if (propertyConfig.UI.OnEditForm.IsVisible &&
- editFormTypes != null)
- {
- ApplyPropertyConfigurationToTypes(propertyConfig, editFormTypes);
+ if (propertyConfig.UI.OnEditForm.IsVisible &&
+ editFormTypes != null)
+ {
+ ApplyPropertyConfigurationToTypes(propertyConfig, editFormTypes);
+ }
}
}
}
@@ -93,29 +103,32 @@ namespace Volo.Abp.ObjectExtending.Modularity
Type[] createApiTypes = null,
Type[] updateApiTypes = null)
{
- if (entityType != null)
+ lock (SyncLock)
{
- ApplyEntityConfigurationToEntity(
+ if (entityType != null)
+ {
+ ApplyEntityConfigurationToEntity(
+ moduleName,
+ entityName,
+ entityType
+ );
+ }
+
+ ApplyEntityConfigurationToApi(
moduleName,
entityName,
- entityType
+ getApiTypes: getApiTypes,
+ createApiTypes: createApiTypes,
+ updateApiTypes: updateApiTypes
);
- }
- ApplyEntityConfigurationToApi(
- moduleName,
- entityName,
- getApiTypes: getApiTypes,
- createApiTypes: createApiTypes,
- updateApiTypes: updateApiTypes
- );
-
- ApplyEntityConfigurationToUi(
- moduleName,
- entityName,
- createFormTypes: createFormTypes,
- editFormTypes: editFormTypes
- );
+ ApplyEntityConfigurationToUi(
+ moduleName,
+ entityName,
+ createFormTypes: createFormTypes,
+ editFormTypes: editFormTypes
+ );
+ }
}
[NotNull]
@@ -123,41 +136,47 @@ namespace Volo.Abp.ObjectExtending.Modularity
string moduleName,
string entityName)
{
- var moduleConfig = ObjectExtensionManager.Instance.Modules().GetOrDefault(moduleName);
- if (moduleConfig == null)
+ lock (SyncLock)
{
- return Array.Empty();
- }
+ var moduleConfig = ObjectExtensionManager.Instance.Modules().GetOrDefault(moduleName);
+ if (moduleConfig == null)
+ {
+ return Array.Empty();
+ }
- var objectConfig = moduleConfig.Entities.GetOrDefault(entityName);
- if (objectConfig == null)
- {
- return Array.Empty();
- }
+ var objectConfig = moduleConfig.Entities.GetOrDefault(entityName);
+ if (objectConfig == null)
+ {
+ return Array.Empty();
+ }
- return objectConfig.GetProperties();
+ return objectConfig.GetProperties();
+ }
}
public static void ApplyPropertyConfigurationToTypes(
ExtensionPropertyConfiguration propertyConfig,
Type[] types)
{
- ObjectExtensionManager.Instance
- .AddOrUpdateProperty(
- types,
- propertyConfig.Type,
- propertyConfig.Name,
- property =>
- {
- property.Attributes.Clear();
- property.Attributes.AddRange(propertyConfig.Attributes);
- property.DisplayName = propertyConfig.DisplayName;
- property.Validators.AddRange(propertyConfig.Validators);
- property.DefaultValue = propertyConfig.DefaultValue;
- property.DefaultValueFactory = propertyConfig.DefaultValueFactory;
- property.Lookup = propertyConfig.UI.Lookup;
- }
- );
+ lock (SyncLock)
+ {
+ ObjectExtensionManager.Instance
+ .AddOrUpdateProperty(
+ types,
+ propertyConfig.Type,
+ propertyConfig.Name,
+ property =>
+ {
+ property.Attributes.Clear();
+ property.Attributes.AddRange(propertyConfig.Attributes);
+ property.DisplayName = propertyConfig.DisplayName;
+ property.Validators.AddRange(propertyConfig.Validators);
+ property.DefaultValue = propertyConfig.DefaultValue;
+ property.DefaultValueFactory = propertyConfig.DefaultValueFactory;
+ property.Lookup = propertyConfig.UI.Lookup;
+ }
+ );
+ }
}
}
}
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs
index 2159e06d5e..d54ee7104a 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using JetBrains.Annotations;
@@ -12,10 +13,10 @@ namespace Volo.Abp.ObjectExtending
public Type Type { get; }
[NotNull]
- protected Dictionary Properties { get; }
+ protected ConcurrentDictionary Properties { get; }
[NotNull]
- public Dictionary