From a44c8a11d6594582e93564c264a8e4fd25ff7b41 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 31 May 2021 09:46:19 +0800 Subject: [PATCH 01/13] Don't set modified audit if the property is generated by the database. Resolve #9166 --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 23 ++++++---- .../Auditing/Auditing_Tests.cs | 46 ++++++++++++++++++- .../EntityFrameworkCore/TestAppDbContext.cs | 5 ++ .../Volo/Abp/TestApp/Domain/Person.cs | 5 +- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index cda8c02416..4b3fcd1d0d 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -372,17 +372,20 @@ namespace Volo.Abp.EntityFrameworkCore protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, EntityChangeReport changeReport) { - UpdateConcurrencyStamp(entry); - SetModificationAuditProperties(entry); - - if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted) - { - SetDeletionAuditProperties(entry); - changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); - } - else + if (entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never)) { - changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Updated)); + UpdateConcurrencyStamp(entry); + SetModificationAuditProperties(entry); + + if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted) + { + SetDeletionAuditProperties(entry); + changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); + } + else + { + changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Updated)); + } } } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Auditing/Auditing_Tests.cs index d1b1018e5c..ee1ca3572c 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Auditing/Auditing_Tests.cs @@ -1,9 +1,53 @@ -using Volo.Abp.TestApp.Testing; +using System; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.TestApp; +using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.EntityFrameworkCore.Auditing { public class Auditing_Tests : Auditing_Tests { + [Fact] + public async Task Should_Not_Set_Modification_If_Properties_Generated_By_Database() + { + await WithUnitOfWorkAsync((async () => + { + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + douglas.LastActiveTime = DateTime.Now; + })); + await WithUnitOfWorkAsync((async () => + { + var douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + + douglas.ShouldNotBeNull(); + douglas.LastModificationTime.ShouldBeNull(); + douglas.LastModificationTime.ShouldBeNull(); + douglas.LastModifierId.ShouldBeNull(); + })); + } + + [Fact] + public async Task Should_Set_Modification_If_Properties_Not_Generated_By_Database() + { + await WithUnitOfWorkAsync((async () => + { + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + douglas.LastActiveTime = DateTime.Now; + douglas.Age = 100; + })); + + await WithUnitOfWorkAsync((async () => + { + var douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + + douglas.ShouldNotBeNull(); + douglas.LastModificationTime.ShouldNotBeNull(); + douglas.LastModificationTime.Value.ShouldBeLessThanOrEqualTo(Clock.Now); + douglas.LastModifierId.ShouldBe(CurrentUserId); + })); + } } } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index da8bbc8a6c..9022a1efc4 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -36,6 +36,11 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore b.HasKey(p => new {p.PersonId, p.Number}); }); + modelBuilder.Entity(b => + { + b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate(); + }); + modelBuilder .Entity(p => { diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs index 9075740f11..0c17d7b250 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs @@ -23,9 +23,10 @@ namespace Volo.Abp.TestApp.Domain public virtual Collection Phones { get; set; } + public virtual DateTime? LastActiveTime { get; set; } + private Person() { - } public Person(Guid id, string name, int age, Guid? tenantId = null, Guid? cityId = null) @@ -65,4 +66,4 @@ namespace Volo.Abp.TestApp.Domain ); } } -} \ No newline at end of file +} From a8995eaeb8ddcabfea202d6ec9daf032a35b870c Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 31 May 2021 10:10:41 +0800 Subject: [PATCH 02/13] Set the database default value of LastActiveTime. --- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 2 +- .../EntityFrameworkCore/TestMigrationsDbContext.cs | 13 ++++++++++--- .../TestApp/EntityFrameworkCore/TestAppDbContext.cs | 9 +++++---- .../Volo/Abp/TestApp/Domain/Person.cs | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 4b3fcd1d0d..03a22f5718 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -372,7 +372,7 @@ namespace Volo.Abp.EntityFrameworkCore protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, EntityChangeReport changeReport) { - if (entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never)) + if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never)) { UpdateConcurrencyStamp(entry); SetModificationAuditProperties(entry); diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index 23137f0f0c..516c7d5789 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext; @@ -16,10 +17,10 @@ namespace Volo.Abp.EntityFrameworkCore public DbSet Books { get; set; } public DbSet EntityWithIntPks { get; set; } - + public DbSet Author { get; set; } - - public TestMigrationsDbContext(DbContextOptions options) + + public TestMigrationsDbContext(DbContextOptions options) : base(options) { @@ -36,6 +37,12 @@ namespace Volo.Abp.EntityFrameworkCore b.HasKey(p => new { p.PersonId, p.Number }); }); + + modelBuilder.Entity(b => + { + b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate().HasDefaultValue(DateTime.Now); + }); + modelBuilder.Entity(b => { b.OwnsMany(c => c.Districts, d => diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index 9022a1efc4..6bc581ca7d 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System; +using Microsoft.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext; using Volo.Abp.TestApp.Domain; @@ -16,10 +17,10 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore public DbSet DummyEntities { get; set; } public DbSet EntityWithIntPks { get; set; } - + public DbSet Author { get; set; } - public TestAppDbContext(DbContextOptions options) + public TestAppDbContext(DbContextOptions options) : base(options) { @@ -38,7 +39,7 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore modelBuilder.Entity(b => { - b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate(); + b.Property(x => x.LastActiveTime).ValueGeneratedOnAddOrUpdate().HasDefaultValue(DateTime.Now); }); modelBuilder diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs index 0c17d7b250..f88e617072 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs @@ -23,7 +23,7 @@ namespace Volo.Abp.TestApp.Domain public virtual Collection Phones { get; set; } - public virtual DateTime? LastActiveTime { get; set; } + public virtual DateTime LastActiveTime { get; set; } private Person() { From 02783c7ed912c34f1263db228547aec889dfcb64 Mon Sep 17 00:00:00 2001 From: EngincanV Date: Thu, 3 Jun 2021 18:15:54 +0300 Subject: [PATCH 03/13] Add missing localizations for license --- .../AbpIoLocalization/Base/Localization/Resources/en-GB.json | 3 ++- .../AbpIoLocalization/Base/Localization/Resources/en.json | 3 ++- .../AbpIoLocalization/Base/Localization/Resources/tr.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en-GB.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en-GB.json index 82ffbf0f4c..442384bd0a 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en-GB.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en-GB.json @@ -32,6 +32,7 @@ "SeeDocuments": "See Documents", "Samples": "Samples", "FreeDDDBook": "Free DDD E-book", - "New": "New" + "New": "New", + "Volo.AbpIo.Domain:020005": "License extend year can not be lower than {MinExtendLicenseYear} year and greater than {MaxExtendLicenseYear} years " } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index 646c2f9648..b9c8d23563 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -34,6 +34,7 @@ "Framework": "Framework", "Support": "Support", "FreeDDDBook": "Free DDD E-book", - "New": "New" + "New": "New", + "Volo.AbpIo.Domain:020005": "License extend year can not be lower than {MinExtendLicenseYear} year and greater than {MaxExtendLicenseYear} years " } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json index 2f932f299b..04c114a6c2 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json @@ -28,6 +28,7 @@ "Commercial": "Ticari", "SeeDocuments": "Dokümanlara Göz Atın", "FreeDDDBook": "Ücretsiz DDD Kitabı", - "New": "Yeni" + "New": "Yeni", + "Volo.AbpIo.Domain:020005": "Lisans uzatma yılı {MinExtendLicenseYear} yıldan az {MaxExtendLicenseYear} yıldan fazla olamaz." } } \ No newline at end of file From 108032718820d852daf62e8edc3ea9229cf09ce1 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 4 Jun 2021 20:20:38 +0800 Subject: [PATCH 04/13] Upgrade Castle.Core.AsyncInterceptor to 2.0 --- framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj index 7e742c0467..036144e8c6 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj +++ b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj @@ -16,7 +16,7 @@ - + From 21141fcf5f21d965784f2c17c87574bfcce694f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 7 Jun 2021 18:22:25 +0300 Subject: [PATCH 05/13] Update road map items. --- docs/en/Road-Map.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/Road-Map.md b/docs/en/Road-Map.md index f57e35c863..26de749096 100644 --- a/docs/en/Road-Map.md +++ b/docs/en/Road-Map.md @@ -8,11 +8,9 @@ This document provides a road map, release schedule and planned features for the This version will focus on **documentation** and **improvements** of current features. In addition, the following features are planned: -* Publishing distributed events as transactional ([#6126](https://github.com/abpframework/abp/issues/6126)) * Revisit the microservice demo solution ([#8385](https://github.com/abpframework/abp/issues/8385)) * A new UI Theme alternative to the Basic Theme ([#6132](https://github.com/abpframework/abp/issues/6132)) * Improvements and new features to the [CMS Kit](Modules/Cms-Kit.md) module ([#8380](https://github.com/abpframework/abp/issues/8380) [#8381](https://github.com/abpframework/abp/issues/8381)) -* Pre-configured test project for the [Blazor UI](UI/Blazor/Overall.md) ([#5516](https://github.com/abpframework/abp/issues/5516)) * Razor engine support for text templating ([#8373](https://github.com/abpframework/abp/issues/8373)) **Planned release date**: End of Quarter 2, 2021. See the [4.4 milestone](https://github.com/abpframework/abp/milestone/52) to track the progress. @@ -25,6 +23,7 @@ We planned to focus on the ABP Framework v5.0 after the 4.4 release. This versio * Upgrading to Bootstrap 5.x ([#8922](https://github.com/abpframework/abp/issues/8922)) * Alternative to IdentityServer4 ([#7221](https://github.com/abpframework/abp/issues/7221)) * Dapr integration ([#2183](https://github.com/abpframework/abp/issues/2183)) +* Publishing distributed events as transactional ([#6126](https://github.com/abpframework/abp/issues/6126)) * Resource based authorization system ([#236](https://github.com/abpframework/abp/issues/236)) * API Versioning system: finalize & document ([#497](https://github.com/abpframework/abp/issues/497)) * Performance optimizations; Enabling .NET Trimming, using source generators and reducing reflection, etc. From 893ec2a72751e3a69fe9e14e590b337919044cc7 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 8 Jun 2021 12:11:40 +0300 Subject: [PATCH 06/13] update yarn.lock --- npm/ng-packs/yarn.lock | 137 +++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 79 deletions(-) diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index 16633ccdb8..7180735fa2 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -2,20 +2,20 @@ # yarn lockfile v1 -"@abp/ng.components@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.components/-/ng.components-4.3.1.tgz#61f8dd9f506a10cc478721b9e069b5328919b808" - integrity sha512-/TUz2JYUcH/NbWma5I3A0nI72YVafOdLGgBH3+L2DjpFYsn+0c7/n+9YCMwZ6Zm6DWAAQE5ZJa+vOEIUMOnVlA== +"@abp/ng.components@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.components/-/ng.components-4.3.2.tgz#6976ae0bf64bd7d69b2e90cdabf8477e9d8129ab" + integrity sha512-wFuN9ysAi71PxhmsxYBNHc03CcbC4Gc/Ga/ure271D6OHkcA888OeG4bRNqGs8w8BUJoZMJQ7F+UHwSA19PDzg== dependencies: ng-zorro-antd "^11.0.0" tslib "^2.0.0" -"@abp/ng.core@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.3.1.tgz#631bfab8bc090b81e3637ced2fd3d01f8fd720d9" - integrity sha512-e48h2OhyrFqBac7K//4aVQWCdjIqkhS1Pa+mh3YA30lv6LTbDdwLQc75q0mDs5PxCeGx3XPldgedMIP8pYWCsA== +"@abp/ng.core@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.3.2.tgz#232d983d3b608377fdebdffd165fd7b0802efafa" + integrity sha512-p+JzBepMRhJNRAe+f1W2cJ1T/oMqAwXz1+Jb0ea8oGySUN1YFhvNNMgr3DGSfnR6UMfGED2pJAeBS/5TC3LC7g== dependencies: - "@abp/utils" "^4.3.0" + "@abp/utils" "^4.3.1" "@angular/localize" "~10.0.10" "@ngxs/store" "^3.7.0" angular-oauth2-oidc "^10.0.0" @@ -25,35 +25,35 @@ ts-toolbelt "6.15.4" tslib "^2.0.0" -"@abp/ng.feature-management@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.3.1.tgz#b576583a35c435569494def6965512c99ef08a05" - integrity sha512-/hRZYKlyIG1xHLWTbGX/tDCq8oGhU6M96IJoXAjUYJc2Nc3lvBAM21oMAG52B4ANWIELaKBX4k3+Su0yXjuUHg== +"@abp/ng.feature-management@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.3.2.tgz#9e1dc02818788353ba7b998f967b40bc13352f73" + integrity sha512-99cL/pE839NunmsP9+6ckO6QT7udYkD+dd/L5T3SYcDFxMlz75EpKnkNNqsrdEs4V4VopoboJeiv6JtrUkPNVw== dependencies: - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.identity@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.3.1.tgz#c92ff2f3cccec18cfb52ae47b11588e2e3ebb186" - integrity sha512-X3EeGR+wuqpuGlSX6iV38QZctIAIgbZqaFBrYKnq97SsXxhQvxw7yQFm0MFxY3S6+ez23WX1C0r0HTpqXHNj0A== +"@abp/ng.identity@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.3.2.tgz#24ed3ccd003e023ac18ef875df5d727e50ad0016" + integrity sha512-EdbSLHC/yjszlMC76B2ydKoopaeCHYItw2GD57e9xSMu6aiKW8/9+3zzTvTdrJ0MSxaZvPo/3IN3cU48rsNwwg== dependencies: - "@abp/ng.permission-management" "~4.3.1" - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.permission-management" "~4.3.2" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.permission-management@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.3.1.tgz#aa0cc1c4136f9e9ddd946fe2f4ebb2cc08087cf7" - integrity sha512-c7PiXedtvvZ70wWWiEgi0UsbUM92tZi5U+l7dV/EKff54FPWb1AbZMSLOGGmiA66h9FoVEjY3PnRqVPvUN1JBQ== +"@abp/ng.permission-management@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.3.2.tgz#d8514152fd43bd3a95e4e375ebd9e3895b73125c" + integrity sha512-rWPMpNfJu7cgmTK+TRwPH96AGBeNLzf2TZOfG53bqw6VRqiqqwQvY+54OD+kZGJw5xgNZQiE3Ze51f1CIgeE5Q== dependencies: - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.schematics@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.3.1.tgz#3a6991131eab86baf6d4be943cfa059efcb2d52f" - integrity sha512-eqqDRU+7RtoDkOlB9tVffYN4F7c135cW9R3LuubnabK29twPuv5BEFMWQO+GYQKFcdn7857LCEHAdP0HQe9grg== +"@abp/ng.schematics@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.3.2.tgz#a792d8c9d99c0099f4a8d998be90721652f5efeb" + integrity sha512-gBV0f7vkbM9rs3HCeTxYDtnxCC6rXnSVrogtsOkZAqWR38riPMVDTK5u4yDeypCEFlMHaOF80QBj6lxpAr9Yrw== dependencies: "@angular-devkit/core" "~11.0.2" "@angular-devkit/schematics" "~11.0.2" @@ -62,38 +62,38 @@ should-quote "^1.0.0" typescript "~3.9.2" -"@abp/ng.setting-management@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.3.1.tgz#c7e2f35bbaa79bb89693febb397d8125bf6089b6" - integrity sha512-y+4EscXVBVWWYeT5CTlln4fiZQelsBq7O49LYq4/n122oY9ItCRP+/INtamhGBtHb8gEld4jMBtZQjZ3JUF24g== +"@abp/ng.setting-management@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.3.2.tgz#afb4540dcaf5e32764261377e771beb688314c5a" + integrity sha512-FRrFZkQSq9mEAsNf3GCskh3UxXUzRGbIn4QUbruImeUvEhKmZiQGoTjjW18CPZiXd/y22iiYpRpovS2frcGbhQ== dependencies: - "@abp/ng.components" "~4.3.1" - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.components" "~4.3.2" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.tenant-management@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.3.1.tgz#5bc42bd4015b38465a206f54435e5d9cc70fc2a3" - integrity sha512-TEYx6DlypdSfa1hNCy2TuUR5uKCI4pyeVSl5Fs0QzKN4W7hcWJWYYWYaaXwrZ+/In8eo/MNUwYjx0G1ZMKxeCA== +"@abp/ng.tenant-management@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.3.2.tgz#fc6655d03ddd1b9aa0d3eedefe459c239ff18ec0" + integrity sha512-xQ/NgXpP81B346W+Cgys3eHMBO68kQDizaazT3nip6o4MuEAY+sNjFKh2qeXbg6Xjy/qC/VwcuImTDKhONOcRg== dependencies: - "@abp/ng.feature-management" "~4.3.1" - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.feature-management" "~4.3.2" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.theme.basic@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.3.1.tgz#7bfd4c8c10e000e052fa632f0e836f7172fed100" - integrity sha512-RacWdY4yN+o8flmjm8XR6LlyQziJVqX3WxylVfgPybgNAiyFww7ZQ6cFMbDseqFmDTbvtYyRuG2tMiAPTZGfNQ== +"@abp/ng.theme.basic@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.3.2.tgz#7243c814905e554f12ebefb933f09f03a008a006" + integrity sha512-tA6XCDe+fjgxz057N1/sPHMiPpO5XAMNvRBeRcYfvD1cJa2OQAbTGAdaVu0H268raRHVta3T7G8qt6++MySaRA== dependencies: - "@abp/ng.theme.shared" "~4.3.1" + "@abp/ng.theme.shared" "~4.3.2" tslib "^2.0.0" -"@abp/ng.theme.shared@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.3.1.tgz#1bfa9979d8f741c30a0bf40585fa3715a7e55017" - integrity sha512-LHkrjCErBvFgYfU9msV1oQ7KKU5g7AiMgK4MLbzKpHaDhN0B0WCdOwUrN4ynn7iXU433quaph605QceoejL7Nw== +"@abp/ng.theme.shared@~4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.3.2.tgz#be7d6a4eceee59700c66fe2fd9c0fc60f49ce60f" + integrity sha512-SJSexJUK26YvbpzYOl2XAV6IiTdt3j46c9ZzYIlMiJVrqAyW3GRvRBcBFEd2qfeq7dBnYYR9oN4sEcNqQL/gCQ== dependencies: - "@abp/ng.core" "~4.3.1" + "@abp/ng.core" "~4.3.2" "@fortawesome/fontawesome-free" "^5.14.0" "@ng-bootstrap/ng-bootstrap" "^7.0.0" "@ngx-validate/core" "^0.0.13" @@ -102,13 +102,6 @@ chart.js "^2.9.3" tslib "^2.0.0" -"@abp/utils@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.3.0.tgz#5fe44ae9a76ba8760bfd54dc6cfc64c96acc4b67" - integrity sha512-FFPSn/cL+b7/wYoRjvQLNJ3KrTiqng2gKPGf+jtMZer92fcVZtsF21ew/z+yxgj+9DRYtWbvn5iu9/ZPKL5hiw== - dependencies: - just-compare "^1.3.0" - "@abp/utils@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.3.1.tgz#cc9163a545526d1970d2d2b85803f345bb38b4a0" @@ -116,6 +109,13 @@ dependencies: just-compare "^1.3.0" +"@abp/utils@^4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.3.2.tgz#ff8fa921aadf8ec6d1d8eb0572133907c19ac6e5" + integrity sha512-xUycN1bW+gTVfD0WJHXG5UbqAqAg/dVrZ/pXlnEGixp04vXwTI+o5oWTV2rQYiOyLbPzmpjSAZmxrkIcAgIQ1A== + dependencies: + just-compare "^1.3.0" + "@angular-builders/jest@^10.0.0": version "10.0.1" resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-10.0.1.tgz#a1a6fb5d11b5d54c051bdaa2012b5f046371560c" @@ -2506,13 +2506,6 @@ enhanced-resolve "5.6.0" webpack-sources "2.2.0" -"@ngx-validate/core@^0.0.12": - version "0.0.12" - resolved "https://registry.yarnpkg.com/@ngx-validate/core/-/core-0.0.12.tgz#4924247c363e0e876e6d63794215914ac9232e8d" - integrity sha512-AhHfb44M2E2Wc37IX9DxAWjgSIZMNrzzpjnPL+VXMNJQj9GqynBjqw0zQtrGYANsAYeFPFhn7UuX6uqrkRvHtQ== - dependencies: - tslib "^1.9.0" - "@ngx-validate/core@^0.0.13": version "0.0.13" resolved "https://registry.yarnpkg.com/@ngx-validate/core/-/core-0.0.13.tgz#954c6d247df8107668f23a39db24ca45c274f3d9" @@ -2534,20 +2527,6 @@ dependencies: tslib "^1.9.0" -"@ngxs/router-plugin@^3.7.0": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@ngxs/router-plugin/-/router-plugin-3.7.1.tgz#cd73224d5dd54ee070dd54cc26ba09a1fde189a8" - integrity sha512-5Pg+Ija4Z4eCYTLtVgZYxsbYqN35YD+Tak/WhHFXvOqFpt0zPMt8VQbgfEFXKOgoUsJYQ4qz/EWqc0GaRXL+Jw== - dependencies: - tslib "^1.9.0" - -"@ngxs/storage-plugin@^3.7.0": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@ngxs/storage-plugin/-/storage-plugin-3.7.1.tgz#39fbce9cf6585cc094d74e5efd54daae3fcd334d" - integrity sha512-2e/iHu2Ax/Z7hEfGZPCuUIPNiTb33e9kIM7xIho5BG18liKy1MTeZ7BS5PQKmMc7oY9Ji90ZjhU9uXnex/Em2w== - dependencies: - tslib "^1.9.0" - "@ngxs/store@^3.7.0": version "3.7.1" resolved "https://registry.yarnpkg.com/@ngxs/store/-/store-3.7.1.tgz#3333379eb36cfe475d019b6c894b263ea515b8a5" From 6d469456c19456572b32ce78a60cf270bfeaffe5 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 8 Jun 2021 12:12:09 +0300 Subject: [PATCH 07/13] make lookup property optional --- .../extensions/src/lib/models/internal/object-extensions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts index ea8a9dad8f..776785ed4c 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts @@ -66,7 +66,7 @@ export interface ExtensionPropertyUiDto { onTable: ExtensionPropertyUiTableDto; onCreateForm: ExtensionPropertyUiFormDto; onEditForm: ExtensionPropertyUiFormDto; - lookup: ExtensionPropertyUiLookupDto; + lookup?: ExtensionPropertyUiLookupDto; } export interface ExtensionPropertyUiFormDto { From 9190350f4a68af472d8b5500a0066adc2fc00dcb Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 8 Jun 2021 12:12:39 +0300 Subject: [PATCH 08/13] update state util tests to reflect optional lookup --- .../theme-shared/extensions/src/tests/state.util.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts b/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts index c0581aae45..f9538a25eb 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts @@ -148,7 +148,7 @@ function createMockEntities(): Record Date: Tue, 8 Jun 2021 16:01:05 +0300 Subject: [PATCH 09/13] Resolved #9262: Allow to replace a dbcontext by a given type --- .../AbpCommonDbContextRegistrationOptions.cs | 13 +++++++++---- ...pCommonDbContextRegistrationOptionsBuilder.cs | 12 ++++++++++-- .../AbpEfCoreServiceCollectionExtensions.cs | 11 +++++++---- .../AbpMemoryDbServiceCollectionExtensions.cs | 12 ++++++++++-- .../AbpMongoDbServiceCollectionExtensions.cs | 16 +++++++--------- 5 files changed, 43 insertions(+), 21 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs index ee172b3409..886d8e020c 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.DependencyInjection public IServiceCollection Services { get; } - public List ReplacedDbContextTypes { get; } + public Dictionary ReplacedDbContextTypes { get; } public Type DefaultRepositoryDbContextType { get; protected set; } @@ -39,7 +39,7 @@ namespace Volo.Abp.DependencyInjection Services = services; DefaultRepositoryDbContextType = originalDbContextType; CustomRepositories = new Dictionary(); - ReplacedDbContextTypes = new List(); + ReplacedDbContextTypes = new Dictionary(); SpecifiedDefaultRepositories = new List(); } @@ -47,15 +47,20 @@ namespace Volo.Abp.DependencyInjection { return ReplaceDbContext(typeof(TOtherDbContext)); } + + public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext() + { + return ReplaceDbContext(typeof(TOtherDbContext), typeof(TTargetDbContext)); + } - public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType) + public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null) { if (!otherDbContextType.IsAssignableFrom(OriginalDbContextType)) { throw new AbpException($"{OriginalDbContextType.AssemblyQualifiedName} should inherit/implement {otherDbContextType.AssemblyQualifiedName}!"); } - ReplacedDbContextTypes.AddIfNotContains(otherDbContextType); + ReplacedDbContextTypes[otherDbContextType] = targetDbContextType; return this; } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs index 34e74d90cd..248300ab62 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs @@ -75,9 +75,17 @@ namespace Volo.Abp.DependencyInjection IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(); /// - /// Replaces given DbContext type with this DbContext type. + /// Replaces given DbContext type with the target DbContext type. + /// + /// The DbContext type to be replaced + /// The target DbContext type + IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(); + + /// + /// Replaces given DbContext type with the given or this DbContext type. /// /// The DbContext type to be replaced - IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType); + /// The target DbContext type (optional, used this DbContext type if not provided) + IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs index a48575ddcc..658269a376 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs @@ -31,18 +31,21 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddTransient(DbContextOptionsFactory.Create); - foreach (var dbContextType in options.ReplacedDbContextTypes) + foreach (var entry in options.ReplacedDbContextTypes) { + var originalDbContextType = entry.Key; + var targetDbContextType = entry.Value ?? typeof(TDbContext); + services.Replace( ServiceDescriptor.Transient( - dbContextType, - sp => sp.GetRequiredService(typeof(TDbContext)) + originalDbContextType, + sp => sp.GetRequiredService(targetDbContextType) ) ); services.Configure(opts => { - opts.DbContextReplacements[dbContextType] = typeof(TDbContext); + opts.DbContextReplacements[originalDbContextType] = targetDbContextType; }); } diff --git a/framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs b/framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs index 3cab95cb03..99ef1bd445 100644 --- a/framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs @@ -18,9 +18,17 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(options.DefaultRepositoryDbContextType, sp => sp.GetRequiredService()); } - foreach (var dbContextType in options.ReplacedDbContextTypes) + foreach (var entry in options.ReplacedDbContextTypes) { - services.Replace(ServiceDescriptor.Singleton(dbContextType, sp => sp.GetRequiredService())); + var originalDbContextType = entry.Key; + var targetDbContextType = entry.Value ?? typeof(TMemoryDbContext); + + services.Replace( + ServiceDescriptor.Singleton( + originalDbContextType, + sp => sp.GetRequiredService(targetDbContextType) + ) + ); } new MemoryDbRepositoryRegistrar(options).AddRepositories(); diff --git a/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs b/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs index 7f9c6b8d42..c05dbe5b3a 100644 --- a/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs @@ -25,23 +25,21 @@ namespace Microsoft.Extensions.DependencyInjection optionsBuilder?.Invoke(options); - foreach (var dbContextType in options.ReplacedDbContextTypes) - { - services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TMongoDbContext))); - } - - foreach (var dbContextType in options.ReplacedDbContextTypes) + foreach (var entry in options.ReplacedDbContextTypes) { + var originalDbContextType = entry.Key; + var targetDbContextType = entry.Value ?? typeof(TMongoDbContext); + services.Replace( ServiceDescriptor.Transient( - dbContextType, - sp => sp.GetRequiredService(typeof(TMongoDbContext)) + originalDbContextType, + sp => sp.GetRequiredService(targetDbContextType) ) ); services.Configure(opts => { - opts.DbContextReplacements[dbContextType] = typeof(TMongoDbContext); + opts.DbContextReplacements[originalDbContextType] = targetDbContextType; }); } From 49d74b562c52035519e9a412053e5b3a9f4ad768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 8 Jun 2021 17:37:24 +0300 Subject: [PATCH 10/13] Update DDD book links --- docs/en/Domain-Driven-Design.md | 4 ++-- docs/en/Index.md | 11 ++++++++++- .../implementing-domain-driven-design-book.png | Bin 0 -> 76877 bytes 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 docs/en/images/implementing-domain-driven-design-book.png diff --git a/docs/en/Domain-Driven-Design.md b/docs/en/Domain-Driven-Design.md index f18c1882ef..03a6232ba3 100644 --- a/docs/en/Domain-Driven-Design.md +++ b/docs/en/Domain-Driven-Design.md @@ -32,6 +32,6 @@ DDD mostly interest in the **Domain** and the **Application** layers, rather tha * [Data Transfer Objects (DTOs)](Data-Transfer-Objects.md) * [Unit of Work](Unit-Of-Work.md) -## The Ultimate DDD Implementation Guide +## Free E-Book: Implementing DDD -See the [Implementing Domain Driven Design](Domain-Driven-Design-Implementation-Guide.md) guide as a **complete reference**. The Guide explains the Domain Driven Design and introduces explicit **rules and examples** to give a deep understanding of the **implementation details**. \ No newline at end of file +See the [Implementing Domain Driven Design book](https://abp.io/books/implementing-domain-driven-design) as a **complete reference**. This book explains the Domain Driven Design and introduces explicit **rules and examples** to give a deep understanding of the **implementation details**. \ No newline at end of file diff --git a/docs/en/Index.md b/docs/en/Index.md index de88e2f2f8..d80caa1519 100644 --- a/docs/en/Index.md +++ b/docs/en/Index.md @@ -29,8 +29,9 @@ ABP has a **comprehensive documentation** that not only explains the ABP Framewo ABP offers a complete, modular and layered software architecture based on [Domain Driven Design](Domain-Driven-Design.md) principles and patterns. It also provides the necessary infrastructure to implement this architecture. * See the [Modularity](Module-Development-Basics.md) document to understand the module system. -* [Implementing Domain Driven Design](Domain-Driven-Design-Implementation-Guide.md) document is an ultimate guide for who want to understand and implement the DDD. +* [Implementing Domain Driven Design Book](https://abp.io/books/implementing-domain-driven-design) is an ultimate guide for who want to understand and implement the DDD. * [Microservice Architecture](Microservice-Architecture.md) document explains how ABP helps to create a microservice solution. +* [Multi-Tenancy](Multi-Tenancy.md) document introduces multi-tenancy and explores the ABP multi-tenancy infrastructure. ### Infrastructure @@ -55,6 +56,14 @@ See the [Application Modules](Modules/Index.md) document for all pre-built modul The [Startup templates](Startup-Templates/Index.md) are pre-built Visual Studio solution templates. You can create your own solution based on these templates to **immediately start your development**. +## Free E-Book: Implementing Domain Driven Design + +![Implementing Domain Driven Design](images/implementing-domain-driven-design-book.png) + +A **practical guide** for implementing Domain Driven Design (DDD). While the implementation details are **based on the ABP Framework** infrastructure, the basic concepts, principles and models can be applied to any solution, even if it is not a .NET solution. + +[Click here to get your free copy](https://abp.io/books/implementing-domain-driven-design). + ## ABP Community ### The Source Code diff --git a/docs/en/images/implementing-domain-driven-design-book.png b/docs/en/images/implementing-domain-driven-design-book.png new file mode 100644 index 0000000000000000000000000000000000000000..6568b46f667c0fa6e0c8c4aec276c4070f622e89 GIT binary patch literal 76877 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