From c00778d18747c8e57744dfb07594244e94a7971b Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 25 Dec 2019 15:16:13 +0800 Subject: [PATCH 01/39] Set value comparer for ExtraProperties and Properties dictionary. Resolve #2459 --- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 7 ++++++- .../Modeling/AbpEntityTypeBuilderExtensions.cs | 8 +++++++- ...dentityServerDbContextModelCreatingExtensions.cs | 13 +++++++++++-- 3 files changed, 24 insertions(+), 4 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 18758d98e3..9ecb7e8c73 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -390,7 +391,11 @@ namespace Volo.Abp.EntityFrameworkCore d => JsonConvert.SerializeObject(d, Formatting.None), s => JsonConvert.DeserializeObject>(s) ) - .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)); + .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) + .Metadata.SetValueComparer(new ValueComparer>( + (d1, d2) => d1.SequenceEqual(d2), + d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), + d => d.ToDictionary(k => k.Key, v => v.Value))); }); } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index 132e91c079..b1824d2706 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Newtonsoft.Json; using Volo.Abp.Auditing; @@ -61,7 +63,11 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling d => JsonConvert.SerializeObject(d, Formatting.None), s => JsonConvert.DeserializeObject>(s) ) - .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)); + .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) + .Metadata.SetValueComparer(new ValueComparer>( + (d1, d2) => d1.SequenceEqual(d2), + d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), + d => d.ToDictionary(k => k.Key, v => v.Value))); } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index bd69f6948f..7ab2e2d5f7 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.IO.Compression; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Newtonsoft.Json; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -205,7 +208,10 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore .HasConversion( d => JsonConvert.SerializeObject(d, Formatting.None), s => JsonConvert.DeserializeObject>(s) - ); + ).Metadata.SetValueComparer(new ValueComparer>( + (d1, d2) => d1.SequenceEqual(d2), + d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), + d => d.ToDictionary(k => k.Key, v => v.Value))); identityResource.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.IdentityResourceId).IsRequired(); }); @@ -232,7 +238,10 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore .HasConversion( d => JsonConvert.SerializeObject(d, Formatting.None), s => JsonConvert.DeserializeObject>(s) - ); + ).Metadata.SetValueComparer(new ValueComparer>( + (d1, d2) => d1.SequenceEqual(d2), + d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), + d => d.ToDictionary(k => k.Key, v => v.Value))); apiResource.HasMany(x => x.Secrets).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); apiResource.HasMany(x => x.Scopes).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); From 25a654dac0f188340e45b103ff6785034c8b41cb Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 25 Dec 2019 15:33:02 +0800 Subject: [PATCH 02/39] Remove unnecessary namespaces. --- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 1 - .../IdentityServerDbContextModelCreatingExtensions.cs | 1 - 2 files changed, 2 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 9ecb7e8c73..3df9219877 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index 7ab2e2d5f7..c7726c37e7 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.IO.Compression; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Newtonsoft.Json; From 8f74373f5f5b92ae4f90c2570ee47cda726feda1 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 26 Dec 2019 13:33:18 +0800 Subject: [PATCH 03/39] Add ConfigureJsonConversionWithValueComparer extension method. --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 148 +----------------- .../AbpEntityTypeBuilderExtensions.cs | 11 +- .../Modeling/AbpPropertyBuilderExtensions.cs | 28 ++++ ...yServerDbContextModelCreatingExtensions.cs | 7 +- 4 files changed, 35 insertions(+), 159 deletions(-) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs 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 3df9219877..c911c7f461 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -11,13 +11,13 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Newtonsoft.Json; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities.Events; using Volo.Abp.EntityFrameworkCore.EntityHistory; +using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -352,151 +352,9 @@ namespace Volo.Abp.EntityFrameworkCore return; } - ConfigureConcurrencyStampProperty(modelBuilder, mutableEntityType); - ConfigureExtraProperties(modelBuilder, mutableEntityType); - ConfigureAuditProperties(modelBuilder, mutableEntityType); - ConfigureTenantIdProperty(modelBuilder, mutableEntityType); - ConfigureGlobalFilters(modelBuilder, mutableEntityType); - } - - protected virtual void ConfigureConcurrencyStampProperty(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) - where TEntity : class - { - if (!typeof(IHasConcurrencyStamp).IsAssignableFrom(typeof(TEntity))) - { - return; - } - - modelBuilder.Entity(b => - { - b.Property(x => ((IHasConcurrencyStamp) x).ConcurrencyStamp) - .IsConcurrencyToken() - .HasColumnName(nameof(IHasConcurrencyStamp.ConcurrencyStamp)); - }); - } - - protected virtual void ConfigureExtraProperties(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) - where TEntity : class - { - if (!typeof(IHasExtraProperties).IsAssignableFrom(typeof(TEntity))) - { - return; - } - - modelBuilder.Entity(b => - { - b.Property(x => ((IHasExtraProperties) x).ExtraProperties) - .HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject>(s) - ) - .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) - .Metadata.SetValueComparer(new ValueComparer>( - (d1, d2) => d1.SequenceEqual(d2), - d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value))); - }); - } - - protected virtual void ConfigureAuditProperties(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) - where TEntity : class - { - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IHasCreationTime)x).CreationTime) - .IsRequired() - .HasColumnName(nameof(IHasCreationTime.CreationTime)); - }); - } + modelBuilder.Entity().ConfigureByConvention(); - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IMayHaveCreator)x).CreatorId) - .IsRequired(false) - .HasColumnName(nameof(IMayHaveCreator.CreatorId)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IMustHaveCreator)x).CreatorId) - .IsRequired() - .HasColumnName(nameof(IMustHaveCreator.CreatorId)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IHasModificationTime)x).LastModificationTime) - .IsRequired(false) - .HasColumnName(nameof(IHasModificationTime.LastModificationTime)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IModificationAuditedObject)x).LastModifierId) - .IsRequired(false) - .HasColumnName(nameof(IModificationAuditedObject.LastModifierId)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((ISoftDelete) x).IsDeleted) - .IsRequired() - .HasDefaultValue(false) - .HasColumnName(nameof(ISoftDelete.IsDeleted)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IHasDeletionTime)x).DeletionTime) - .IsRequired(false) - .HasColumnName(nameof(IHasDeletionTime.DeletionTime)); - }); - } - - if (typeof(TEntity).IsAssignableTo()) - { - modelBuilder.Entity(b => - { - b.Property(x => ((IDeletionAuditedObject)x).DeleterId) - .IsRequired(false) - .HasColumnName(nameof(IDeletionAuditedObject.DeleterId)); - }); - } - } - - protected virtual void ConfigureTenantIdProperty(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) - where TEntity : class - { - if (!typeof(TEntity).IsAssignableTo()) - { - return; - } - - modelBuilder.Entity(b => - { - b.Property(x => ((IMultiTenant)x).TenantId) - .IsRequired(false) - .HasColumnName(nameof(IMultiTenant.TenantId)); - }); + ConfigureGlobalFilters(modelBuilder, mutableEntityType); } protected virtual void ConfigureGlobalFilters(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index b1824d2706..b1e59a6c95 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; -using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; namespace Volo.Abp.EntityFrameworkCore.Modeling @@ -59,15 +56,11 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling if (b.Metadata.ClrType.IsAssignableTo()) { b.Property>(nameof(IHasExtraProperties.ExtraProperties)) - .HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject>(s) - ) .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) - .Metadata.SetValueComparer(new ValueComparer>( + .ConfigureJsonConversionWithValueComparer( (d1, d2) => d1.SequenceEqual(d2), d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value))); + d => d.ToDictionary(k => k.Key, v => v.Value)); } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs new file mode 100644 index 0000000000..5dae5b4771 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Newtonsoft.Json; + +namespace Volo.Abp.EntityFrameworkCore.Modeling +{ + public static class AbpPropertyBuilderExtensions + { + public static void ConfigureJsonConversionWithValueComparer( + this PropertyBuilder propertyBuilder, + Expression> equalsExpression, + Expression> hashCodeExpression, + Expression> snapshotExpression) + { + propertyBuilder.HasConversion( + d => JsonConvert.SerializeObject(d, Formatting.None), + s => JsonConvert.DeserializeObject(s) + ) + .Metadata.SetValueComparer(new ValueComparer( + equalsExpression, + hashCodeExpression, + snapshotExpression)); + } + } +} \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index c7726c37e7..3b29cbf2a0 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -204,13 +204,10 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore identityResource.Property(x => x.DisplayName).HasMaxLength(IdentityResourceConsts.DisplayNameMaxLength); identityResource.Property(x => x.Description).HasMaxLength(IdentityResourceConsts.DescriptionMaxLength); identityResource.Property(x => x.Properties) - .HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject>(s) - ).Metadata.SetValueComparer(new ValueComparer>( + .ConfigureJsonConversionWithValueComparer( (d1, d2) => d1.SequenceEqual(d2), d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value))); + d => d.ToDictionary(k => k.Key, v => v.Value)); identityResource.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.IdentityResourceId).IsRequired(); }); From cf6fc616e594492f6ce02dc733355f8b03503d7e Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 26 Dec 2019 13:43:26 +0800 Subject: [PATCH 04/39] ConfigureJsonConversionWithValueComparer for Properties of ApiResource. --- .../IdentityServerDbContextModelCreatingExtensions.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index 3b29cbf2a0..f0278e7633 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -231,13 +231,10 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore apiResource.Property(x => x.DisplayName).HasMaxLength(ApiResourceConsts.DisplayNameMaxLength); apiResource.Property(x => x.Description).HasMaxLength(ApiResourceConsts.DescriptionMaxLength); apiResource.Property(x => x.Properties) - .HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject>(s) - ).Metadata.SetValueComparer(new ValueComparer>( + .ConfigureJsonConversionWithValueComparer( (d1, d2) => d1.SequenceEqual(d2), d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value))); + d => d.ToDictionary(k => k.Key, v => v.Value)); apiResource.HasMany(x => x.Secrets).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); apiResource.HasMany(x => x.Scopes).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); From 59ade77831012899a30d9502b3e4d0fcdf75373e Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 26 Dec 2019 16:25:38 +0800 Subject: [PATCH 05/39] Add more ConfigureJsonConversionWithValueComparer extension methods. --- .../Modeling/AbpPropertyBuilderExtensions.cs | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs index 5dae5b4771..93aeb792da 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs @@ -9,7 +9,87 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling { public static class AbpPropertyBuilderExtensions { - public static void ConfigureJsonConversionWithValueComparer( + /// + /// Serialize and property attributes using json(JsonConvert). + /// + /// The builder for the property being configured. + /// Sets the custom ValueComparer for this property. + public static PropertyBuilder ConfigureJsonConversionWithValueComparer( + this PropertyBuilder propertyBuilder, + ValueComparer valueComparer) + { + propertyBuilder.HasConversion( + d => JsonConvert.SerializeObject(d, Formatting.None), + s => JsonConvert.DeserializeObject(s) + ) + .Metadata.SetValueComparer(valueComparer); + + return propertyBuilder; + } + + /// + /// Serialize and property attributes using json(JsonConvert). + /// + /// The builder for the property being configured. + /// + /// If true, then EF will use ValueComparer if the type + /// implements it. This is usually used when byte arrays act as keys. + /// + public static PropertyBuilder ConfigureJsonConversionWithValueComparer( + this PropertyBuilder propertyBuilder, + bool favorStructuralComparisons) + { + propertyBuilder.HasConversion( + d => JsonConvert.SerializeObject(d, Formatting.None), + s => JsonConvert.DeserializeObject(s) + ) + .Metadata.SetValueComparer(new ValueComparer(favorStructuralComparisons)); + + return propertyBuilder; + } + + /// + /// Serialize and property attributes using json(JsonConvert). + /// Creates a new ValueComparer with the given comparison expression. + /// A shallow copy will be used for the snapshot. + /// + /// The builder for the property being configured. + /// The comparison expression. + /// The associated hash code generator. + public static PropertyBuilder ConfigureJsonConversionWithValueComparer( + this PropertyBuilder propertyBuilder, + Expression> equalsExpression, + Expression> hashCodeExpression) + { + propertyBuilder.HasConversion( + d => JsonConvert.SerializeObject(d, Formatting.None), + s => JsonConvert.DeserializeObject(s) + ) + .Metadata.SetValueComparer(new ValueComparer( + equalsExpression, + hashCodeExpression)); + + return propertyBuilder; + } + + /// + /// Serialize and property attributes using json(JsonConvert). + /// + /// Creates a new ValueComparer with the given comparison and + /// snapshotting expressions. + /// + /// + /// Snapshotting is the process of creating a copy of the value into a snapshot so it can + /// later be compared to determine if it has changed. For some types, such as collections, + /// this needs to be a deep copy of the collection rather than just a shallow copy of the + /// reference. + /// + /// + /// The builder for the property being configured. + /// The comparison expression. + /// The associated hash code generator. + /// The snapshot expression. + public static PropertyBuilder ConfigureJsonConversionWithValueComparer( this PropertyBuilder propertyBuilder, Expression> equalsExpression, Expression> hashCodeExpression, @@ -23,6 +103,8 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling equalsExpression, hashCodeExpression, snapshotExpression)); + + return propertyBuilder; } } } \ No newline at end of file From 908f28297a5bb61e5e6662bbcb4b094280965184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Thu, 2 Jan 2020 02:31:47 +0300 Subject: [PATCH 06/39] Implement IDisposable on AutofacServiceProvider. --- .../AutofacServiceProvider.cs | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProvider.cs b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProvider.cs index ff2d45b2a4..1c1a24ff66 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProvider.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProvider.cs @@ -1,6 +1,6 @@ // This software is part of the Autofac IoC container // Copyright © 2015 Autofac Contributors -// http://autofac.org +// https://autofac.org // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -33,19 +33,21 @@ namespace Autofac.Extensions.DependencyInjection /// /// /// - public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService + public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable { - private readonly IComponentContext _componentContext; + private readonly ILifetimeScope _lifetimeScope; + + private bool _disposed = false; /// /// Initializes a new instance of the class. /// - /// - /// The component context from which services should be resolved. + /// + /// The lifetime scope from which services will be resolved. /// - public AutofacServiceProvider(IComponentContext componentContext) + public AutofacServiceProvider(ILifetimeScope lifetimeScope) { - this._componentContext = componentContext; + this._lifetimeScope = lifetimeScope; } /// @@ -66,7 +68,7 @@ namespace Autofac.Extensions.DependencyInjection /// public object GetRequiredService(Type serviceType) { - return this._componentContext.Resolve(serviceType); + return this._lifetimeScope.Resolve(serviceType); } /// @@ -81,7 +83,40 @@ namespace Autofac.Extensions.DependencyInjection /// public object GetService(Type serviceType) { - return this._componentContext.ResolveOptional(serviceType); + return this._lifetimeScope.ResolveOptional(serviceType); + } + + /// + /// Gets the underlying instance of . + /// + public ILifetimeScope LifetimeScope => _lifetimeScope; + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (!this._disposed) + { + this._disposed = true; + if (disposing) + { + this._lifetimeScope.Dispose(); + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); } } } \ No newline at end of file From bf1960213fb5aa319454a095226ba1ec82018369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Thu, 2 Jan 2020 02:33:52 +0300 Subject: [PATCH 07/39] Listen IHostApplicationLifetime Stopping and Stopped event. --- .../AbpApplicationBuilderExtensions.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 22c0079571..033fef813f 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.RequestLocalization; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Volo.Abp; using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.ExceptionHandling; @@ -19,8 +20,21 @@ namespace Microsoft.AspNetCore.Builder { Check.NotNull(app, nameof(app)); + var application = app.ApplicationServices.GetRequiredService(); + var applicationLifetime = app.ApplicationServices.GetRequiredService(); + + applicationLifetime.ApplicationStopping.Register(() => + { + application.Shutdown(); + }); + + applicationLifetime.ApplicationStopped.Register(() => + { + application.Dispose(); + }); + app.ApplicationServices.GetRequiredService>().Value = app; - app.ApplicationServices.GetRequiredService().Initialize(app.ApplicationServices); + application.Initialize(app.ApplicationServices); } public static IApplicationBuilder UseAuditing(this IApplicationBuilder app) @@ -42,7 +56,8 @@ namespace Microsoft.AspNetCore.Builder .UseMiddleware(); } - public static IApplicationBuilder UseAbpRequestLocalization(this IApplicationBuilder app, Action optionsAction = null) + public static IApplicationBuilder UseAbpRequestLocalization(this IApplicationBuilder app, + Action optionsAction = null) { app.ApplicationServices .GetRequiredService() @@ -62,4 +77,4 @@ namespace Microsoft.AspNetCore.Builder return app.UseMiddleware(); } } -} +} \ No newline at end of file From 8d5d0adb83f261b9e51188146638ea2b4339325d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Thu, 2 Jan 2020 02:35:33 +0300 Subject: [PATCH 08/39] Disposing AbpApplicationWithExternalServiceProvider. --- .../Abp/AbpApplicationWithExternalServiceProvider.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs index 1becd3be9b..58f75f3b96 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs @@ -26,5 +26,15 @@ namespace Volo.Abp InitializeModules(); } + + public override void Dispose() + { + base.Dispose(); + + if (ServiceProvider is IDisposable disposableServiceProvider) + { + disposableServiceProvider.Dispose(); + } + } } } From 6c7363c4f98efd88715881780250325a2c0b7f50 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 4 Jan 2020 13:10:33 +0800 Subject: [PATCH 09/39] Fix DocumentPreferences may be null. --- .../src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs index c5188b3760..c653068baa 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs @@ -439,7 +439,7 @@ namespace Volo.Docs.Pages.Documents.Project var value = keyValue.Split("=")[1]; UserPreferences.Add(key, value); - UserPreferences.Add(key + "_Value", DocumentPreferences.Parameters?.FirstOrDefault(p => p.Name == key) + UserPreferences.Add(key + "_Value", DocumentPreferences?.Parameters?.FirstOrDefault(p => p.Name == key) ?.Values.FirstOrDefault(v => v.Key == value).Value); } } @@ -456,7 +456,7 @@ namespace Volo.Docs.Pages.Documents.Project UserPreferences.Add(key, value); UserPreferences.Add(key + "_Value", - DocumentPreferences.Parameters?.FirstOrDefault(p => p.Name == key)?.Values + DocumentPreferences?.Parameters?.FirstOrDefault(p => p.Name == key)?.Values .FirstOrDefault(v => v.Key == value).Value); } From d2cca7306e65076f38d73b63df865168217df75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Mon, 6 Jan 2020 09:36:09 +0300 Subject: [PATCH 10/39] Add ValidationInterceptor. --- ...lectionDynamicHttpClientProxyExtensions.cs | 6 ++++ .../Volo.Abp.Http.Client.csproj | 1 + .../Abp/Http/Client/AbpHttpClientModule.cs | 4 ++- .../PersonAppServiceClientProxy_Tests.cs | 32 ++++++++++++++----- .../Application/PersonAppService_Tests.cs | 15 ++++++--- .../Abp/TestApp/Application/Dto/PersonDto.cs | 2 ++ 6 files changed, 46 insertions(+), 14 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs index f34a732cb0..e3fd273026 100644 --- a/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs +++ b/framework/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs @@ -8,6 +8,7 @@ using Volo.Abp; using Volo.Abp.Castle.DynamicProxy; using Volo.Abp.Http.Client; using Volo.Abp.Http.Client.DynamicProxying; +using Volo.Abp.Validation; namespace Microsoft.Extensions.DependencyInjection { @@ -154,6 +155,9 @@ namespace Microsoft.Extensions.DependencyInjection services.AddTransient(interceptorType); var interceptorAdapterType = typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType); + + var validationInterceptorAdapterType = + typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(typeof(ValidationInterceptor)); if (asDefaultService) { @@ -162,6 +166,7 @@ namespace Microsoft.Extensions.DependencyInjection serviceProvider => ProxyGeneratorInstance .CreateInterfaceProxyWithoutTarget( type, + (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType), (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType) ) ); @@ -174,6 +179,7 @@ namespace Microsoft.Extensions.DependencyInjection var service = ProxyGeneratorInstance .CreateInterfaceProxyWithoutTarget( type, + (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType), (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType) ); diff --git a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj index 8a5172db8b..e7198718b1 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj +++ b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj @@ -24,6 +24,7 @@ + \ No newline at end of file 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 f2a7540956..e98c0738fd 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 @@ -3,6 +3,7 @@ using Volo.Abp.Castle; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; +using Volo.Abp.Validation; namespace Volo.Abp.Http.Client { @@ -10,7 +11,8 @@ namespace Volo.Abp.Http.Client typeof(AbpHttpModule), typeof(AbpCastleCoreModule), typeof(AbpThreadingModule), - typeof(AbpMultiTenancyModule) + typeof(AbpMultiTenancyModule), + typeof(AbpValidationModule) )] public class AbpHttpClientModule : AbpModule { diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index 80a68e5a18..4dddcd6ce8 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using NSubstitute.Extensions; using Shouldly; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; @@ -9,6 +10,7 @@ using Volo.Abp.Http.Client; using Volo.Abp.TestApp.Application; using Volo.Abp.TestApp.Application.Dto; using Volo.Abp.TestApp.Domain; +using Volo.Abp.Validation; using Xunit; namespace Volo.Abp.Http.DynamicProxying @@ -38,7 +40,8 @@ namespace Volo.Abp.Http.DynamicProxying [Fact] public async Task GetList() { - var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()).ConfigureAwait(false); + var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()) + .ConfigureAwait(false); people.TotalCount.ShouldBeGreaterThan(0); people.Items.Count.ShouldBe((int) people.TotalCount); } @@ -59,11 +62,11 @@ namespace Volo.Abp.Http.DynamicProxying { var uniquePersonName = Guid.NewGuid().ToString(); - var person = await _peopleAppService.CreateAsync(new PersonDto - { - Name = uniquePersonName, - Age = 42 - } + var person = await _peopleAppService.CreateAsync(new PersonDto + { + Name = uniquePersonName, + Age = 42 + } ).ConfigureAwait(false); person.ShouldNotBeNull(); @@ -74,7 +77,20 @@ namespace Volo.Abp.Http.DynamicProxying personInDb.ShouldNotBeNull(); personInDb.Id.ShouldBe(person.Id); } - + + [Fact] + public async Task Create_Validate_Exception() + { + await Assert.ThrowsAsync(async () => + { + var person = await _peopleAppService.CreateAsync(new PersonDto + { + Age = 42 + } + ).ConfigureAwait(false); + }).ConfigureAwait(false); + } + [Fact] public async Task Update() { @@ -135,4 +151,4 @@ namespace Volo.Abp.Http.DynamicProxying result.Inner1.Inner2.Value3.ShouldBe("value three"); } } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs b/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs index fc38b437b8..47fa69d64f 100644 --- a/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs +++ b/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs @@ -19,7 +19,7 @@ namespace Volo.Abp.TestApp.Application public PersonAppService_Tests() { - _peopleAppService = ServiceProvider.GetRequiredService(); + _peopleAppService = ServiceProvider.GetRequiredService(); } protected override void AfterAddApplication(IServiceCollection services) @@ -31,14 +31,17 @@ namespace Volo.Abp.TestApp.Application [Fact] public async Task GetList() { - var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()).ConfigureAwait(false); + var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()) + .ConfigureAwait(false); people.Items.Count.ShouldBeGreaterThan(0); } [Fact] public async Task Create() { - var personDto = await _peopleAppService.CreateAsync(new PersonDto()).ConfigureAwait(false); + var uniquePersonName = Guid.NewGuid().ToString(); + var personDto = await _peopleAppService.CreateAsync(new PersonDto {Name = uniquePersonName}) + .ConfigureAwait(false); var repository = ServiceProvider.GetService>(); var person = await repository.FindAsync(personDto.Id).ConfigureAwait(false); @@ -52,7 +55,9 @@ namespace Volo.Abp.TestApp.Application { _fakeCurrentTenant.Id.Returns(TestDataBuilder.TenantId1); - var personDto = await _peopleAppService.CreateAsync(new PersonDto()).ConfigureAwait(false); + var uniquePersonName = Guid.NewGuid().ToString(); + var personDto = await _peopleAppService.CreateAsync(new PersonDto {Name = uniquePersonName}) + .ConfigureAwait(false); var repository = ServiceProvider.GetService>(); var person = await repository.FindAsync(personDto.Id).ConfigureAwait(false); @@ -63,4 +68,4 @@ namespace Volo.Abp.TestApp.Application person.TenantId.ShouldBe(TestDataBuilder.TenantId1); } } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/PersonDto.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/PersonDto.cs index b878b4f9c4..88ed3452af 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/PersonDto.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/PersonDto.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Application.Dtos; using Volo.Abp.MultiTenancy; @@ -6,6 +7,7 @@ namespace Volo.Abp.TestApp.Application.Dto { public class PersonDto : EntityDto, IMultiTenant { + [Required] public string Name { get; set; } public int Age { get; set; } From d7761c6dfe0a9772075151a6654ef78dc55b690d Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 7 Jan 2020 16:10:19 +0800 Subject: [PATCH 11/39] Add AbpAccountApplicationModule to the IdentityServer project of the module template. --- .../MyCompanyName.MyProjectName.IdentityServer.csproj | 1 + .../MyProjectNameIdentityServerModule.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj index c2a1d1dde6..b25ce4b603 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj @@ -22,6 +22,7 @@ + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs index 76d930d7ea..5f0f80c05e 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -11,6 +11,7 @@ using StackExchange.Redis; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp; +using Volo.Abp.Account; using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.Mvc; @@ -44,6 +45,7 @@ namespace MyCompanyName.MyProjectName { [DependsOn( typeof(AbpAccountWebIdentityServerModule), + typeof(AbpAccountApplicationModule), typeof(AbpAspNetCoreMvcUiMultiTenancyModule), typeof(AbpAspNetCoreMvcModule), typeof(AbpAspNetCoreMvcUiBasicThemeModule), From 8f0ce16a2404f0f02d9509ce2667520fe8ebc8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 11:26:12 +0300 Subject: [PATCH 12/39] Move setting the IApplicationBuilder line to top. --- .../AspNetCore/Builder/AbpApplicationBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 033fef813f..295a8bd100 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Builder { Check.NotNull(app, nameof(app)); + app.ApplicationServices.GetRequiredService>().Value = app; var application = app.ApplicationServices.GetRequiredService(); var applicationLifetime = app.ApplicationServices.GetRequiredService(); @@ -33,7 +34,6 @@ namespace Microsoft.AspNetCore.Builder application.Dispose(); }); - app.ApplicationServices.GetRequiredService>().Value = app; application.Initialize(app.ApplicationServices); } From 0fb7ba4614ebb64a00f19a9bef277a0fb9bee764 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 7 Jan 2020 11:44:13 +0300 Subject: [PATCH 13/39] feat(account): add the enableLocalLogin condition to auth-wrapper --- .../Settings/AccountSettingDefinitionProvider.cs | 4 ++-- .../auth-wrapper/auth-wrapper.component.html | 12 +++++++++++- .../auth-wrapper/auth-wrapper.component.ts | 6 ++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Settings/AccountSettingDefinitionProvider.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Settings/AccountSettingDefinitionProvider.cs index 034b2b1a01..907100c61a 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Settings/AccountSettingDefinitionProvider.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Settings/AccountSettingDefinitionProvider.cs @@ -13,7 +13,7 @@ namespace Volo.Abp.Account.Settings AccountSettingNames.IsSelfRegistrationEnabled, "true", L("DisplayName:Abp.Account.IsSelfRegistrationEnabled"), - L("Description:Abp.Account.IsSelfRegistrationEnabled")) + L("Description:Abp.Account.IsSelfRegistrationEnabled"), isVisibleToClients : true) ); context.Add( @@ -21,7 +21,7 @@ namespace Volo.Abp.Account.Settings AccountSettingNames.EnableLocalLogin, "true", L("DisplayName:Abp.Account.EnableLocalLogin"), - L("Description:Abp.Account.EnableLocalLogin")) + L("Description:Abp.Account.EnableLocalLogin"), isVisibleToClients : true) ); } diff --git a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html index f25b6eae0c..531c598a5e 100644 --- a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.html @@ -5,7 +5,10 @@ > + + +
+ {{ 'AbpAccount::InvalidLoginRequest' | abpLocalization }} + {{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | abpLocalization }} +
+
diff --git a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts index d38754ff50..aadbbfa656 100644 --- a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts @@ -1,5 +1,8 @@ import { Component, Input, TemplateRef } from '@angular/core'; import { Account } from '../../models/account'; +import { Select } from '@ngxs/store'; +import { ConfigState } from '@abp/ng.core'; +import { Observable } from 'rxjs'; @Component({ selector: 'abp-auth-wrapper', @@ -8,6 +11,9 @@ import { Account } from '../../models/account'; }) export class AuthWrapperComponent implements Account.AuthWrapperComponentInputs, Account.AuthWrapperComponentOutputs { + @Select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')) + enableLocalLogin$: Observable<'True' | 'False'>; + @Input() readonly mainContentRef: TemplateRef; From b07a181db731e4ed80564d626b3d81423a5175e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 15:31:40 +0300 Subject: [PATCH 14/39] Fixed #2568: User menu in Module.Web.Host not working. --- .../Menus/MyProjectNameMenuContributor.cs | 9 ++- .../Controllers/AccountController.cs | 9 +++ .../MyProjectNameWebHostMenuContributor.cs | 60 +++++++++++++++++++ .../MyProjectNameWebHostModule.cs | 12 +++- .../Localization/MyProjectName/en.json | 2 +- .../Localization/MyProjectName/tr.json | 6 ++ 6 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/Controllers/AccountController.cs create mode 100644 templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostMenuContributor.cs create mode 100644 templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/Menus/MyProjectNameMenuContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/Menus/MyProjectNameMenuContributor.cs index f7ef2c2093..2a12f2ee32 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/Menus/MyProjectNameMenuContributor.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/Menus/MyProjectNameMenuContributor.cs @@ -8,6 +8,7 @@ using MyCompanyName.MyProjectName.MultiTenancy; using Volo.Abp.Account.Localization; using Volo.Abp.TenantManagement.Web.Navigation; using Volo.Abp.UI.Navigation; +using Volo.Abp.Users; namespace MyCompanyName.MyProjectName.Web.Menus { @@ -51,11 +52,15 @@ namespace MyCompanyName.MyProjectName.Web.Menus { var l = context.ServiceProvider.GetRequiredService>(); var accountStringLocalizer = context.ServiceProvider.GetRequiredService>(); + var currentUser = context.ServiceProvider.GetRequiredService(); var identityServerUrl = _configuration["AuthServer:Authority"] ?? ""; - context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountStringLocalizer["ManageYourProfile"], $"{identityServerUrl.EnsureEndsWith('/')}Account/Manage", icon: "fa fa-cog", order: 1000, null, "_blank")); - context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", l["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000)); + if (currentUser.IsAuthenticated) + { + context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountStringLocalizer["ManageYourProfile"], $"{identityServerUrl.EnsureEndsWith('/')}Account/Manage", icon: "fa fa-cog", order: 1000, null, "_blank")); + context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", l["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000)); + } return Task.CompletedTask; } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/Controllers/AccountController.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/Controllers/AccountController.cs new file mode 100644 index 0000000000..318e7191a2 --- /dev/null +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/Controllers/AccountController.cs @@ -0,0 +1,9 @@ +using Volo.Abp.AspNetCore.Mvc.Authentication; + +namespace MyCompanyName.MyProjectName.Controllers +{ + public class AccountController : ChallengeAccountController + { + + } +} diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostMenuContributor.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostMenuContributor.cs new file mode 100644 index 0000000000..430a1c237c --- /dev/null +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostMenuContributor.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using Localization.Resources.AbpUi; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using MyCompanyName.MyProjectName.Localization; +using Volo.Abp.UI.Navigation; +using Volo.Abp.Users; + +namespace MyCompanyName.MyProjectName +{ + public class MyProjectNameWebHostMenuContributor : IMenuContributor + { + private readonly IConfiguration _configuration; + + public MyProjectNameWebHostMenuContributor(IConfiguration configuration) + { + _configuration = configuration; + } + + public Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.User) + { + AddLogoutItemToMenu(context); + } + + return Task.CompletedTask; + } + + private void AddLogoutItemToMenu(MenuConfigurationContext context) + { + var currentUser = context.ServiceProvider.GetRequiredService(); + var l = context.ServiceProvider.GetRequiredService>(); + + if (currentUser.IsAuthenticated) + { + context.Menu.Items.Add(new ApplicationMenuItem( + "Account.Manage", + l["ManageYourProfile"], + $"{_configuration["AuthServer:Authority"].EnsureEndsWith('/')}Account/Manage", + icon: "fa fa-cog", + order: int.MaxValue - 1001, + null, + "_blank") + ); + + + context.Menu.Items.Add(new ApplicationMenuItem( + "Account.Logout", + l["Logout"], + "/Account/Logout", + "fas fa-power-off", + order: int.MaxValue - 1000 + )); + } + } + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs index 6eb3dc9ea3..473bdab2cf 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebHostModule.cs @@ -37,6 +37,7 @@ using Volo.Abp.TenantManagement; using Volo.Abp.TenantManagement.Web; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.UI; +using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; namespace MyCompanyName.MyProjectName @@ -76,6 +77,7 @@ namespace MyCompanyName.MyProjectName var hostingEnvironment = context.Services.GetHostingEnvironment(); var configuration = context.Services.GetConfiguration(); + ConfigureMenu(configuration); ConfigureCache(configuration); ConfigureUrls(configuration); ConfigureAuthentication(context, configuration); @@ -85,7 +87,15 @@ namespace MyCompanyName.MyProjectName ConfigureMultiTenancy(); ConfigureRedis(context, configuration, hostingEnvironment); } - + + private void ConfigureMenu(IConfiguration configuration) + { + Configure(options => + { + options.MenuContributors.Add(new MyProjectNameWebHostMenuContributor(configuration)); + }); + } + private void ConfigureCache(IConfiguration configuration) { Configure(options => diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json index 92e4e9582b..171d9a1220 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json @@ -1,6 +1,6 @@ { "culture": "en", "texts": { - + "ManageYourProfile": "Manage your profile" } } \ No newline at end of file diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json new file mode 100644 index 0000000000..2b48193353 --- /dev/null +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "ManageYourProfile": "Profil yönetimi" + } +} \ No newline at end of file From 4ed7d5717f8ad047f73871efac53c6662d55c2b7 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 7 Jan 2020 16:15:10 +0300 Subject: [PATCH 15/39] account mvc IsSelfRegistrationEnabled --- .../src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml | 5 +++-- .../Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml index 94ef0e45a2..fca4622a29 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.Account.Settings +@using Volo.Abp.Settings @model Volo.Abp.Account.Web.Pages.Account.LoginModel @inherits Volo.Abp.Account.Web.Pages.Account.AccountPage @inject Volo.Abp.Settings.ISettingProvider SettingProvider @@ -7,8 +8,8 @@ {
-

@L["Login"]

- @if (string.Equals(await SettingProvider.GetOrNullAsync(AccountSettingNames.IsSelfRegistrationEnabled), "true", StringComparison.OrdinalIgnoreCase)) +

@L["Login"]

+ @if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled)) { @L["AreYouANewUser"] diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 4d95613fe1..5abb564b09 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -45,7 +45,8 @@ namespace Volo.Abp.Account.Web.Pages.Account protected virtual async Task CheckSelfRegistrationAsync() { - if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled).ConfigureAwait(false)) + if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled).ConfigureAwait(false) || + !await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin).ConfigureAwait(false)) { throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]); } From 4bd5fa750c9590fce2895bcf5be8123b19b66d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 17:29:59 +0300 Subject: [PATCH 16/39] Update DeveloperApiKeyResult.cs --- .../Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs index 0b1fe54c2f..481aa6f404 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.Cli.Licensing public bool HasActiveLicense { get; set; } public string OrganizationName { get; set; } public string ApiKey { get; set; } - public DateTime LicenseEndTime { get; set; } + public DateTime? LicenseEndTime { get; set; } public string LicenseCode { get; set; } } } \ No newline at end of file From 57a9421495aa892c7b31d9d28eb346334c73c916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 18:46:05 +0300 Subject: [PATCH 17/39] New document: Switch to another DBMS for Entity Framework Core --- docs/en/Entity-Framework-Core-Other-DBMS.md | 90 +++++++++++++++++++++ docs/en/Entity-Framework-Core.md | 2 + docs/en/docs-nav.json | 4 + 3 files changed, 96 insertions(+) create mode 100644 docs/en/Entity-Framework-Core-Other-DBMS.md diff --git a/docs/en/Entity-Framework-Core-Other-DBMS.md b/docs/en/Entity-Framework-Core-Other-DBMS.md new file mode 100644 index 0000000000..854309d070 --- /dev/null +++ b/docs/en/Entity-Framework-Core-Other-DBMS.md @@ -0,0 +1,90 @@ +# Switch to another DBMS for Entity Framework Core + +**[The application startup template](Startup-Templates/Application.md)** comes with SQL Server provider pre-configured for the Entity Framework Core. Entity Framework Core supports [many other DBMSs](https://docs.microsoft.com/en-us/ef/core/providers/) and you can use any of them with your ABP based applications. + +ABP framework provides integration packages for some common DBMSs to make the configuration a bit easier (see the [entity framework core document](Entity-Framework-Core.md) for a list of available integration packages). However, you can configure your DBMS provider without these integration packages. + +While using the integration package is always recommended (it also makes standard for the depended version across different modules), you can do it manually if there is no integration package for your DBMS provider. + +This document explains how to switch to MySQL without using [the MySQL integration package](Entity-Framework-Core-MySQL.md). + +## Replace the SQL Server Dependency + +* Remove the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package dependency from the `.EntityFrameworkCore` project. +* Add the [Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql/) NuGet package dependency to your `.EntityFrameworkCore` project. + +## Remove the Module Dependency + +Remove the `AbpEntityFrameworkCoreSqlServerModule` from the dependency list of your ***YourProjectName*EntityFrameworkCoreModule** class. + +## Change the UseSqlServer() Calls + +Find the following code part inside the *YourProjectName*EntityFrameworkCoreModule class: + +````csharp +Configure(options => +{ + options.UseSqlServer(); +}); +```` + +Replace it with the following code part: + +````csharp +Configure(options => +{ + options.Configure(ctx => + { + if (ctx.ExistingConnection != null) + { + ctx.DbContextOptions.UseMySql(ctx.ExistingConnection); + } + else + { + ctx.DbContextOptions.UseMySql(ctx.ConnectionString); + } + }); +}); +```` + +* `UseMySql` calls in this code is defined by the Pomelo.EntityFrameworkCore.MySql package and you can use its additional options if you need. +* This code first checks if there is an existing (active) connection to the same database in the current request and reuses it if possible. This allows to share a single transaction among different DbContext types. ABP handles the rest of the things. +* It uses `ctx.ConnectionString` and passes to the `UseMySql` if there is no active connection (which will cause to create a new database connection). Using the `ctx.ConnectionString` is important here. Don't pass a static connection string (or a connection string from a configuration). Because ABP [dynamically determines the correct connection string](Connection-Strings.md) in a multi-database or [multi-tenant](Multi-Tenancy.md) environment. + +## Change the Connection Strings + +MySQL connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/mysql/ ) for details of MySQL connection string options. + +You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. + +## Change the Migrations DbContext + +MySQL DBMS has some slight differences than the SQL Server. Some module database mapping configuration (especially the field lengths) causes problems with MySQL. For example, some of the the [IdentityServer module](Modules/IdentityServer.md) tables has such problems and it provides an option to configure the fields based on your DBMS. + +The startup template contains a *YourProjectName*MigrationsDbContext which is responsible to maintain and migrate the database schema. This DbContext basically calls extension methods of the depended modules to configure their database tables. + +Open the *YourProjectName*MigrationsDbContext and change the `builder.ConfigureIdentityServer();` line as shown below: + +````csharp +builder.ConfigureIdentityServer(options => +{ + options.DatabaseProvider = EfCoreDatabaseProvider.MySql; +}); +```` + +Then `ConfigureIdentityServer()` method will set the field lengths to not exceed the MySQL limits. Refer to related module documentation if you have any problem while creating or executing the database migrations. + +## Re-Generate the Migrations + +The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core Migrations depend on the selected DBMS provider. So, changing the DBMS provider will cause the migration fails. + +* Delete the Migrations folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. +* Run `Add-Migration "Initial"` on the Package Manager Console (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). + +This will create a database migration with all database objects (tables) configured. + +Run the `.DbMigrator` project to create the database and seed the initial data. + +## Run the Application + +It is ready. Just run the application and enjoy coding. \ No newline at end of file diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index b73e16a3e5..9d29f58fd2 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -38,6 +38,8 @@ ABP framework provides integration packages for some common DBMSs to make the co * [MySQL](Entity-Framework-Core-MySQL.md) * [PostgreSQL](Entity-Framework-Core-PostgreSQL.md) +* [SQLite](Entity-Framework-Core-SQLite.md) +* [Others](Entity-Framework-Core-Other-DBMS.md) ## Creating DbContext diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 9943c3b6af..e167d50c47 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -281,6 +281,10 @@ { "text": "Switch to SQLite", "path": "Entity-Framework-Core-SQLite.md" + }, + { + "text": "Switch to another DBMS", + "path": "Entity-Framework-Core-Other-DBMS.md" } ] }, From 1a449829db535f162b48e2fbb5a6ea741c123297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 19:02:58 +0300 Subject: [PATCH 18/39] Added new sections to the Connection-Strings and Dependency-Injection documents. --- docs/en/Connection-Strings.md | 11 ++++++++++- docs/en/Dependency-Injection.md | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/en/Connection-Strings.md b/docs/en/Connection-Strings.md index a080e10dea..57a156ff7b 100644 --- a/docs/en/Connection-Strings.md +++ b/docs/en/Connection-Strings.md @@ -61,4 +61,13 @@ Once you want to separate a module's database, you typically will need to create ## Multi-Tenancy -See [the multi-tenancy document](Multi-Tenancy.md) to learn how to use separate databases for tenants. \ No newline at end of file +See [the multi-tenancy document](Multi-Tenancy.md) to learn how to use separate databases for tenants. + +## Replace the Connection String Resolver + +ABP defines the `IConnectionStringResolver` and uses it whenever it needs a connection string. It has two pre-built implementations: + +* `DefaultConnectionStringResolver` uses the `AbpDbConnectionOptions` to select the connection string based on the rules defined in the "Configure the Connection Strings" section above. +* `MultiTenantConnectionStringResolver` used for multi-tenant applications and tries to get the configured connection string for the current tenant if available. It uses the `ITenantStore` to find the connection strings. It inherits from the `DefaultConnectionStringResolver` and fallbacks to the base logic if no connection string specified for the current tenant. + +If you need a custom logic to determine the connection string, implement the `IConnectionStringResolver` interface (optionally derive from the existing implementations) and replace the existing implementation using the [dependency injection](Dependency-Injection.md) system. \ No newline at end of file diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index eb0da4a0e4..73cd8095ea 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -166,6 +166,26 @@ public class BlogModule : AbpModule } ```` +### Replace a Service + +If you need to replace an existing service (defined by the ABP framework or another module dependency), you have two options; + +1. Use the `Dependency` attribute of the ABP framework as explained above. +2. Use the `IServiceCollection.Replace` method of the Microsoft Dependency Injection library. Example: + +````csharp +public class MyModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + //Replacing the IConnectionStringResolver service + context.Services.Replace(ServiceDescriptor.Transient()); + } +} +```` + + + ## Injecting Dependencies There are three common ways of using a service that has already been registered. From 789b3d33a034b4992dc369444b4f6afaf1cf2e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 7 Jan 2020 19:03:16 +0300 Subject: [PATCH 19/39] Update Dependency-Injection.md --- docs/en/Dependency-Injection.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index 73cd8095ea..335e829461 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -184,8 +184,6 @@ public class MyModule : AbpModule } ```` - - ## Injecting Dependencies There are three common ways of using a service that has already been registered. From e754f3350ff1b6c3b7009c46566998aec313c3cc Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:04:05 +0800 Subject: [PATCH 20/39] Implement generic AbpJsonValueConverter --- .../ValueConverters/AbpJsonValueConverter.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs new file mode 100644 index 0000000000..31a1418022 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Newtonsoft.Json; + +namespace Volo.Abp.EntityFrameworkCore.ValueConverters +{ + public class AbpJsonValueConverter : ValueConverter + { + public AbpJsonValueConverter() + : base( + d => JsonConvert.SerializeObject(d, Formatting.None), + s => JsonConvert.DeserializeObject(s)) + { + } + } +} From 288b80cfdc3f003ecd045802529cc4684bda1c24 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:04:27 +0800 Subject: [PATCH 21/39] Implement AbpDictionaryValueConverter --- .../AbpDictionaryValueComparer.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueComparers/AbpDictionaryValueComparer.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueComparers/AbpDictionaryValueComparer.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueComparers/AbpDictionaryValueComparer.cs new file mode 100644 index 0000000000..4efd8bfa63 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueComparers/AbpDictionaryValueComparer.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace Volo.Abp.EntityFrameworkCore.ValueComparers +{ + public class AbpDictionaryValueComparer : ValueComparer> + { + public AbpDictionaryValueComparer() + : base( + (d1, d2) => d1.SequenceEqual(d2), + d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), + d => d.ToDictionary(k => k.Key, v => v.Value)) + { + } + } +} From 3de25ee4b31b3382b3632cd9f99656dd53971d38 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:06:58 +0800 Subject: [PATCH 22/39] Refactor to use ABP converter and comparer --- .../Modeling/AbpEntityTypeBuilderExtensions.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index b1e59a6c95..dceaeaf74f 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; +using Volo.Abp.EntityFrameworkCore.ValueComparers; +using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.MultiTenancy; namespace Volo.Abp.EntityFrameworkCore.Modeling @@ -57,10 +58,8 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling { b.Property>(nameof(IHasExtraProperties.ExtraProperties)) .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)) - .ConfigureJsonConversionWithValueComparer( - (d1, d2) => d1.SequenceEqual(d2), - d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value)); + .HasConversion(new AbpJsonValueConverter>()) + .Metadata.SetValueComparer(new AbpDictionaryValueComparer()); } } From 986e58f477ab76fd4472f0cb2cb73178dd587772 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:12:03 +0800 Subject: [PATCH 23/39] Refactor to use ABP converter and comparer --- ...ityServerDbContextModelCreatingExtensions.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index f0278e7633..33e2b011c4 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Newtonsoft.Json; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.EntityFrameworkCore.ValueComparers; +using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.IdentityServer.ApiResources; using Volo.Abp.IdentityServer.Clients; using Volo.Abp.IdentityServer.Grants; @@ -204,10 +203,8 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore identityResource.Property(x => x.DisplayName).HasMaxLength(IdentityResourceConsts.DisplayNameMaxLength); identityResource.Property(x => x.Description).HasMaxLength(IdentityResourceConsts.DescriptionMaxLength); identityResource.Property(x => x.Properties) - .ConfigureJsonConversionWithValueComparer( - (d1, d2) => d1.SequenceEqual(d2), - d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value)); + .HasConversion(new AbpJsonValueConverter>()) + .Metadata.SetValueComparer(new AbpDictionaryValueComparer()); identityResource.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.IdentityResourceId).IsRequired(); }); @@ -231,10 +228,8 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore apiResource.Property(x => x.DisplayName).HasMaxLength(ApiResourceConsts.DisplayNameMaxLength); apiResource.Property(x => x.Description).HasMaxLength(ApiResourceConsts.DescriptionMaxLength); apiResource.Property(x => x.Properties) - .ConfigureJsonConversionWithValueComparer( - (d1, d2) => d1.SequenceEqual(d2), - d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), - d => d.ToDictionary(k => k.Key, v => v.Value)); + .HasConversion(new AbpJsonValueConverter>()) + .Metadata.SetValueComparer(new AbpDictionaryValueComparer()); apiResource.HasMany(x => x.Secrets).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); apiResource.HasMany(x => x.Scopes).WithOne().HasForeignKey(x => x.ApiResourceId).IsRequired(); From a6608af1dcdd57311643146adc0658c2a6e5892c Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:17:05 +0800 Subject: [PATCH 24/39] Remove unnecessary using directive --- .../ValueConverters/AbpJsonValueConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs index 31a1418022..e77b2842a8 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Newtonsoft.Json; namespace Volo.Abp.EntityFrameworkCore.ValueConverters From e199513e3e56390c06f009f439054b8f36aff562 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 8 Jan 2020 00:17:53 +0800 Subject: [PATCH 25/39] Remove unnecessary extensions --- .../Modeling/AbpPropertyBuilderExtensions.cs | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs deleted file mode 100644 index 93aeb792da..0000000000 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpPropertyBuilderExtensions.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; - -namespace Volo.Abp.EntityFrameworkCore.Modeling -{ - public static class AbpPropertyBuilderExtensions - { - /// - /// Serialize and property attributes using json(JsonConvert). - /// - /// The builder for the property being configured. - /// Sets the custom ValueComparer for this property. - public static PropertyBuilder ConfigureJsonConversionWithValueComparer( - this PropertyBuilder propertyBuilder, - ValueComparer valueComparer) - { - propertyBuilder.HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject(s) - ) - .Metadata.SetValueComparer(valueComparer); - - return propertyBuilder; - } - - /// - /// Serialize and property attributes using json(JsonConvert). - /// - /// The builder for the property being configured. - /// - /// If true, then EF will use ValueComparer if the type - /// implements it. This is usually used when byte arrays act as keys. - /// - public static PropertyBuilder ConfigureJsonConversionWithValueComparer( - this PropertyBuilder propertyBuilder, - bool favorStructuralComparisons) - { - propertyBuilder.HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject(s) - ) - .Metadata.SetValueComparer(new ValueComparer(favorStructuralComparisons)); - - return propertyBuilder; - } - - /// - /// Serialize and property attributes using json(JsonConvert). - /// Creates a new ValueComparer with the given comparison expression. - /// A shallow copy will be used for the snapshot. - /// - /// The builder for the property being configured. - /// The comparison expression. - /// The associated hash code generator. - public static PropertyBuilder ConfigureJsonConversionWithValueComparer( - this PropertyBuilder propertyBuilder, - Expression> equalsExpression, - Expression> hashCodeExpression) - { - propertyBuilder.HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject(s) - ) - .Metadata.SetValueComparer(new ValueComparer( - equalsExpression, - hashCodeExpression)); - - return propertyBuilder; - } - - /// - /// Serialize and property attributes using json(JsonConvert). - /// - /// Creates a new ValueComparer with the given comparison and - /// snapshotting expressions. - /// - /// - /// Snapshotting is the process of creating a copy of the value into a snapshot so it can - /// later be compared to determine if it has changed. For some types, such as collections, - /// this needs to be a deep copy of the collection rather than just a shallow copy of the - /// reference. - /// - /// - /// The builder for the property being configured. - /// The comparison expression. - /// The associated hash code generator. - /// The snapshot expression. - public static PropertyBuilder ConfigureJsonConversionWithValueComparer( - this PropertyBuilder propertyBuilder, - Expression> equalsExpression, - Expression> hashCodeExpression, - Expression> snapshotExpression) - { - propertyBuilder.HasConversion( - d => JsonConvert.SerializeObject(d, Formatting.None), - s => JsonConvert.DeserializeObject(s) - ) - .Metadata.SetValueComparer(new ValueComparer( - equalsExpression, - hashCodeExpression, - snapshotExpression)); - - return propertyBuilder; - } - } -} \ No newline at end of file From 8621591f4c0e13cbb91292ead13a97ecc83480ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 09:56:39 +0300 Subject: [PATCH 26/39] Add Volo.Abp.Account.Application reference to the Web.Unified.csproj. --- .../MyCompanyName.MyProjectName.Web.Unified.csproj | 1 + .../MyProjectNameWebUnifiedModule.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj index dd930afbb0..1099836b9c 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj @@ -19,6 +19,7 @@ + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs index 2f5b250e02..8390297a9f 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyProjectNameWebUnifiedModule.cs @@ -8,6 +8,7 @@ using MyCompanyName.MyProjectName.Web; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp; +using Volo.Abp.Account; using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; @@ -42,6 +43,7 @@ namespace MyCompanyName.MyProjectName typeof(AbpAuditLoggingEntityFrameworkCoreModule), typeof(AbpAutofacModule), typeof(AbpAccountWebModule), + typeof(AbpAccountApplicationModule), typeof(AbpEntityFrameworkCoreSqlServerModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), From 474fc95824405143e02e84d696c5654b0c26bbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 10:02:02 +0300 Subject: [PATCH 27/39] Resolved #2579: Use Contextualized object mapper for the module template. --- .../MyProjectNameAppService.cs | 1 + .../MyProjectNameApplicationModule.cs | 4 +++- .../MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs | 1 + .../Pages/MyProjectNamePageModel.cs | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameAppService.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameAppService.cs index 31bfbeb0de..71bb06e43f 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameAppService.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameAppService.cs @@ -8,6 +8,7 @@ namespace MyCompanyName.MyProjectName protected MyProjectNameAppService() { LocalizationResource = typeof(MyProjectNameResource); + ObjectMapperContext = typeof(MyProjectNameApplicationModule); } } } diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs index bb0e72e856..457c1bc51b 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyProjectNameApplicationModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.AutoMapper; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; using Volo.Abp.Application; @@ -14,6 +15,7 @@ namespace MyCompanyName.MyProjectName { public override void ConfigureServices(ServiceConfigurationContext context) { + context.Services.AddAutoMapperObjectMapper(); Configure(options => { options.AddMaps(validate: true); diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index 213161e296..8e378a6bc2 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -42,6 +42,7 @@ namespace MyCompanyName.MyProjectName.Web options.FileSets.AddEmbedded("MyCompanyName.MyProjectName.Web"); }); + context.Services.AddAutoMapperObjectMapper(); Configure(options => { options.AddMaps(validate: true); diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectNamePageModel.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectNamePageModel.cs index d0b9c04a45..e1470049ac 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectNamePageModel.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectNamePageModel.cs @@ -10,6 +10,7 @@ namespace MyCompanyName.MyProjectName.Web.Pages protected MyProjectNamePageModel() { LocalizationResourceType = typeof(MyProjectNameResource); + ObjectMapperContext = typeof(MyProjectNameWebModule); } } } \ No newline at end of file From 13ac954cf7a3a18c9cd86159f477e2573491bbd2 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Wed, 8 Jan 2020 11:20:53 +0300 Subject: [PATCH 28/39] Fix PostgreSQL document --- docs/en/Entity-Framework-Core-PostgreSQL.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/Entity-Framework-Core-PostgreSQL.md b/docs/en/Entity-Framework-Core-PostgreSQL.md index 85ce3197f2..328ca4d416 100644 --- a/docs/en/Entity-Framework-Core-PostgreSQL.md +++ b/docs/en/Entity-Framework-Core-PostgreSQL.md @@ -12,12 +12,12 @@ Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFra ## UsePostgreSql() -Find `UseSqlServer()` calls in your solution, replace with `UsePostgreSql()`. Check the following files: +Find `UseSqlServer()` call in *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project and replace with `UsePostgreSql()`. -* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. -* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. -> Depending on your solution structure, you may find more code files need to be changed. +Find `UseSqlServer()` call in *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project and replace with `UseNpgsql()`. + +> Depending on your solution structure, you may find more `UseSqlServer()` calls that needs to be changed. ## Change the Connection Strings @@ -38,4 +38,4 @@ Run the `.DbMigrator` project to create the database and seed the initial data. ## Run the Application -It is ready. Just run the application and enjoy coding. \ No newline at end of file +It is ready. Just run the application and enjoy coding. From e04aea53b053110a6432254c0bae3a718d41e3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 13:55:12 +0300 Subject: [PATCH 29/39] Inherit from the MyProjectNamePageModel --- .../Pages/MyProjectName/Index.cshtml.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/Index.cshtml.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/Index.cshtml.cs index a9c43a1c88..bb6a7a4da8 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/Index.cshtml.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/Index.cshtml.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - namespace MyCompanyName.MyProjectName.Web.Pages.MyProjectName { - public class IndexModel : PageModel + public class IndexModel : MyProjectNamePageModel { public void OnGet() { From f42c452991092537f8dbe34a8677ec009ca5232b Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Wed, 8 Jan 2020 15:03:28 +0300 Subject: [PATCH 30/39] resolved: MarkdownDocumentToHtmlConverter miscatches the links resolved https://github.com/abpframework/abp/issues/2582 --- .../Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs index 81ce5ab1f7..5d7282a7e6 100644 --- a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs +++ b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs @@ -24,7 +24,7 @@ namespace Volo.Docs.Markdown } private const string MdLinkFormat = "[{0}]({1}{2}/{3}/{4}{5}/{6})"; - private const string MarkdownLinkRegExp = @"\[(.*?)\]\((.*?\.md)\)"; + private const string MarkdownLinkRegExp = @"\[(.*?)\]\((.*?)\)"; private const string AnchorLinkRegExp = @"]+href=\""(.*?)\""[^>]*>(.*)?"; public virtual string Convert(ProjectDto project, DocumentWithDetailsDto document, string version, @@ -56,7 +56,7 @@ namespace Volo.Docs.Markdown var normalized = Regex.Replace(content, MarkdownLinkRegExp, delegate (Match match) { var link = match.Groups[2].Value; - if (UrlHelper.IsExternalLink(link)) + if (UrlHelper.IsExternalLink(link) || !link.EndsWith(".md")) { return match.Value; } From 748efaf92b6a991a5069c9f7de74eee0a2f2316d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 15:11:41 +0300 Subject: [PATCH 31/39] Fix document extraproperties --- docs/en/Modules/Docs.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/en/Modules/Docs.md b/docs/en/Modules/Docs.md index dbafe910da..6859caa7fa 100644 --- a/docs/en/Modules/Docs.md +++ b/docs/en/Modules/Docs.md @@ -316,7 +316,7 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen - ExtraProperties: ```json - {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/en/","GitHubAccessToken":"***"} + {"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"} ``` Note that `GitHubAccessToken` is masked with `***`. It's a private token that you must get it from GitHub. See https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ @@ -328,7 +328,7 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table: ```mssql -INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/en/","GitHubAccessToken":"***"}', N'/', N'master') +INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs","GitHubAccessToken":"***"}', N'/', N'master') ``` Be aware that `GitHubAccessToken` is masked. It's a private token and you must get your own token and replace the `***` string. @@ -354,10 +354,10 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen - ExtraProperties: ```json - {"Path":"C:\\Github\\abp\\docs\\en"} + {"Path":"C:\\Github\\abp\\docs"} ``` - Note that `Path` must be replaced with your local docs directory. You can fetch the ABP Framework's documents from https://github.com/abpframework/abp/tree/master/docs/en and copy to the directory `C:\\Github\\abp\\docs\\en` to get it work. + Note that `Path` must be replaced with your local docs directory. You can fetch the ABP Framework's documents from https://github.com/abpframework/abp/tree/master/docs and copy to the directory `C:\\Github\\abp\\docs` to get it work. - MainWebsiteUrl: `/` @@ -366,11 +366,9 @@ You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documen For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table: ```mssql -INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs\\en"}', N'/', NULL) +INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs"}', N'/', NULL) ``` - - Add one of the sample projects above and run the application. In the menu you will see `Documents` link, click the menu link to open the documents page. So far, we have created a new application from abp.io website and made it up and ready for Docs module. From 714ab6fa2705bf9be91f2aaffff665768de1aeb3 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 8 Jan 2020 21:12:17 +0800 Subject: [PATCH 32/39] Remove the angular folder on module project creation with --no-ui option Resolve #2585 --- .../Cli/ProjectBuilding/Building/Steps/RemoveFolderStep.cs | 5 +++-- .../ProjectBuilding/Templates/Module/ModuleTemplateBase.cs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveFolderStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveFolderStep.cs index ddf7010dd7..636175f0b1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveFolderStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveFolderStep.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps { @@ -14,7 +15,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps public override void Execute(ProjectBuildContext context) { //Remove the folder content - var folderPathWithSlash = _folderPath + "/"; + var folderPathWithSlash = _folderPath.EnsureEndsWith('/'); context.Files.RemoveAll(file => file.Name.StartsWith(folderPathWithSlash)); //Remove the folder diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs index 09b75ac0e2..3faf920ab0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs @@ -43,6 +43,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Module "MyCompanyName.MyProjectName.Web.Unified", projectFolderPath: "/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified" )); + + steps.Add(new RemoveFolderStep("/angular")); } private void RandomizeSslPorts(ProjectBuildContext context, List steps) From 0060f6ef730de92b8b18230369c011a64d90b693 Mon Sep 17 00:00:00 2001 From: olicooper Date: Wed, 8 Jan 2020 16:10:38 +0000 Subject: [PATCH 33/39] Grammatical issue with Identity en localization --- .../Volo/Abp/Identity/Localization/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json index e7a038fd57..886594ac84 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json @@ -7,7 +7,7 @@ "UserName": "User name", "EmailAddress": "Email address", "PhoneNumber": "Phone number", - "UserInformations": "User informations", + "UserInformations": "User information", "DisplayName:IsDefault": "Default", "DisplayName:IsStatic": "Static", "DisplayName:IsPublic": "Public", @@ -99,4 +99,4 @@ "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Whether the username can be updated by the user.", "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user." } -} \ No newline at end of file +} From cb1e0e78dea5042179b6da02af5ad569950ce69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 20:33:16 +0300 Subject: [PATCH 34/39] Add AbpDbConnectionOptions part to the Connection-Strings document. --- docs/en/Connection-Strings.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/en/Connection-Strings.md b/docs/en/Connection-Strings.md index 57a156ff7b..eb7aae3a21 100644 --- a/docs/en/Connection-Strings.md +++ b/docs/en/Connection-Strings.md @@ -35,6 +35,21 @@ This configuration defines three different connection strings: [Pre-built application modules](Modules/Index.md) define constants for the connection string names. For example, the IdentityServer module defines a ` ConnectionStringName ` constant in the ` AbpIdentityServerDbProperties ` class (located in the ` Volo.Abp.IdentityServer ` namespace). Other modules similarly define constants, so you can investigate the connection string name. +### AbpDbConnectionOptions + +ABP actually uses the `AbpDbConnectionOptions` to get the connection strings. If you set the connection strings as explained above, `AbpDbConnectionOptions` is automatically filled. However, you can set or override the connection strings using [the options pattern](Options.md). You can configure the `AbpDbConnectionOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) as shown below: + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.ConnectionStrings.Default = "..."; + options.ConnectionStrings["AbpPermissionManagement"] = "..."; + }); +} +```` + ## Set the Connection String Name A module typically has a unique connection string name associated to its `DbContext` class using the `ConnectionStringName` attribute. Example: From 0680f1f0beb0f75dfb3cfbe08d93177b1b79e561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 21:04:09 +0300 Subject: [PATCH 35/39] Added Options document --- docs/en/Options.md | 71 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/docs/en/Options.md b/docs/en/Options.md index cead1aea61..5c21b9326d 100644 --- a/docs/en/Options.md +++ b/docs/en/Options.md @@ -1,4 +1,73 @@ # Options -TODO! +Microsoft has introduced [the options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) that is used to configure a group of settings used by the framework services. This pattern is implemented by the [Microsoft.Extensions.Options](https://www.nuget.org/packages/Microsoft.Extensions.Options) NuGet package, so it is usable by any type of applications in addition to ASP.NET Core based applications. +ABP framework follows this option pattern and defines options classes to configure the framework and the modules (they are explained in the documents of the related feature). + +Since [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) explains the pattern in detail, no reason to repeat all. However, ABP adds a few more features and they will be explained here. + +## Configure Options + +You typically configure options in the `ConfigureServices` of the `Startup` class. However, since ABP framework provides a modular infrastructure, you configure options in the `ConfigureServices` of your [module](Module-Development-Basics.md). Example: + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + context.Services.Configure(options => + { + options.IsEnabled = false; + }); +} +```` + +* `AbpAuditingOptions` is a simple class defines some properties like `IsEnabled` used here. +* `AbpModule` base class defines `Configure` method to make the code simpler. So, instead of `context.Services.Configure<...>`, you can directly use the `Configure<...>` shortcut method. + +If you are developing a reusable module, you may need to define an options class to allow developers to configure your module. In this case, define a plain options class as shown below: + +````csharp +public class MyOptions +{ + public int Value1 { get; set; } + public bool Value2 { get; set; } +} +```` + +Then developers can configure your options just like the `AbpAuditingOptions` example above: + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.Value1 = 42; + options.Value2 = true; + }); +} +```` + +* In this example, used the shortcut `Configure<...>` method. + +### Get the Option Value + +Whenever you need to get the value of an option, [inject](Dependency-Injection.md) the `IOptions` service into your class and use its `.Value` property. Example: + +````csharp +public class MyService : ITransientDependency +{ + private readonly MyOptions _options; + + public MyService(IOptions options) + { + _options = options.Value; //Notice the options.Value usage! + } + + public void DoIt() + { + var v1 = _options.Value1; + var v2 = _options.Value2; + } +} +```` + +Read [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) for all details of the options pattern. \ No newline at end of file From f654c0f7a19900ff0a44dbfe52d798209b24927c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 22:05:26 +0300 Subject: [PATCH 36/39] Added Pre Configure section --- docs/en/Options.md | 47 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/docs/en/Options.md b/docs/en/Options.md index 5c21b9326d..20168bdbe3 100644 --- a/docs/en/Options.md +++ b/docs/en/Options.md @@ -70,4 +70,49 @@ public class MyService : ITransientDependency } ```` -Read [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) for all details of the options pattern. \ No newline at end of file +Read [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) for all details of the options pattern. + +## Pre Configure + +One restriction of the options pattern is that you can only resolve (inject) the `IOptions` and get the option values when the dependency injection configuration completes (that means the `ConfigureServices` methods of all modules complete). + +If you are developing a module, you may need to allow developers to set some options and use these options in the dependency injection registration phase. You may need to configure other services or change the dependency injection registration code based on these option values. + +For such cases, ABP introduces the `PreConfigure` and the `ExecutePreConfiguredActions` extension methods for the `IServiceCollection`. The pattern works as explained below. + +1. Define a plan option class in your module. Example: + +````csharp +public class MyPreOptions +{ + public bool MyValue { get; set; } +} +```` + +Then any [module class](Module-Development-Basics.md) depends on your module can use the `PreConfigure` method in its `PreConfigureServices` method. Example: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.MyValue = true; + }); +} +```` + +> Multiple modules can pre-configure the options and override the option values based on their dependency order. + +Finally, your module can execute the `ExecutePreConfiguredActions` method in its `ConfigureServices` method to get the configured option values. Example: + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var options = context.Services.ExecutePreConfiguredActions(); + if (options.MyValue) + { + //... + } +} +```` + From f0ae27a7141fcdbbb34ea8b72421355f3b19b0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 8 Jan 2020 22:06:39 +0300 Subject: [PATCH 37/39] Add Options menu --- docs/en/docs-nav.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index e167d50c47..5667631c61 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -60,6 +60,10 @@ "text": "Configuration", "path": "Configuration.md" }, + { + "text": "Options", + "path": "Options.md" + }, { "text": "Dependency Injection", "path": "Dependency-Injection.md", From 201f08856482930649f1bbc406e7088db7a76183 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 9 Jan 2020 09:43:27 +0800 Subject: [PATCH 38/39] Update Virtual-File-System.md --- docs/en/Virtual-File-System.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/Virtual-File-System.md b/docs/en/Virtual-File-System.md index d13bbf491b..f2f85049bd 100644 --- a/docs/en/Virtual-File-System.md +++ b/docs/en/Virtual-File-System.md @@ -38,7 +38,8 @@ If you want to add multiple files, this can be tedious. Alternatively, you can d ````C# - + + ```` From 90dea581b685314de487dfe19120c6376e655f57 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 9 Jan 2020 09:43:57 +0800 Subject: [PATCH 39/39] Update Virtual-File-System.md --- docs/zh-Hans/Virtual-File-System.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/zh-Hans/Virtual-File-System.md b/docs/zh-Hans/Virtual-File-System.md index 49c97c8162..97cf22c73e 100644 --- a/docs/zh-Hans/Virtual-File-System.md +++ b/docs/zh-Hans/Virtual-File-System.md @@ -39,7 +39,8 @@ namespace MyCompany.MyProject ````C# - + + ````