Browse Source

Merge pull request #13881 from abpframework/Dynamic-Features

Dynamic Features.
pull/14001/head
liangshiwei 4 years ago
committed by GitHub
parent
commit
2745ee6c4f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  2. 10
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs
  3. 10
      framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs
  4. 2
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs
  5. 13
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs
  6. 120
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs
  7. 12
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs
  8. 16
      framework/src/Volo.Abp.Features/Volo/Abp/Features/ICanCreateChildFeature.cs
  9. 13
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IDynamicFeatureDefinitionStore.cs
  10. 9
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs
  11. 13
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IStaticFeatureDefinitionStore.cs
  12. 33
      framework/src/Volo.Abp.Features/Volo/Abp/Features/NullDynamicFeatureDefinitionStore.cs
  13. 121
      framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs
  14. 11
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs
  15. 47
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs
  16. 21
      framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs
  17. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs
  18. 56
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs
  19. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs
  20. 1
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj
  21. 30
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs
  22. 16
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureDefinitionRecordConsts.cs
  23. 8
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecordConsts.cs
  24. 0
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueConsts.cs
  25. 0
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/IValueValidatorFactory.cs
  26. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/NewtonsoftStringValueTypeJsonConverter.cs
  27. 0
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs
  28. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs
  29. 0
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorFactory.cs
  30. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs
  31. 8
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/ValueValidatorFactoryOptions.cs
  32. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj
  33. 138
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs
  34. 171
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DynamicFeatureDefinitionStore.cs
  35. 119
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DynamicFeatureDefinitionStoreInMemoryCache.cs
  36. 205
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs
  37. 102
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionSerializer.cs
  38. 85
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs
  39. 10
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs
  40. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs
  41. 6
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs
  42. 26
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IDynamicFeatureDefinitionStoreInMemoryCache.cs
  43. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureDefinitionRecordRepository.cs
  44. 15
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureDefinitionSerializer.cs
  45. 9
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureGroupDefinitionRecordRepository.cs
  46. 8
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IStaticFeatureSaver.cs
  47. 294
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureSaver.cs
  48. 25
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StringValueTypeSerializer.cs
  49. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs
  50. 27
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureDefinitionRecordRepository.cs
  51. 16
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureGroupDefinitionRecordRepository.cs
  52. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs
  53. 36
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs
  54. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs
  55. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs
  56. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs
  57. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs
  58. 10
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs
  59. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs
  60. 30
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureDefinitionRecordRepository.cs
  61. 16
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureGroupDefinitionRecordRepository.cs
  62. 4
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs
  63. 9
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs
  64. 2
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs
  65. 87
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureDefinitionSerializer_Tests.cs
  66. 52
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/StringValueTypeSerializer_Tests.cs
  67. 13
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs
  68. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs
  69. 19
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs
  70. 9
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs
  71. 60
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs
  72. 27
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs
  73. 13
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/StaticPermissionSaver_Tests.cs
  74. 1
      modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj
  75. 6
      modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/EFCorePermissionDefinitionRecordRepository_Tests.cs
  76. 1
      modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj
  77. 9
      modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs
  78. 91
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220909024045_Initial.Designer.cs
  79. 59
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220909024045_Initial.cs
  80. 89
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs
  81. 91
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220909024045_Initial.Designer.cs
  82. 59
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220909024045_Initial.cs
  83. 89
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs
  84. 91
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220909024056_Initial.Designer.cs
  85. 59
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220909024056_Initial.cs
  86. 89
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs
  87. 158
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220909023853_Initial.Designer.cs
  88. 88
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220909023853_Initial.cs
  89. 89
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs
  90. 104
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220909024101_Initial.Designer.cs
  91. 86
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220909024101_Initial.cs
  92. 89
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs
  93. 91
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220909024110_Initial.Designer.cs
  94. 59
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220909024110_Initial.cs
  95. 89
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs
  96. 91
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220909024111_Initial.Designer.cs
  97. 59
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220909024111_Initial.cs
  98. 89
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -308,7 +308,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{
var result = new ApplicationFeatureConfigurationDto();
foreach (var featureDefinition in _featureDefinitionManager.GetAll())
foreach (var featureDefinition in await _featureDefinitionManager.GetAllAsync())
{
if (!featureDefinition.IsVisibleToClients)
{

10
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs

@ -1,4 +1,5 @@
using Volo.Abp.Collections;
using System.Collections.Generic;
using Volo.Abp.Collections;
namespace Volo.Abp.Authorization.Permissions;
@ -8,9 +9,16 @@ public class AbpPermissionOptions
public ITypeList<IPermissionValueProvider> ValueProviders { get; }
public HashSet<string> DeletedPermissions { get; }
public HashSet<string> DeletedPermissionGroups { get; }
public AbpPermissionOptions()
{
DefinitionProviders = new TypeList<IPermissionDefinitionProvider>();
ValueProviders = new TypeList<IPermissionValueProvider>();
DeletedPermissions = new HashSet<string>();
DeletedPermissionGroups = new HashSet<string>();
}
}

10
framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureOptions.cs

@ -1,4 +1,5 @@
using Volo.Abp.Collections;
using System.Collections.Generic;
using Volo.Abp.Collections;
namespace Volo.Abp.Features;
@ -8,9 +9,16 @@ public class AbpFeatureOptions
public ITypeList<IFeatureValueProvider> ValueProviders { get; }
public HashSet<string> DeletedFeatures { get; }
public HashSet<string> DeletedFeatureGroups { get; }
public AbpFeatureOptions()
{
DefinitionProviders = new TypeList<IFeatureDefinitionProvider>();
ValueProviders = new TypeList<IFeatureValueProvider>();
DeletedFeatures = new HashSet<string>();
DeletedFeatureGroups = new HashSet<string>();
}
}

2
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs

@ -37,7 +37,7 @@ public class FeatureChecker : FeatureCheckerBase
public override async Task<string> GetOrNullAsync(string name)
{
var featureDefinition = FeatureDefinitionManager.Get(name);
var featureDefinition = await FeatureDefinitionManager.GetAsync(name);
var providers = Enumerable
.Reverse(Providers);

13
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs

@ -7,7 +7,7 @@ using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features;
public class FeatureDefinition
public class FeatureDefinition : ICanCreateChildFeature
{
/// <summary>
/// Unique name of the feature.
@ -178,6 +178,17 @@ public class FeatureDefinition
_children.Remove(featureToRemove);
}
public FeatureDefinition CreateChildFeature(string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true,
bool isAvailableToHost = true)
{
return this.CreateChild(name, defaultValue, displayName, description, valueType, isVisibleToClients, isAvailableToHost);
}
public override string ToString()
{
return $"[{nameof(FeatureDefinition)}: {Name}]";

120
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs

@ -2,120 +2,70 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features;
public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency
{
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions;
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions;
protected AbpFeatureOptions Options { get; }
private readonly IServiceScopeFactory _serviceScopeFactory;
protected IStaticFeatureDefinitionStore StaticStore;
protected IDynamicFeatureDefinitionStore DynamicStore;
public FeatureDefinitionManager(
IOptions<AbpFeatureOptions> options,
IServiceScopeFactory serviceScopeFactory)
IStaticFeatureDefinitionStore staticStore,
IDynamicFeatureDefinitionStore dynamicStore)
{
_serviceScopeFactory = serviceScopeFactory;
Options = options.Value;
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>(
CreateFeatureDefinitions,
isThreadSafe: true
);
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>(
CreateFeatureGroupDefinitions,
isThreadSafe: true
);
StaticStore = staticStore;
DynamicStore = dynamicStore;
}
public virtual FeatureDefinition Get(string name)
public virtual async Task<FeatureDefinition> GetAsync(string name)
{
Check.NotNull(name, nameof(name));
var feature = GetOrNull(name);
if (feature == null)
var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined feature: " + name);
}
return feature;
}
public virtual IReadOnlyList<FeatureDefinition> GetAll()
{
return FeatureDefinitions.Values.ToImmutableList();
}
public virtual FeatureDefinition GetOrNull(string name)
{
return FeatureDefinitions.GetOrDefault(name);
}
public IReadOnlyList<FeatureGroupDefinition> GetGroups()
{
return FeatureGroupDefinitions.Values.ToImmutableList();
return permission;
}
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
public virtual async Task<FeatureDefinition> GetOrNullAsync(string name)
{
var features = new Dictionary<string, FeatureDefinition>();
foreach (var groupDefinition in FeatureGroupDefinitions.Values)
{
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
Check.NotNull(name, nameof(name));
return features;
return await StaticStore.GetOrNullAsync(name) ??
await DynamicStore.GetOrNullAsync(name);
}
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
public virtual async Task<IReadOnlyList<FeatureDefinition>> GetAllAsync()
{
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
var staticFeatures = await StaticStore.GetFeaturesAsync();
var staticFeatureNames = staticFeatures
.Select(p => p.Name)
.ToImmutableHashSet();
features[feature.Name] = feature;
var dynamicFeatures = await DynamicStore.GetFeaturesAsync();
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
/* We prefer static features over dynamics */
return staticFeatures.Concat(
dynamicFeatures.Where(d => !staticFeatureNames.Contains(d.Name))
).ToImmutableList();
}
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions()
public virtual async Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
{
var context = new FeatureDefinitionContext();
var staticGroups = await StaticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
using (var scope = _serviceScopeFactory.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(context);
}
}
var dynamicGroups = await DynamicStore.GetGroupsAsync();
return context.Groups;
/* We prefer static groups over dynamics */
return staticGroups.Concat(
dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
).ToImmutableList();
}
}

12
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs

@ -5,7 +5,7 @@ using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features;
public class FeatureGroupDefinition
public class FeatureGroupDefinition : ICanCreateChildFeature
{
/// <summary>
/// Unique name of the group.
@ -69,6 +69,16 @@ public class FeatureGroupDefinition
return feature;
}
public FeatureDefinition CreateChildFeature(string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true,
bool isAvailableToHost = true)
{
return AddFeature(name, defaultValue, displayName, description, valueType, isVisibleToClients);
}
public virtual List<FeatureDefinition> GetFeaturesWithChildren()
{
var features = new List<FeatureDefinition>();

16
framework/src/Volo.Abp.Features/Volo/Abp/Features/ICanCreateChildFeature.cs

@ -0,0 +1,16 @@
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features;
public interface ICanCreateChildFeature
{
FeatureDefinition CreateChildFeature(
string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true,
bool isAvailableToHost = true);
}

13
framework/src/Volo.Abp.Features/Volo/Abp/Features/IDynamicFeatureDefinitionStore.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Features;
public interface IDynamicFeatureDefinitionStore
{
Task<FeatureDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync();
Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync();
}

9
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Features;
@ -6,11 +7,11 @@ namespace Volo.Abp.Features;
public interface IFeatureDefinitionManager
{
[NotNull]
FeatureDefinition Get([NotNull] string name);
Task<FeatureDefinition> GetAsync([NotNull] string name);
IReadOnlyList<FeatureDefinition> GetAll();
Task<IReadOnlyList<FeatureDefinition>> GetAllAsync();
FeatureDefinition GetOrNull(string name);
Task<FeatureDefinition> GetOrNullAsync(string name);
IReadOnlyList<FeatureGroupDefinition> GetGroups();
Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync();
}

13
framework/src/Volo.Abp.Features/Volo/Abp/Features/IStaticFeatureDefinitionStore.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Features;
public interface IStaticFeatureDefinitionStore
{
Task<FeatureDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync();
Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync();
}

33
framework/src/Volo.Abp.Features/Volo/Abp/Features/NullDynamicFeatureDefinitionStore.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features;
public class NullDynamicFeatureDefinitionStore : IDynamicFeatureDefinitionStore, ISingletonDependency
{
private static readonly Task<FeatureDefinition> CachedFeatureResult = Task.FromResult((FeatureDefinition)null);
private static readonly Task<IReadOnlyList<FeatureDefinition>> CachedFeaturesResult =
Task.FromResult((IReadOnlyList<FeatureDefinition>)Array.Empty<FeatureDefinition>().ToImmutableList());
private static readonly Task<IReadOnlyList<FeatureGroupDefinition>> CachedGroupsResult =
Task.FromResult((IReadOnlyList<FeatureGroupDefinition>)Array.Empty<FeatureGroupDefinition>().ToImmutableList());
public Task<FeatureDefinition> GetOrNullAsync(string name)
{
return CachedFeatureResult;
}
public Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync()
{
return CachedFeaturesResult;
}
public Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
{
return CachedGroupsResult;
}
}

121
framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features;
public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingletonDependency
{
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions;
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions;
protected AbpFeatureOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
public StaticFeatureDefinitionStore(
IOptions<AbpFeatureOptions> options,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>(
CreateFeatureDefinitions,
isThreadSafe: true
);
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>(
CreateFeatureGroupDefinitions,
isThreadSafe: true
);
}
public virtual async Task<FeatureDefinition> GetAsync(string name)
{
Check.NotNull(name, nameof(name));
var feature = await GetOrNullAsync(name);
if (feature == null)
{
throw new AbpException("Undefined feature: " + name);
}
return feature;
}
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
{
var features = new Dictionary<string, FeatureDefinition>();
foreach (var groupDefinition in FeatureGroupDefinitions.Values)
{
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
return features;
}
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
{
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
features[feature.Name] = feature;
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
}
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions()
{
var context = new FeatureDefinitionContext();
using (var scope = _serviceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(context);
}
}
return context.Groups;
}
public virtual Task<FeatureDefinition> GetOrNullAsync(string name)
{
return Task.FromResult(FeatureDefinitions.GetOrDefault(name));
}
public virtual Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync()
{
return Task.FromResult<IReadOnlyList<FeatureDefinition>>(FeatureDefinitions.Values.ToList());
}
public virtual Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
{
return Task.FromResult<IReadOnlyList<FeatureGroupDefinition>>(FeatureGroupDefinitions.Values.ToList());
}
}

11
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs

@ -15,6 +15,11 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi
public virtual string Serialize(ILocalizableString localizableString)
{
if (localizableString == null)
{
return null;
}
if (localizableString is LocalizableString realLocalizableString)
{
return $"L:{realLocalizableString.ResourceName},{realLocalizableString.Name}";
@ -36,7 +41,7 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi
{
return new FixedLocalizableString(value);
}
var type = value[0];
switch (type)
{
@ -48,7 +53,7 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi
{
throw new AbpException("Invalid LocalizableString value: " + value);
}
var resourceName = value.Substring(2, commaPosition - 2);
var name = value.Substring(commaPosition + 1);
if (name.IsNullOrWhiteSpace())
@ -61,4 +66,4 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi
return new FixedLocalizableString(value);
}
}
}
}

47
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs

@ -0,0 +1,47 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Authorization.Permissions;
using Xunit;
namespace Volo.Abp.Authorization;
public class StaticPermissionDefinitionStore_Tests : AuthorizationTestBase
{
private readonly IStaticPermissionDefinitionStore _store;
public StaticPermissionDefinitionStore_Tests()
{
_store = GetRequiredService<IStaticPermissionDefinitionStore>();
}
[Fact]
public async Task GetOrNullAsync()
{
var permission = await _store.GetOrNullAsync("MyPermission1");
permission.ShouldNotBeNull();
permission.Name.ShouldBe("MyPermission1");
permission.StateCheckers.ShouldContain(x => x.GetType() == typeof(TestRequireEditionPermissionSimpleStateChecker));
permission = await _store.GetOrNullAsync("NotExists");
permission.ShouldBeNull();
}
[Fact]
public async Task GetPermissionsAsync()
{
var permissions = await _store.GetPermissionsAsync();
permissions.ShouldContain(x => x.Name == "MyAuthorizedService1");
permissions.ShouldContain(x => x.Name == "MyPermission1");
permissions.ShouldContain(x => x.Name == "MyPermission2");
permissions.ShouldContain(x => x.Name == "MyPermission3");
permissions.ShouldContain(x => x.Name == "MyPermission4");
permissions.ShouldContain(x => x.Name == "MyPermission5");
}
[Fact]
public async Task GetGroupsAsync()
{
var groups = await _store.GetGroupsAsync();
groups.ShouldNotContain(x => x.Name == "TestGetGroup");
}
}

21
framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureDefinitionManager_Tests.cs

@ -1,4 +1,5 @@
using Shouldly;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.Features;
@ -13,22 +14,22 @@ public class FeatureDefinitionManager_Tests : FeatureTestBase
}
[Fact]
public void Should_Get_Defined_Features()
public async Task Should_Get_Defined_Features()
{
_featureDefinitionManager.GetOrNull("BooleanTestFeature1").ShouldNotBeNull();
_featureDefinitionManager.Get("BooleanTestFeature1").Name.ShouldBe("BooleanTestFeature1");
await _featureDefinitionManager.GetOrNullAsync("BooleanTestFeature1").ShouldNotBeNull();
(await _featureDefinitionManager.GetAsync("BooleanTestFeature1")).Name.ShouldBe("BooleanTestFeature1");
_featureDefinitionManager.GetOrNull("IntegerTestFeature1").ShouldNotBeNull();
_featureDefinitionManager.Get("IntegerTestFeature1").Name.ShouldBe("IntegerTestFeature1");
await _featureDefinitionManager.GetOrNullAsync("IntegerTestFeature1").ShouldNotBeNull();
(await _featureDefinitionManager.GetAsync("IntegerTestFeature1")).Name.ShouldBe("IntegerTestFeature1");
}
[Fact]
public void Should_Not_Get_Undefined_Features()
public async Task Should_Not_Get_Undefined_Features()
{
_featureDefinitionManager.GetOrNull("UndefinedFeature").ShouldBeNull();
Assert.Throws<AbpException>(() =>
(await _featureDefinitionManager.GetOrNullAsync("UndefinedFeature")).ShouldBeNull();
await Assert.ThrowsAsync<AbpException>(async () =>
{
_featureDefinitionManager.Get("UndefinedFeature");
await _featureDefinitionManager.GetAsync("UndefinedFeature");
});
}
}

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs

@ -1,11 +1,6 @@
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Application;
using Volo.Abp.Application;
using Volo.Abp.Authorization;
using Volo.Abp.FeatureManagement.JsonConverters;
using Volo.Abp.Json;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
@ -25,21 +20,5 @@ public class AbpFeatureManagementApplicationContractsModule : AbpModule
{
options.FileSets.AddEmbedded<AbpFeatureManagementApplicationContractsModule>();
});
var contractsOptionsActions = context.Services.GetPreConfigureActions<AbpFeatureManagementApplicationContractsOptions>();
Configure<AbpFeatureManagementApplicationContractsOptions>(options =>
{
contractsOptionsActions.Configure(options);
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{
options.Converters.Add<NewtonsoftStringValueTypeJsonConverter>();
});
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter(contractsOptionsActions.Configure()));
});
}
}

56
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs

@ -5,8 +5,8 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Features;
using Volo.Abp.Localization;
namespace Volo.Abp.FeatureManagement;
@ -35,14 +35,9 @@ public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppSer
Groups = new List<FeatureGroupDto>()
};
foreach (var group in FeatureDefinitionManager.GetGroups())
foreach (var group in await FeatureDefinitionManager.GetGroupsAsync())
{
var groupDto = new FeatureGroupDto
{
Name = group.Name,
DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
Features = new List<FeatureDto>()
};
var groupDto = CreateFeatureGroupDto(group);
foreach (var featureDefinition in group.GetFeaturesWithChildren())
{
@ -55,20 +50,7 @@ public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppSer
}
var feature = await FeatureManager.GetOrNullWithProviderAsync(featureDefinition.Name, providerName, providerKey);
groupDto.Features.Add(new FeatureDto
{
Name = featureDefinition.Name,
DisplayName = featureDefinition.DisplayName?.Localize(StringLocalizerFactory),
ValueType = featureDefinition.ValueType,
Description = featureDefinition.Description?.Localize(StringLocalizerFactory),
ParentName = featureDefinition.Parent?.Name,
Value = feature.Value,
Provider = new FeatureProviderDto
{
Name = feature.Provider?.Name,
Key = feature.Provider?.Key
}
});
groupDto.Features.Add(CreateFeatureDto(feature, featureDefinition));
}
SetFeatureDepth(groupDto.Features, providerName, providerKey);
@ -82,6 +64,36 @@ public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppSer
return result;
}
private FeatureGroupDto CreateFeatureGroupDto(FeatureGroupDefinition groupDefinition)
{
return new FeatureGroupDto
{
Name = groupDefinition.Name,
DisplayName = groupDefinition.DisplayName?.Localize(StringLocalizerFactory),
Features = new List<FeatureDto>()
};
}
private FeatureDto CreateFeatureDto(FeatureNameValueWithGrantedProvider featureNameValueWithGrantedProvider, FeatureDefinition featureDefinition)
{
return new FeatureDto
{
Name = featureDefinition.Name,
DisplayName = featureDefinition.DisplayName?.Localize(StringLocalizerFactory),
Description = featureDefinition.Description?.Localize(StringLocalizerFactory),
ValueType = featureDefinition.ValueType,
ParentName = featureDefinition.Parent?.Name,
Value = featureNameValueWithGrantedProvider.Value,
Provider = new FeatureProviderDto
{
Name = featureNameValueWithGrantedProvider.Provider?.Name,
Key = featureNameValueWithGrantedProvider.Provider?.Key
}
};
}
public virtual async Task UpdateAsync([NotNull] string providerName, string providerKey, UpdateFeaturesDto input)
{
await CheckProviderPolicy(providerName, providerKey);

4
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs

@ -50,9 +50,9 @@ public partial class FeatureManagementModal
ToggleValues = new Dictionary<string, bool>();
SelectionStringValues = new Dictionary<string, string>();
Groups = (await FeatureAppService.GetAsync(ProviderName, ProviderKey))?.Groups;
var result = await FeatureAppService.GetAsync(ProviderName, ProviderKey);
Groups ??= new List<FeatureGroupDto>();
Groups = result?.Groups ?? new List<FeatureGroupDto>();
if (Groups.Any())
{

1
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj

@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Json\Volo.Abp.Json.csproj" />
</ItemGroup>
<ItemGroup>

30
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs

@ -1,4 +1,11 @@
using Volo.Abp.FeatureManagement.Localization;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.FeatureManagement.JsonConverters;
using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Json;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
@ -9,8 +16,9 @@ using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpValidationModule)
)]
typeof(AbpValidationModule),
typeof(AbpJsonModule)
)]
public class AbpFeatureManagementDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
@ -33,5 +41,21 @@ public class AbpFeatureManagementDomainSharedModule : AbpModule
{
options.MapCodeNamespace("Volo.Abp.FeatureManagement", typeof(AbpFeatureManagementResource));
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{
options.Converters.Add<NewtonsoftStringValueTypeJsonConverter>();
});
var valueValidatorFactoryOptions = context.Services.GetPreConfigureActions<ValueValidatorFactoryOptions>();
Configure<ValueValidatorFactoryOptions>(options =>
{
valueValidatorFactoryOptions.Configure(options);
});
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.Add(new StringValueTypeJsonConverter(valueValidatorFactoryOptions.Configure()));
});
}
}

16
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureDefinitionRecordConsts.cs

@ -0,0 +1,16 @@
namespace Volo.Abp.FeatureManagement;
public static class FeatureDefinitionRecordConsts
{
public static int MaxNameLength { get; set; } = 128;
public static int MaxDisplayNameLength { get; set; } = 256;
public static int MaxDescriptionLength { get; set; } = 256;
public static int MaxDefaultValueLength { get; set; } = 256;
public static int MaxAllowedProvidersLength { get; set; } = 256;
public static int MaxValueTypeLength { get; set; } = 256;
}

8
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecordConsts.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.FeatureManagement;
public static class FeatureGroupDefinitionRecordConsts
{
public static int MaxNameLength { get; set; } = 128;
public static int MaxDisplayNameLength { get; set; } = 256;
}

0
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueConsts.cs

0
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/IValueValidatorFactory.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/IValueValidatorFactory.cs

4
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/NewtonsoftStringValueTypeJsonConverter.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/NewtonsoftStringValueTypeJsonConverter.cs

@ -14,9 +14,9 @@ public class NewtonsoftStringValueTypeJsonConverter : JsonConverter, ITransientD
{
public override bool CanWrite => false;
protected readonly AbpFeatureManagementApplicationContractsOptions Options;
protected readonly ValueValidatorFactoryOptions Options;
public NewtonsoftStringValueTypeJsonConverter(IOptions<AbpFeatureManagementApplicationContractsOptions> options)
public NewtonsoftStringValueTypeJsonConverter(IOptions<ValueValidatorFactoryOptions> options)
{
Options = options.Value;
}

0
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs

4
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs

@ -12,9 +12,9 @@ public class StringValueTypeJsonConverter : JsonConverter<IStringValueType>
private JsonSerializerOptions _writeJsonSerializerOptions;
protected readonly AbpFeatureManagementApplicationContractsOptions Options;
protected readonly ValueValidatorFactoryOptions Options;
public StringValueTypeJsonConverter(AbpFeatureManagementApplicationContractsOptions options)
public StringValueTypeJsonConverter(ValueValidatorFactoryOptions options)
{
Options = options;
}

0
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorFactory.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorFactory.cs

4
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs

@ -14,9 +14,9 @@ public class ValueValidatorJsonConverter : JsonConverter<IValueValidator>
private JsonSerializerOptions _writeJsonSerializerOptions;
protected readonly AbpFeatureManagementApplicationContractsOptions Options;
protected readonly ValueValidatorFactoryOptions Options;
public ValueValidatorJsonConverter(AbpFeatureManagementApplicationContractsOptions options)
public ValueValidatorJsonConverter(ValueValidatorFactoryOptions options)
{
Options = options;
}

8
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsOptions.cs → modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/ValueValidatorFactoryOptions.cs

@ -4,13 +4,13 @@ using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement;
public class AbpFeatureManagementApplicationContractsOptions
public class ValueValidatorFactoryOptions
{
public HashSet<IValueValidatorFactory> ValueValidatorFactory { get; }
public AbpFeatureManagementApplicationContractsOptions()
public ValueValidatorFactoryOptions()
{
ValueValidatorFactory = new HashSet<IValueValidatorFactory>
ValueValidatorFactory = new HashSet<IValueValidatorFactory>
{
new ValueValidatorFactory<AlwaysValidValueValidator>("NULL"),
new ValueValidatorFactory<BooleanValueValidator>("BOOLEAN"),

4
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo.Abp.FeatureManagement.Domain.csproj

@ -18,4 +18,8 @@
<ProjectReference Include="..\Volo.Abp.FeatureManagement.Domain.Shared\Volo.Abp.FeatureManagement.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Polly" Version="$(PollyPackageVersion)" />
</ItemGroup>
</Project>

138
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs

@ -1,8 +1,18 @@
using Volo.Abp.Caching;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Features;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.FeatureManagement;
@ -30,4 +40,130 @@ public class AbpFeatureManagementDomainModule : AbpModule
options.MapCodeNamespace("AbpFeatureManagement", typeof(AbpFeatureManagementResource));
});
}
private readonly CancellationTokenSource _cancellationTokenSource = new();
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicFeatures(context);
return Task.CompletedTask;
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
{
_cancellationTokenSource.Cancel();
return Task.CompletedTask;
}
private void InitializeDynamicFeatures(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider
.GetRequiredService<IOptions<FeatureManagementOptions>>()
.Value;
if (!options.SaveStaticFeaturesToDatabase && !options.IsDynamicFeatureStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
Task.Run(async () =>
{
using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticFeaturesToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicFeaturesAsync(options, scope);
}
}
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { }
});
}
private static async Task SaveStaticFeaturesToDatabaseAsync(
FeatureManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticFeaturesToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10))
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticFeatureSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpFeatureManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private static async Task PreCacheDynamicFeaturesAsync(FeatureManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicFeatureStoreEnabled)
{
return;
}
try
{
// Pre-cache features, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicFeatureDefinitionStore>()
.GetGroupsAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpFeatureManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicFeatures
}
}
}

171
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DynamicFeatureDefinitionStore.cs

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Features;
using Volo.Abp.Threading;
namespace Volo.Abp.FeatureManagement;
[Dependency(ReplaceServices = true)]
public class DynamicFeatureDefinitionStore : IDynamicFeatureDefinitionStore, ITransientDependency
{
protected IFeatureGroupDefinitionRecordRepository FeatureGroupRepository { get; }
protected IFeatureDefinitionRecordRepository FeatureRepository { get; }
protected IFeatureDefinitionSerializer FeatureDefinitionSerializer { get; }
protected IDynamicFeatureDefinitionStoreInMemoryCache StoreCache { get; }
protected IDistributedCache DistributedCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
public FeatureManagementOptions FeatureManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicFeatureDefinitionStore(
IFeatureGroupDefinitionRecordRepository featureGroupRepository,
IFeatureDefinitionRecordRepository featureRepository,
IFeatureDefinitionSerializer featureDefinitionSerializer,
IDynamicFeatureDefinitionStoreInMemoryCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<FeatureManagementOptions> featureManagementOptions,
IAbpDistributedLock distributedLock)
{
FeatureGroupRepository = featureGroupRepository;
FeatureRepository = featureRepository;
FeatureDefinitionSerializer = featureDefinitionSerializer;
StoreCache = storeCache;
DistributedCache = distributedCache;
DistributedLock = distributedLock;
FeatureManagementOptions = featureManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
public virtual async Task<FeatureDefinition> GetOrNullAsync(string name)
{
if (!FeatureManagementOptions.IsDynamicFeatureStoreEnabled)
{
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetFeatureOrNull(name);
}
}
public virtual async Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync()
{
if (!FeatureManagementOptions.IsDynamicFeatureStoreEnabled)
{
return Array.Empty<FeatureDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetFeatures().ToImmutableList();
}
}
public virtual async Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
{
if (!FeatureManagementOptions.IsDynamicFeatureStoreEnabled)
{
return Array.Empty<FeatureGroupDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetGroups().ToImmutableList();
}
}
protected virtual async Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30)
{
/* We get the latest feature with a small delay for optimization */
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
}
protected virtual async Task UpdateInMemoryStoreCache()
{
var featureGroupRecords = await FeatureGroupRepository.GetListAsync();
var featureRecords = await FeatureRepository.GetListAsync();
await StoreCache.FillAsync(featureGroupRecords, featureRecords);
}
protected virtual async Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = GetCommonStampCacheKey();
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
await using (var commonLockHandle = await DistributedLock
.TryAcquireAsync(GetCommonDistributedLockKey(), TimeSpan.FromMinutes(2)))
{
if (commonLockHandle == null)
{
/* This request will fail */
throw new AbpException(
"Could not acquire distributed lock for feature definition common stamp check!"
);
}
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
}
);
}
return stampInDistributedCache;
}
protected virtual string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryFeatureCacheStamp";
}
protected virtual string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpFeatureUpdateLock";
}
}

119
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DynamicFeatureDefinitionStoreInMemoryCache.cs

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Localization;
namespace Volo.Abp.FeatureManagement;
public class DynamicFeatureDefinitionStoreInMemoryCache:
IDynamicFeatureDefinitionStoreInMemoryCache,
ISingletonDependency
{
public string CacheStamp { get; set; }
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions { get; }
protected IDictionary<string, FeatureDefinition> FeatureDefinitions { get; }
protected StringValueTypeSerializer StateCheckerSerializer { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1);
public DateTime? LastCheckTime { get; set; }
public DynamicFeatureDefinitionStoreInMemoryCache(
StringValueTypeSerializer stateCheckerSerializer,
ILocalizableStringSerializer localizableStringSerializer)
{
StateCheckerSerializer = stateCheckerSerializer;
LocalizableStringSerializer = localizableStringSerializer;
FeatureGroupDefinitions = new Dictionary<string, FeatureGroupDefinition>();
FeatureDefinitions = new Dictionary<string, FeatureDefinition>();
}
public Task FillAsync(
List<FeatureGroupDefinitionRecord> featureGroupRecords,
List<FeatureDefinitionRecord> featureRecords)
{
FeatureGroupDefinitions.Clear();
FeatureDefinitions.Clear();
var context = new FeatureDefinitionContext();
foreach (var featureGroupRecord in featureGroupRecords)
{
var featureGroup = context.AddGroup(
featureGroupRecord.Name,
LocalizableStringSerializer.Deserialize(featureGroupRecord.DisplayName)
);
FeatureGroupDefinitions[featureGroup.Name] = featureGroup;
foreach (var property in featureGroupRecord.ExtraProperties)
{
featureGroup[property.Key] = property.Value;
}
var featureRecordsInThisGroup = featureRecords
.Where(p => p.GroupName == featureGroup.Name);
foreach (var featureRecord in featureRecordsInThisGroup.Where(x => x.ParentName == null))
{
AddFeatureRecursively(featureGroup, featureRecord, featureRecords);
}
}
return Task.CompletedTask;
}
public FeatureDefinition GetFeatureOrNull(string name)
{
return FeatureDefinitions.GetOrDefault(name);
}
public IReadOnlyList<FeatureDefinition> GetFeatures()
{
return FeatureDefinitions.Values.ToList();
}
public IReadOnlyList<FeatureGroupDefinition> GetGroups()
{
return FeatureGroupDefinitions.Values.ToList();
}
private void AddFeatureRecursively(ICanCreateChildFeature featureContainer,
FeatureDefinitionRecord featureRecord,
List<FeatureDefinitionRecord> allFeatureRecords)
{
var feature = featureContainer.CreateChildFeature(
featureRecord.Name,
featureRecord.DefaultValue,
LocalizableStringSerializer.Deserialize(featureRecord.DisplayName),
LocalizableStringSerializer.Deserialize(featureRecord.Description),
StateCheckerSerializer.Deserialize(featureRecord.ValueType),
featureRecord.IsVisibleToClients,
featureRecord.IsAvailableToHost
);
FeatureDefinitions[feature.Name] = feature;
if (!featureRecord.AllowedProviders.IsNullOrWhiteSpace())
{
feature.AllowedProviders.AddRange(featureRecord.AllowedProviders.Split(','));
}
foreach (var property in featureRecord.ExtraProperties)
{
feature[property.Key] = property.Value;
}
foreach (var subFeature in allFeatureRecords.Where(p => p.ParentName == featureRecord.Name))
{
AddFeatureRecursively(feature, subFeature, allFeatureRecords);
}
}
}

205
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs

@ -0,0 +1,205 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.FeatureManagement;
public class FeatureDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if feature definitions have changed (in StaticFeatureSaver.CalculateHash()).
*/
[JsonIgnore] //TODO: TODO: Use JSON modifier to ignore this property
public override Guid Id { get; protected set; }
public string GroupName { get; set; }
public string Name { get; set; }
public string ParentName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string DefaultValue { get; set; }
public bool IsVisibleToClients { get; set; }
public bool IsAvailableToHost { get; set; }
/// <summary>
/// Comma separated list of provider names.
/// </summary>
public string AllowedProviders { get; set; }
/// <summary>
/// Serialized string to store info about the ValueType.
/// </summary>
public string ValueType { get; set; } // ToggleStringValueType
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public FeatureDefinitionRecord()
{
IsVisibleToClients = true;
IsAvailableToHost = true;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public FeatureDefinitionRecord(
Guid id,
string groupName,
string name,
string parentName,
string displayName = null,
string description = null,
string defaultValue = null,
bool isVisibleToClients = true,
bool isAvailableToHost = true,
string allowedProviders = null,
string valueType = null)
: base(id)
{
GroupName = Check.NotNullOrWhiteSpace(groupName, nameof(groupName), FeatureDefinitionRecordConsts.MaxNameLength);
Name = Check.NotNullOrWhiteSpace(name, nameof(name), FeatureDefinitionRecordConsts.MaxNameLength);
ParentName = Check.Length(parentName, nameof(parentName), FeatureDefinitionRecordConsts.MaxNameLength);
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), FeatureDefinitionRecordConsts.MaxDisplayNameLength);
Description = Check.Length(description, nameof(description), FeatureDefinitionRecordConsts.MaxDescriptionLength);
DefaultValue = Check.NotNullOrWhiteSpace(defaultValue, nameof(defaultValue), FeatureDefinitionRecordConsts.MaxDefaultValueLength);
IsVisibleToClients = isVisibleToClients;
IsAvailableToHost = isAvailableToHost;
AllowedProviders = Check.Length(allowedProviders, nameof(allowedProviders), FeatureDefinitionRecordConsts.MaxAllowedProvidersLength);
ValueType = Check.NotNullOrWhiteSpace(valueType, nameof(valueType), FeatureDefinitionRecordConsts.MaxValueTypeLength);
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public bool HasSameData(FeatureDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
return false;
}
if (GroupName != otherRecord.GroupName)
{
return false;
}
if (ParentName != otherRecord.ParentName)
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
}
if (Description != otherRecord.Description)
{
return false;
}
if (DefaultValue != otherRecord.DefaultValue)
{
return false;
}
if (IsVisibleToClients != otherRecord.IsVisibleToClients)
{
return false;
}
if (IsAvailableToHost != otherRecord.IsAvailableToHost)
{
return false;
}
if (AllowedProviders != otherRecord.AllowedProviders)
{
return false;
}
if (ValueType != otherRecord.ValueType)
{
return false;
}
if (!this.HasSameExtraProperties(otherRecord))
{
return false;
}
return true;
}
public void Patch(FeatureDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
Name = otherRecord.Name;
}
if (GroupName != otherRecord.GroupName)
{
GroupName = otherRecord.GroupName;
}
if (ParentName != otherRecord.ParentName)
{
ParentName = otherRecord.ParentName;
}
if (DisplayName != otherRecord.DisplayName)
{
DisplayName = otherRecord.DisplayName;
}
if (Description != otherRecord.Description)
{
Description = otherRecord.Description;
}
if (DefaultValue != otherRecord.DefaultValue)
{
DefaultValue = otherRecord.DefaultValue;
}
if (IsVisibleToClients != otherRecord.IsVisibleToClients)
{
IsVisibleToClients = otherRecord.IsVisibleToClients;
}
if (IsAvailableToHost != otherRecord.IsAvailableToHost)
{
IsAvailableToHost = otherRecord.IsAvailableToHost;
}
if (AllowedProviders != otherRecord.AllowedProviders)
{
AllowedProviders = otherRecord.AllowedProviders;
}
if (ValueType != otherRecord.ValueType)
{
ValueType = otherRecord.ValueType;
}
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

102
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionSerializer.cs

@ -0,0 +1,102 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement;
public class FeatureDefinitionSerializer : IFeatureDefinitionSerializer, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
protected StringValueTypeSerializer StringValueTypeSerializer { get; }
public FeatureDefinitionSerializer(IGuidGenerator guidGenerator, ILocalizableStringSerializer localizableStringSerializer, StringValueTypeSerializer stringValueTypeSerializer)
{
GuidGenerator = guidGenerator;
LocalizableStringSerializer = localizableStringSerializer;
StringValueTypeSerializer = stringValueTypeSerializer;
}
public async Task<(FeatureGroupDefinitionRecord[], FeatureDefinitionRecord[])> SerializeAsync(IEnumerable<FeatureGroupDefinition> featureGroups)
{
var featureGroupRecords = new List<FeatureGroupDefinitionRecord>();
var featureRecords = new List<FeatureDefinitionRecord>();
foreach (var featureGroup in featureGroups)
{
featureGroupRecords.Add(await SerializeAsync(featureGroup));
foreach (var feature in featureGroup.GetFeaturesWithChildren())
{
featureRecords.Add(await SerializeAsync(feature, featureGroup));
}
}
return (featureGroupRecords.ToArray(), featureRecords.ToArray());
}
public Task<FeatureGroupDefinitionRecord> SerializeAsync(FeatureGroupDefinition featureGroup)
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
var featureGroupRecord = new FeatureGroupDefinitionRecord(
GuidGenerator.Create(),
featureGroup.Name,
LocalizableStringSerializer.Serialize(featureGroup.DisplayName)
);
foreach (var property in featureGroup.Properties)
{
featureGroupRecord.SetProperty(property.Key, property.Value);
}
return Task.FromResult(featureGroupRecord);
}
}
public Task<FeatureDefinitionRecord> SerializeAsync(FeatureDefinition feature, FeatureGroupDefinition featureGroup)
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
var featureRecord = new FeatureDefinitionRecord(
GuidGenerator.Create(),
featureGroup?.Name,
feature.Name,
feature.Parent?.Name,
LocalizableStringSerializer.Serialize(feature.DisplayName),
LocalizableStringSerializer.Serialize(feature.Description),
feature.DefaultValue,
feature.IsVisibleToClients,
feature.IsAvailableToHost,
SerializeProviders(feature.AllowedProviders),
SerializeStringValueType(feature.ValueType)
);
foreach (var property in feature.Properties)
{
featureRecord.SetProperty(property.Key, property.Value);
}
return Task.FromResult(featureRecord);
}
}
protected virtual string SerializeProviders(ICollection<string> providers)
{
return providers.Any()
? providers.JoinAsString(",")
: null;
}
protected virtual string SerializeStringValueType(IStringValueType stringValueType)
{
return StringValueTypeSerializer.Serialize(stringValueType);
}
}

85
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs

@ -0,0 +1,85 @@
using System;
using Newtonsoft.Json;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.FeatureManagement;
public class FeatureGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if feature definitions have changed (in StaticFeatureSaver.CalculateHash()).
*/
[JsonIgnore] //TODO: TODO: Use JSON modifier to ignore this property
public override Guid Id { get; protected set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public FeatureGroupDefinitionRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public FeatureGroupDefinitionRecord(
Guid id,
string name,
string displayName)
: base(id)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), FeatureGroupDefinitionRecordConsts.MaxNameLength);
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), FeatureGroupDefinitionRecordConsts.MaxDisplayNameLength);;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public bool HasSameData(FeatureGroupDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
}
if (!this.HasSameExtraProperties(otherRecord))
{
return false;
}
return true;
}
public void Patch(FeatureGroupDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
Name = otherRecord.Name;
}
if (DisplayName != otherRecord.DisplayName)
{
DisplayName = otherRecord.DisplayName;
}
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

10
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs

@ -9,6 +9,16 @@ public class FeatureManagementOptions
public Dictionary<string, string> ProviderPolicies { get; }
/// <summary>
/// Default: true.
/// </summary>
public bool SaveStaticFeaturesToDatabase { get; set; } = true;
/// <summary>
/// Default: false.
/// </summary>
public bool IsDynamicFeatureStoreEnabled { get; set; }
public FeatureManagementOptions()
{
Providers = new TypeList<IFeatureManagementProvider>();

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs

@ -87,7 +87,7 @@ public class FeatureManagementStore : IFeatureManagementStore, ITransientDepende
string currentName,
FeatureValueCacheItem currentCacheItem)
{
var featureDefinitions = FeatureDefinitionManager.GetAll();
var featureDefinitions = await FeatureDefinitionManager.GetAllAsync();
var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);

6
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs

@ -75,7 +75,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
{
Check.NotNull(providerName, nameof(providerName));
var featureDefinitions = FeatureDefinitionManager.GetAll();
var featureDefinitions = await FeatureDefinitionManager.GetAllAsync();
var providers = Enumerable.Reverse(Providers).SkipWhile(c => c.Name != providerName);
if (!fallback)
@ -130,7 +130,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName));
var feature = FeatureDefinitionManager.Get(name);
var feature = await FeatureDefinitionManager.GetAsync(name);
if (feature.ValueType?.Validator.IsValid(value) == false)
{
@ -186,7 +186,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency
string providerKey,
bool fallback = true) //TODO: Fallback is not used
{
var feature = FeatureDefinitionManager.Get(name);
var feature = await FeatureDefinitionManager.GetAsync(name);
var providers = Enumerable
.Reverse(Providers);

26
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IDynamicFeatureDefinitionStoreInMemoryCache.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement;
public interface IDynamicFeatureDefinitionStoreInMemoryCache
{
string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; }
Task FillAsync(
List<FeatureGroupDefinitionRecord> featureGroupRecords,
List<FeatureDefinitionRecord> featureRecords);
FeatureDefinition GetFeatureOrNull(string name);
IReadOnlyList<FeatureDefinition> GetFeatures();
IReadOnlyList<FeatureGroupDefinition> GetGroups();
}

13
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureDefinitionRecordRepository.cs

@ -0,0 +1,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.FeatureManagement;
public interface IFeatureDefinitionRecordRepository : IBasicRepository<FeatureDefinitionRecord, Guid>
{
Task<FeatureDefinitionRecord> FindByNameAsync(
string name,
CancellationToken cancellationToken = default);
}

15
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureDefinitionSerializer.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement;
public interface IFeatureDefinitionSerializer
{
Task<(FeatureGroupDefinitionRecord[], FeatureDefinitionRecord[])> SerializeAsync(IEnumerable<FeatureGroupDefinition> featureGroups);
Task<FeatureGroupDefinitionRecord> SerializeAsync(FeatureGroupDefinition featureGroup);
Task<FeatureDefinitionRecord> SerializeAsync(FeatureDefinition feature, [CanBeNull] FeatureGroupDefinition featureGroup);
}

9
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureGroupDefinitionRecordRepository.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.FeatureManagement;
public interface IFeatureGroupDefinitionRecordRepository : IBasicRepository<FeatureGroupDefinitionRecord, Guid>
{
}

8
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IStaticFeatureSaver.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.FeatureManagement;
public interface IStaticFeatureSaver
{
Task SaveAsync();
}

294
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureSaver.cs

@ -0,0 +1,294 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Features;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.FeatureManagement;
public class StaticFeatureSaver : IStaticFeatureSaver, ITransientDependency
{
protected IStaticFeatureDefinitionStore StaticStore { get; }
protected IFeatureGroupDefinitionRecordRepository FeatureGroupRepository { get; }
protected IFeatureDefinitionRecordRepository FeatureRepository { get; }
protected IFeatureDefinitionSerializer FeatureSerializer { get; }
protected IDistributedCache Cache { get; }
protected IApplicationNameAccessor ApplicationNameAccessor { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected AbpFeatureOptions FeatureOptions { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public StaticFeatureSaver(
IStaticFeatureDefinitionStore staticStore,
IFeatureGroupDefinitionRecordRepository featureGroupRepository,
IFeatureDefinitionRecordRepository featureRepository,
IFeatureDefinitionSerializer featureSerializer,
IDistributedCache cache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IApplicationNameAccessor applicationNameAccessor,
IAbpDistributedLock distributedLock,
IOptions<AbpFeatureOptions> featureManagementOptions,
ICancellationTokenProvider cancellationTokenProvider)
{
StaticStore = staticStore;
FeatureGroupRepository = featureGroupRepository;
FeatureRepository = featureRepository;
FeatureSerializer = featureSerializer;
Cache = cache;
ApplicationNameAccessor = applicationNameAccessor;
DistributedLock = distributedLock;
CancellationTokenProvider = cancellationTokenProvider;
FeatureOptions = featureManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
[UnitOfWork]
public virtual async Task SaveAsync()
{
await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(
GetApplicationDistributedLockKey()
);
if (applicationLockHandle == null)
{
/* Another application instance is already doing it */
return;
}
/* NOTE: This can be further optimized by using 4 cache values for:
* Groups, features, deleted groups and deleted features.
* But the code would be more complex. This is enough for now.
*/
var cacheKey = GetApplicationHashCacheKey();
var cachedHash = await Cache.GetStringAsync(cacheKey, CancellationTokenProvider.Token);
var (featureGroupRecords, featureRecords) = await FeatureSerializer.SerializeAsync(
await StaticStore.GetGroupsAsync()
);
var currentHash = CalculateHash(
featureGroupRecords,
featureRecords,
FeatureOptions.DeletedFeatureGroups,
FeatureOptions.DeletedFeatures
);
if (cachedHash == currentHash)
{
return;
}
await using (var commonLockHandle = await DistributedLock.TryAcquireAsync(
GetCommonDistributedLockKey(),
TimeSpan.FromMinutes(5)))
{
if (commonLockHandle == null)
{
/* It will re-try */
throw new AbpException("Could not acquire distributed lock for saving static features!");
}
var hasChangesInGroups = await UpdateChangedFeatureGroupsAsync(featureGroupRecords);
var hasChangesInFeatures = await UpdateChangedFeaturesAsync(featureRecords);
if (hasChangesInGroups ||hasChangesInFeatures)
{
await Cache.SetStringAsync(
GetCommonStampCacheKey(),
Guid.NewGuid().ToString(),
new DistributedCacheEntryOptions {
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
},
CancellationTokenProvider.Token
);
}
}
await Cache.SetStringAsync(
cacheKey,
currentHash,
new DistributedCacheEntryOptions {
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
},
CancellationTokenProvider.Token
);
}
private async Task<bool> UpdateChangedFeatureGroupsAsync(
IEnumerable<FeatureGroupDefinitionRecord> featureGroupRecords)
{
var newRecords = new List<FeatureGroupDefinitionRecord>();
var changedRecords = new List<FeatureGroupDefinitionRecord>();
var featureGroupRecordsInDatabase = (await FeatureGroupRepository.GetListAsync())
.ToDictionary(x => x.Name);
foreach (var featureGroupRecord in featureGroupRecords)
{
var featureGroupRecordInDatabase = featureGroupRecordsInDatabase.GetOrDefault(featureGroupRecord.Name);
if (featureGroupRecordInDatabase == null)
{
/* New group */
newRecords.Add(featureGroupRecord);
continue;
}
if (featureGroupRecord.HasSameData(featureGroupRecordInDatabase))
{
/* Not changed */
continue;
}
/* Changed */
featureGroupRecordInDatabase.Patch(featureGroupRecord);
changedRecords.Add(featureGroupRecordInDatabase);
}
/* Deleted */
var deletedRecords = FeatureOptions.DeletedFeatureGroups.Any()
? featureGroupRecordsInDatabase.Values
.Where(x => FeatureOptions.DeletedFeatureGroups.Contains(x.Name))
.ToArray()
: Array.Empty<FeatureGroupDefinitionRecord>();
if (newRecords.Any())
{
await FeatureGroupRepository.InsertManyAsync(newRecords);
}
if (changedRecords.Any())
{
await FeatureGroupRepository.UpdateManyAsync(changedRecords);
}
if (deletedRecords.Any())
{
await FeatureGroupRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private async Task<bool> UpdateChangedFeaturesAsync(
IEnumerable<FeatureDefinitionRecord> featureRecords)
{
var newRecords = new List<FeatureDefinitionRecord>();
var changedRecords = new List<FeatureDefinitionRecord>();
var featureRecordsInDatabase = (await FeatureRepository.GetListAsync())
.ToDictionary(x => x.Name);
foreach (var featureRecord in featureRecords)
{
var featureRecordInDatabase = featureRecordsInDatabase.GetOrDefault(featureRecord.Name);
if (featureRecordInDatabase == null)
{
/* New group */
newRecords.Add(featureRecord);
continue;
}
if (featureRecord.HasSameData(featureRecordInDatabase))
{
/* Not changed */
continue;
}
/* Changed */
featureRecordInDatabase.Patch(featureRecord);
changedRecords.Add(featureRecordInDatabase);
}
/* Deleted */
var deletedRecords = new List<FeatureDefinitionRecord>();
if (FeatureOptions.DeletedFeatures.Any())
{
deletedRecords.AddRange(
featureRecordsInDatabase.Values
.Where(x => FeatureOptions.DeletedFeatures.Contains(x.Name))
);
}
if (FeatureOptions.DeletedFeatureGroups.Any())
{
deletedRecords.AddIfNotContains(
featureRecordsInDatabase.Values
.Where(x => FeatureOptions.DeletedFeatureGroups.Contains(x.GroupName))
);
}
if (newRecords.Any())
{
await FeatureRepository.InsertManyAsync(newRecords);
}
if (changedRecords.Any())
{
await FeatureRepository.UpdateManyAsync(changedRecords);
}
if (deletedRecords.Any())
{
await FeatureRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private string GetApplicationDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationNameAccessor.ApplicationName}_AbpFeatureUpdateLock";
}
private string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpFeatureUpdateLock";
}
private string GetApplicationHashCacheKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationNameAccessor.ApplicationName}_AbpFeaturesHash";
}
private string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryFeatureCacheStamp";
}
private static string CalculateHash(
FeatureGroupDefinitionRecord[] featureGroupRecords,
FeatureDefinitionRecord[] featureRecords,
IEnumerable<string> deletedFeatureGroups,
IEnumerable<string> deletedFeatures)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("FeatureGroupRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(featureGroupRecords));
stringBuilder.Append("FeatureRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(featureRecords));
stringBuilder.Append("DeletedFeatureGroups:");
stringBuilder.AppendLine(deletedFeatureGroups.JoinAsString(","));
stringBuilder.Append("DeletedFeature:");
stringBuilder.Append(deletedFeatures.JoinAsString(","));
return stringBuilder
.ToString()
.ToMd5();
}
}

25
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StringValueTypeSerializer.cs

@ -0,0 +1,25 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement;
public class StringValueTypeSerializer : ITransientDependency
{
protected IJsonSerializer JsonSerializer { get; }
public StringValueTypeSerializer(IJsonSerializer jsonSerializer)
{
JsonSerializer = jsonSerializer;
}
public virtual string Serialize(IStringValueType stringValueType)
{
return JsonSerializer.Serialize(stringValueType);
}
public virtual IStringValueType Deserialize(string value)
{
return JsonSerializer.Deserialize<IStringValueType>(value);
}
}

2
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs

@ -14,6 +14,8 @@ public class AbpFeatureManagementEntityFrameworkCoreModule : AbpModule
{
context.Services.AddAbpDbContext<FeatureManagementDbContext>(options =>
{
options.AddRepository<FeatureGroupDefinitionRecord, EfCoreFeatureGroupDefinitionRecordRepository>();
options.AddRepository<FeatureDefinitionRecord, EfCoreFeatureDefinitionRecordRepository>();
options.AddDefaultRepositories<IFeatureManagementDbContext>();
options.AddRepository<FeatureValue, EfCoreFeatureValueRepository>();

27
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureDefinitionRecordRepository.cs

@ -0,0 +1,27 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
public class EfCoreFeatureDefinitionRecordRepository :
EfCoreRepository<IFeatureManagementDbContext, FeatureDefinitionRecord, Guid>,
IFeatureDefinitionRecordRepository
{
public EfCoreFeatureDefinitionRecordRepository(
IDbContextProvider<IFeatureManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<FeatureDefinitionRecord> FindByNameAsync(string name, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.OrderBy(x => x.Id)
.FirstOrDefaultAsync(r => r.Name == name, cancellationToken);
}
}

16
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureGroupDefinitionRecordRepository.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
public class EfCoreFeatureGroupDefinitionRecordRepository :
EfCoreRepository<IFeatureManagementDbContext, FeatureGroupDefinitionRecord, Guid>,
IFeatureGroupDefinitionRecordRepository
{
public EfCoreFeatureGroupDefinitionRecordRepository(
IDbContextProvider<IFeatureManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

4
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs

@ -9,6 +9,10 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[ConnectionStringName(AbpFeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementDbContext : AbpDbContext<FeatureManagementDbContext>, IFeatureManagementDbContext
{
public DbSet<FeatureGroupDefinitionRecord> FeatureGroups { get; set; }
public DbSet<FeatureDefinitionRecord> Features { get; set; }
public DbSet<FeatureValue> FeatureValues { get; set; }
public FeatureManagementDbContext(DbContextOptions<FeatureManagementDbContext> options)

36
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs

@ -26,7 +26,41 @@ public static class FeatureManagementDbContextModelCreatingExtensions
b.Property(x => x.ProviderName).HasMaxLength(FeatureValueConsts.MaxProviderNameLength);
b.Property(x => x.ProviderKey).HasMaxLength(FeatureValueConsts.MaxProviderKeyLength);
b.HasIndex(x => new { x.Name, x.ProviderName, x.ProviderKey }).IsUnique(true);
b.HasIndex(x => new { x.Name, x.ProviderName, x.ProviderKey }).IsUnique();
b.ApplyObjectExtensionMappings();
});
builder.Entity<FeatureGroupDefinitionRecord>(b =>
{
b.ToTable(AbpFeatureManagementDbProperties.DbTablePrefix + "FeatureGroups", AbpFeatureManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).HasMaxLength(FeatureGroupDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.DisplayName).HasMaxLength(FeatureGroupDefinitionRecordConsts.MaxDisplayNameLength).IsRequired();
b.HasIndex(x => new { x.Name }).IsUnique();
b.ApplyObjectExtensionMappings();
});
builder.Entity<FeatureDefinitionRecord>(b =>
{
b.ToTable(AbpFeatureManagementDbProperties.DbTablePrefix + "Features", AbpFeatureManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.GroupName).HasMaxLength(FeatureGroupDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.Name).HasMaxLength(FeatureDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.ParentName).HasMaxLength(FeatureDefinitionRecordConsts.MaxNameLength);
b.Property(x => x.DisplayName).HasMaxLength(FeatureDefinitionRecordConsts.MaxDisplayNameLength).IsRequired();
b.Property(x => x.Description).HasMaxLength(FeatureDefinitionRecordConsts.MaxDescriptionLength);
b.Property(x => x.DefaultValue).HasMaxLength(FeatureDefinitionRecordConsts.MaxDefaultValueLength);
b.Property(x => x.AllowedProviders).HasMaxLength(FeatureDefinitionRecordConsts.MaxAllowedProvidersLength);
b.Property(x => x.ValueType).HasMaxLength(FeatureDefinitionRecordConsts.MaxValueTypeLength);
b.HasIndex(x => new { x.Name }).IsUnique();
b.HasIndex(x => new { x.GroupName });
b.ApplyObjectExtensionMappings();
});

4
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs

@ -9,5 +9,9 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[ConnectionStringName(AbpFeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementDbContext : IEfCoreDbContext
{
DbSet<FeatureGroupDefinitionRecord> FeatureGroups { get; }
DbSet<FeatureDefinitionRecord> Features { get; }
DbSet<FeatureValue> FeatureValues { get; }
}

4
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs

@ -32,10 +32,10 @@ public class AbpFeatureManagementHttpApiModule : AbpModule
.AddBaseTypes(typeof(AbpUiResource));
});
var contractsOptions = context.Services.ExecutePreConfiguredActions<AbpFeatureManagementApplicationContractsOptions>();
var valueValidatorFactoryOptions = context.Services.ExecutePreConfiguredActions<ValueValidatorFactoryOptions>();
Configure<JsonOptions>(options =>
{
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter(contractsOptions));
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter(valueValidatorFactoryOptions));
});
}
}

2
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs

@ -16,6 +16,8 @@ public class AbpFeatureManagementMongoDbModule : AbpModule
{
options.AddDefaultRepositories<IFeatureManagementMongoDbContext>();
options.AddRepository<FeatureGroupDefinitionRecord, MongoFeatureGroupDefinitionRecordRepository>();
options.AddRepository<FeatureDefinitionRecord, MongoFeatureDefinitionRecordRepository>();
options.AddRepository<FeatureValue, MongoFeatureValueRepository>();
});
}

2
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs

@ -9,6 +9,8 @@ namespace Volo.Abp.FeatureManagement.MongoDB;
[ConnectionStringName(AbpFeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementMongoDbContext : AbpMongoDbContext, IFeatureManagementMongoDbContext
{
public IMongoCollection<FeatureGroupDefinitionRecord> FeatureGroups => Collection<FeatureGroupDefinitionRecord>();
public IMongoCollection<FeatureDefinitionRecord> Features => Collection<FeatureDefinitionRecord>();
public IMongoCollection<FeatureValue> FeatureValues => Collection<FeatureValue>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)

10
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs

@ -9,6 +9,16 @@ public static class FeatureManagementMongoDbContextExtensions
{
Check.NotNull(builder, nameof(builder));
builder.Entity<FeatureGroupDefinitionRecord>(b =>
{
b.CollectionName = AbpFeatureManagementDbProperties.DbTablePrefix + "FeatureGroups";
});
builder.Entity<FeatureDefinitionRecord>(b =>
{
b.CollectionName = AbpFeatureManagementDbProperties.DbTablePrefix + "Features";
});
builder.Entity<FeatureValue>(b =>
{
b.CollectionName = AbpFeatureManagementDbProperties.DbTablePrefix + "FeatureValues";

4
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs

@ -9,5 +9,9 @@ namespace Volo.Abp.FeatureManagement.MongoDB;
[ConnectionStringName(AbpFeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementMongoDbContext : IAbpMongoDbContext
{
IMongoCollection<FeatureGroupDefinitionRecord> FeatureGroups { get; }
IMongoCollection<FeatureDefinitionRecord> Features { get; }
IMongoCollection<FeatureValue> FeatureValues { get; }
}

30
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureDefinitionRecordRepository.cs

@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB;
public class MongoFeatureDefinitionRecordRepository :
MongoDbRepository<IFeatureManagementMongoDbContext, FeatureDefinitionRecord, Guid>,
IFeatureDefinitionRecordRepository
{
public MongoFeatureDefinitionRecordRepository(
IMongoDbContextProvider<IFeatureManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<FeatureDefinitionRecord> FindByNameAsync(string name, CancellationToken cancellationToken = default)
{
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken))
.OrderBy(x => x.Id)
.FirstOrDefaultAsync(
s => s.Name == name,
cancellationToken
);
}
}

16
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureGroupDefinitionRecordRepository.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB;
public class MongoFeatureGroupDefinitionRecordRepository :
MongoDbRepository<IFeatureManagementMongoDbContext, FeatureGroupDefinitionRecord, Guid>,
IFeatureGroupDefinitionRecordRepository
{
public MongoFeatureGroupDefinitionRecordRepository(
IMongoDbContextProvider<IFeatureManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

4
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs

@ -9,7 +9,9 @@ using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB;
public class MongoFeatureValueRepository : MongoDbRepository<IFeatureManagementMongoDbContext, FeatureValue, Guid>, IFeatureValueRepository
public class MongoFeatureValueRepository :
MongoDbRepository<IFeatureManagementMongoDbContext, FeatureValue, Guid>,
IFeatureValueRepository
{
public MongoFeatureValueRepository(IMongoDbContextProvider<IFeatureManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)

9
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs

@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.Web.Pages.FeatureManagement;
@ -31,14 +34,18 @@ public class FeatureManagementModal : AbpPageModel
protected ILocalEventBus LocalEventBus { get; }
public AbpLocalizationOptions LocalizationOptions { get; }
public FeatureManagementModal(
IFeatureAppService featureAppService,
ILocalEventBus localEventBus)
ILocalEventBus localEventBus,
IOptions<AbpLocalizationOptions> localizationOptions)
{
ObjectMapperContext = typeof(AbpFeatureManagementWebModule);
FeatureAppService = featureAppService;
LocalEventBus = localEventBus;
LocalizationOptions = localizationOptions.Value;
}
public virtual async Task<IActionResult> OnGetAsync()

2
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs

@ -20,7 +20,7 @@ public abstract class StringValueJsonConverter_Tests : FeatureManagementApplicat
protected override void BeforeAddApplication(IServiceCollection services)
{
services.PreConfigure<AbpFeatureManagementApplicationContractsOptions>(options =>
services.PreConfigure<ValueValidatorFactoryOptions>(options =>
{
options.ValueValidatorFactory.Add(new ValueValidatorFactory<UrlValueValidator>("URL"));
});

87
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureDefinitionSerializer_Tests.cs

@ -0,0 +1,87 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
using Xunit;
namespace Volo.Abp.FeatureManagement;
public class FeatureDefinitionSerializer_Tests : FeatureManagementDomainTestBase
{
private readonly IFeatureDefinitionSerializer _serializer;
public FeatureDefinitionSerializer_Tests()
{
_serializer = GetRequiredService<IFeatureDefinitionSerializer>();
}
[Fact]
public async Task Serialize_Feature_Group_Definition()
{
// Arrange
var context = new FeatureDefinitionContext();
var group1 = CreateFeatureGroup1(context);
// Act
var featureGroupRecord = await _serializer.SerializeAsync(group1);
//Assert
featureGroupRecord.Name.ShouldBe("Group1");
featureGroupRecord.DisplayName.ShouldBe("F:Group one");
featureGroupRecord.GetProperty("CustomProperty1").ShouldBe("CustomValue1");
}
[Fact]
public async Task Serialize_Complex_Feature_Definition()
{
// Arrange
var context = new FeatureDefinitionContext();
var group1 = CreateFeatureGroup1(context);
var feature1 = group1.AddFeature(
"Feature1",
"default",
new LocalizableString(typeof(AbpFeatureManagementResource), "Feature1"),
new LocalizableString(typeof(AbpFeatureManagementResource), "Feature1"),
new ToggleStringValueType(),
isVisibleToClients: true
)
.WithProviders("ProviderA", "ProviderB")
.WithProperty("CustomProperty2", "CustomValue2");
// Act
var featureRecord = await _serializer.SerializeAsync(
feature1,
group1
);
//Assert
featureRecord.Name.ShouldBe("Feature1");
featureRecord.GroupName.ShouldBe("Group1");
featureRecord.DisplayName.ShouldBe("L:AbpFeatureManagement,Feature1");
featureRecord.ValueType.ShouldBe("{\"name\":\"ToggleStringValueType\",\"properties\":{},\"validator\":{\"name\":\"BOOLEAN\",\"properties\":{}}}");
featureRecord.GetProperty("CustomProperty2").ShouldBe("CustomValue2");
featureRecord.AllowedProviders.ShouldBe("ProviderA,ProviderB");
}
private static FeatureGroupDefinition CreateFeatureGroup1(
IFeatureDefinitionContext context)
{
var group = context.AddGroup(
"Group1",
displayName: new FixedLocalizableString("Group one")
);
group["CustomProperty1"] = "CustomValue1";
return group;
}
}

52
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/StringValueTypeSerializer_Tests.cs

@ -0,0 +1,52 @@
using System;
using Shouldly;
using Volo.Abp.Validation.StringValues;
using Xunit;
namespace Volo.Abp.FeatureManagement;
public class StringValueTypeSerializer_Tests : FeatureManagementDomainTestBase
{
private readonly StringValueTypeSerializer _serializer;
public StringValueTypeSerializer_Tests()
{
_serializer = GetRequiredService<StringValueTypeSerializer>();
}
[Fact]
public void Serialize_And_Deserialize_Test()
{
// Arrange
var valueType = new SelectionStringValueType
{
ItemSource = new StaticSelectionStringValueItemSource(
new LocalizableSelectionStringValueItem
{
Value = "TestValue",
DisplayText = new LocalizableStringInfo("TestResourceName", "TestName")
}),
Validator = new AlwaysValidValueValidator()
};
// Act
var valueTypeJson = _serializer.Serialize(valueType);
//Assert
valueTypeJson.ShouldBe("{\"itemSource\":{\"items\":[{\"value\":\"TestValue\",\"displayText\":{\"resourceName\":\"TestResourceName\",\"name\":\"TestName\"}}]},\"name\":\"SelectionStringValueType\",\"properties\":{},\"validator\":{\"name\":\"NULL\",\"properties\":{}}}");
// Act
var valueType2 = _serializer.Deserialize(valueTypeJson);
//Assert
valueType2.ShouldBeOfType<SelectionStringValueType>();
valueType2.Validator.ShouldBeOfType<AlwaysValidValueValidator>();
valueType2.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>();
valueType2.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x =>
x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" &&
x.DisplayText.Name == "TestName");
}
}

13
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs

@ -5,9 +5,11 @@ using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Threading;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.Uow;
namespace Volo.Abp.Identity;
@ -36,6 +38,17 @@ public class AbpIdentityDomainTestModule : AbpModule
.Get<IdentityResource>()
.AddVirtualJson("/Volo/Abp/Identity/LocalizationExtensions");
});
Configure<PermissionManagementOptions>(options =>
{
options.IsDynamicPermissionStoreEnabled = false;
options.SaveStaticPermissionsToDatabase = false;
});
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

4
modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs

@ -103,7 +103,7 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
{
return new PermissionGrantInfoDto {
Name = permission.Name,
DisplayName = permission.DisplayName.Localize(StringLocalizerFactory),
DisplayName = permission.DisplayName?.Localize(StringLocalizerFactory),
ParentName = permission.Parent?.Name,
AllowedProviders = permission.Providers,
GrantedProviders = new List<ProviderInfoDto>()
@ -117,7 +117,7 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
return new PermissionGroupDto
{
Name = group.Name,
DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
DisplayName = group.DisplayName?.Localize(StringLocalizerFactory),
DisplayNameKey = localizableDisplayName?.Name,
DisplayNameResource = localizableDisplayName?.ResourceType != null
? LocalizationResourceNameAttribute.GetName(localizableDisplayName.ResourceType)

19
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs

@ -25,7 +25,12 @@ namespace Volo.Abp.PermissionManagement;
public class AbpPermissionManagementDomainModule : AbpModule
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicPermissions(context);
@ -44,12 +49,12 @@ public class AbpPermissionManagementDomainModule : AbpModule
.ServiceProvider
.GetRequiredService<IOptions<PermissionManagementOptions>>()
.Value;
if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
Task.Run(async () =>
@ -58,7 +63,7 @@ public class AbpPermissionManagementDomainModule : AbpModule
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
@ -67,7 +72,7 @@ public class AbpPermissionManagementDomainModule : AbpModule
{
return;
}
await SaveStaticPermissionsToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
@ -127,7 +132,7 @@ public class AbpPermissionManagementDomainModule : AbpModule
try
{
// Pre-cache permissions, so first request doesn't wait
// Pre-cache permissions, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicPermissionDefinitionStore>()
@ -144,4 +149,4 @@ public class AbpPermissionManagementDomainModule : AbpModule
throw; // It will be cached in InitializeDynamicPermissions
}
}
}
}

9
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs

@ -8,11 +8,7 @@ public class PermissionManagementOptions
public ITypeList<IPermissionManagementProvider> ManagementProviders { get; }
public Dictionary<string, string> ProviderPolicies { get; }
public HashSet<string> DeletedPermissions { get; }
public HashSet<string> DeletedPermissionGroups { get; }
/// <summary>
/// Default: true.
/// </summary>
@ -27,8 +23,5 @@ public class PermissionManagementOptions
{
ManagementProviders = new TypeList<IPermissionManagementProvider>();
ProviderPolicies = new Dictionary<string, string>();
DeletedPermissions = new HashSet<string>();
DeletedPermissionGroups = new HashSet<string>();
}
}

60
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs

@ -24,20 +24,20 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
protected IDistributedCache Cache { get; }
protected IApplicationNameAccessor ApplicationNameAccessor { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected PermissionManagementOptions PermissionManagementOptions { get; }
protected AbpPermissionOptions PermissionOptions { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public StaticPermissionSaver(
IStaticPermissionDefinitionStore staticStore,
IPermissionGroupDefinitionRecordRepository permissionGroupRepository,
IPermissionDefinitionRecordRepository permissionRepository,
IPermissionDefinitionSerializer permissionSerializer,
IDistributedCache cache,
IDistributedCache cache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IApplicationNameAccessor applicationNameAccessor,
IAbpDistributedLock distributedLock,
IOptions<PermissionManagementOptions> permissionManagementOptions,
IOptions<AbpPermissionOptions> permissionOptions,
ICancellationTokenProvider cancellationTokenProvider)
{
StaticStore = staticStore;
@ -48,23 +48,23 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
ApplicationNameAccessor = applicationNameAccessor;
DistributedLock = distributedLock;
CancellationTokenProvider = cancellationTokenProvider;
PermissionManagementOptions = permissionManagementOptions.Value;
PermissionOptions = permissionOptions.Value;
CacheOptions = cacheOptions.Value;
}
[UnitOfWork]
public virtual async Task SaveAsync()
{
await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(
GetApplicationDistributedLockKey()
);
if (applicationLockHandle == null)
{
/* Another application instance is already doing it */
return;
}
/* NOTE: This can be further optimized by using 4 cache values for:
* Groups, permissions, deleted groups and deleted permissions.
* But the code would be more complex. This is enough for now.
@ -80,10 +80,10 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
var currentHash = CalculateHash(
permissionGroupRecords,
permissionRecords,
PermissionManagementOptions.DeletedPermissionGroups,
PermissionManagementOptions.DeletedPermissions
PermissionOptions.DeletedPermissionGroups,
PermissionOptions.DeletedPermissions
);
if (cachedHash == currentHash)
{
return;
@ -154,11 +154,11 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
permissionGroupRecordInDatabase.Patch(permissionGroupRecord);
changedRecords.Add(permissionGroupRecordInDatabase);
}
/* Deleted */
var deletedRecords = PermissionManagementOptions.DeletedPermissionGroups.Any()
var deletedRecords = PermissionOptions.DeletedPermissionGroups.Any()
? permissionGroupRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissionGroups.Contains(x.Name))
.Where(x => PermissionOptions.DeletedPermissionGroups.Contains(x.Name))
.ToArray()
: Array.Empty<PermissionGroupDefinitionRecord>();
@ -176,10 +176,10 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
{
await PermissionGroupRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private async Task<bool> UpdateChangedPermissionsAsync(
IEnumerable<PermissionDefinitionRecord> permissionRecords)
{
@ -209,23 +209,23 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
permissionRecordInDatabase.Patch(permissionRecord);
changedRecords.Add(permissionRecordInDatabase);
}
/* Deleted */
var deletedRecords = new List<PermissionDefinitionRecord>();
if (PermissionManagementOptions.DeletedPermissions.Any())
if (PermissionOptions.DeletedPermissions.Any())
{
deletedRecords.AddRange(
permissionRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissions.Contains(x.Name))
.Where(x => PermissionOptions.DeletedPermissions.Contains(x.Name))
);
}
if (PermissionManagementOptions.DeletedPermissionGroups.Any())
if (PermissionOptions.DeletedPermissionGroups.Any())
{
deletedRecords.AddIfNotContains(
permissionRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissionGroups.Contains(x.GroupName))
.Where(x => PermissionOptions.DeletedPermissionGroups.Contains(x.GroupName))
);
}
@ -243,7 +243,7 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
{
await PermissionRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
@ -261,7 +261,7 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
{
return $"{CacheOptions.KeyPrefix}_{ApplicationNameAccessor.ApplicationName}_AbpPermissionsHash";
}
private string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp";
@ -274,21 +274,21 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
IEnumerable<string> deletedPermissions)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("PermissionGroupRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionGroupRecords));
stringBuilder.Append("PermissionRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionRecords));
stringBuilder.Append("DeletedPermissionGroups:");
stringBuilder.AppendLine(deletedPermissionGroups.JoinAsString(","));
stringBuilder.Append("DeletedPermission:");
stringBuilder.Append(deletedPermissions.JoinAsString(","));
return stringBuilder
.ToString()
.ToMd5();
}
}
}

27
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs

@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.PermissionManagement;
public abstract class PermissionDefinitionRecordRepository_Tests : PermissionTestBase
{
protected IPermissionDefinitionRecordRepository PermissionDefinitionRecordRepository { get; set; }
protected PermissionDefinitionRecordRepository_Tests()
{
PermissionDefinitionRecordRepository = GetRequiredService<IPermissionDefinitionRecordRepository>();
}
[Fact]
public async Task FindByNameAsync()
{
var permission = await PermissionDefinitionRecordRepository.FindByNameAsync("MyPermission1");
permission.ShouldNotBeNull();
permission.Name.ShouldBe("MyPermission1");
permission = await PermissionDefinitionRecordRepository.FindByNameAsync("MyPermission2");
permission.ShouldNotBeNull();
permission.Name.ShouldBe("MyPermission2");
}
}

13
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/StaticPermissionSaver_Tests.cs

@ -0,0 +1,13 @@
namespace Volo.Abp.PermissionManagement;
public class StaticPermissionSaver_Tests : PermissionTestBase
{
private readonly IStaticPermissionSaver _saver;
public StaticPermissionSaver_Tests()
{
_saver = GetRequiredService<IStaticPermissionSaver>();
}
}

1
modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj

@ -13,6 +13,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.Domain.Tests\Volo.Abp.PermissionManagement.Domain.Tests.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj" />
</ItemGroup>

6
modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/EFCorePermissionDefinitionRecordRepository_Tests.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
public class EFCorePermissionDefinitionRecordRepository_Tests : PermissionDefinitionRecordRepository_Tests
{
}

1
modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj

@ -13,6 +13,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.PermissionManagement.MongoDB\Volo.Abp.PermissionManagement.MongoDB.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.Domain.Tests\Volo.Abp.PermissionManagement.Domain.Tests.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj" />
</ItemGroup>

9
modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs

@ -0,0 +1,9 @@
using Xunit;
namespace Volo.Abp.PermissionManagement.MongoDB;
[Collection(MongoTestCollection.Name)]
public class MongoDbPermissionDefinitionRecordRepository_Tests : PermissionGrantRepository_Tests<AbpPermissionManagementMongoDbTestModule>
{
}

91
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220825012458_Initial.Designer.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220909024045_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
{
[DbContext(typeof(MyProjectNameDbContext))]
[Migration("20220825012458_Initial")]
[Migration("20220909024045_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,95 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

59
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220825012458_Initial.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20220909024045_Initial.cs

@ -63,6 +63,42 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -712,6 +748,23 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -889,6 +942,12 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

91
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220825012635_Initial.Designer.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220909024045_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Host.Migrations
{
[DbContext(typeof(MyProjectNameDbContext))]
[Migration("20220825012635_Initial")]
[Migration("20220909024045_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,95 @@ namespace MyCompanyName.MyProjectName.Host.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

59
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220825012635_Initial.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220909024045_Initial.cs

@ -63,6 +63,42 @@ namespace MyCompanyName.MyProjectName.Host.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -712,6 +748,23 @@ namespace MyCompanyName.MyProjectName.Host.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -889,6 +942,12 @@ namespace MyCompanyName.MyProjectName.Host.Migrations
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Host.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

91
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220825012450_Initial.Designer.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220909024056_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Mvc.Migrations
{
[DbContext(typeof(MyProjectNameDbContext))]
[Migration("20220825012450_Initial")]
[Migration("20220909024056_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,95 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

59
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220825012450_Initial.cs → templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20220909024056_Initial.cs

@ -63,6 +63,42 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -712,6 +748,23 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -889,6 +942,12 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

158
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220825012920_Initial.Designer.cs → templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220909023853_Initial.Designer.cs

@ -12,8 +12,8 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(AuthServerDbContext))]
[Migration("20220825012920_Initial")]
[DbContext(typeof(MyProjectNameDbContext))]
[Migration("20220909023853_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,153 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsAbandoned")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("JobArgs")
.IsRequired()
.HasMaxLength(1048576)
.HasColumnType("nvarchar(max)");
b.Property<string>("JobName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime?>("LastTryTime")
.HasColumnType("datetime2");
b.Property<DateTime>("NextTryTime")
.HasColumnType("datetime2");
b.Property<byte>("Priority")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint")
.HasDefaultValue((byte)15);
b.Property<short>("TryCount")
.ValueGeneratedOnAdd()
.HasColumnType("smallint")
.HasDefaultValue((short)0);
b.HasKey("Id");
b.HasIndex("IsAbandoned", "NextTryTime");
b.ToTable("AbpBackgroundJobs", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")
@ -324,7 +471,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
@ -371,7 +517,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("SourceTenantId")
@ -398,7 +543,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
@ -475,7 +619,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Action")
@ -550,7 +693,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<int>("AccessFailedCount")
@ -834,7 +976,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Code")
@ -1424,7 +1565,6 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")

88
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220825012920_Initial.cs → templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220909023853_Initial.cs

@ -43,6 +43,27 @@ namespace MyCompanyName.MyProjectName.Migrations
table.PrimaryKey("PK_AbpAuditLogs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpBackgroundJobs",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
JobName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
JobArgs = table.Column<string>(type: "nvarchar(max)", maxLength: 1048576, nullable: false),
TryCount = table.Column<short>(type: "smallint", nullable: false, defaultValue: (short)0),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
NextTryTime = table.Column<DateTime>(type: "datetime2", nullable: false),
LastTryTime = table.Column<DateTime>(type: "datetime2", nullable: true),
IsAbandoned = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
Priority = table.Column<byte>(type: "tinyint", nullable: false, defaultValue: (byte)15),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpClaimTypes",
columns: table => new
@ -63,6 +84,42 @@ namespace MyCompanyName.MyProjectName.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -697,6 +754,11 @@ namespace MyCompanyName.MyProjectName.Migrations
table: "AbpAuditLogs",
columns: new[] { "TenantId", "UserId", "ExecutionTime" });
migrationBuilder.CreateIndex(
name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime",
table: "AbpBackgroundJobs",
columns: new[] { "IsAbandoned", "NextTryTime" });
migrationBuilder.CreateIndex(
name: "IX_AbpEntityChanges_AuditLogId",
table: "AbpEntityChanges",
@ -712,6 +774,23 @@ namespace MyCompanyName.MyProjectName.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -883,12 +962,21 @@ namespace MyCompanyName.MyProjectName.Migrations
migrationBuilder.DropTable(
name: "AbpAuditLogActions");
migrationBuilder.DropTable(
name: "AbpBackgroundJobs");
migrationBuilder.DropTable(
name: "AbpClaimTypes");
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs

@ -344,6 +344,95 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpBackgroundJobs", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

104
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220825012254_Initial.Designer.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220909024101_Initial.Designer.cs

@ -12,8 +12,8 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(MyProjectNameDbContext))]
[Migration("20220825012254_Initial")]
[DbContext(typeof(AuthServerDbContext))]
[Migration("20220909024101_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,62 +288,93 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b =>
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsAbandoned")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("JobArgs")
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(1048576)
.HasColumnType("nvarchar(max)");
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("JobName")
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime?>("LastTryTime")
.HasColumnType("datetime2");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("NextTryTime")
.HasColumnType("datetime2");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<byte>("Priority")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint")
.HasDefaultValue((byte)15);
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.Property<short>("TryCount")
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("smallint")
.HasDefaultValue((short)0);
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("IsAbandoned", "NextTryTime");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpBackgroundJobs", (string)null);
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
@ -382,6 +413,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
@ -428,6 +460,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("SourceTenantId")
@ -454,6 +487,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
@ -530,6 +564,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Action")
@ -604,6 +639,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<int>("AccessFailedCount")
@ -887,6 +923,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Code")
@ -1476,6 +1513,7 @@ namespace MyCompanyName.MyProjectName.Migrations
modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")

86
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20220825012254_Initial.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20220909024101_Initial.cs

@ -44,44 +44,59 @@ namespace MyCompanyName.MyProjectName.Migrations
});
migrationBuilder.CreateTable(
name: "AbpBackgroundJobs",
name: "AbpClaimTypes",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
JobName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
JobArgs = table.Column<string>(type: "nvarchar(max)", maxLength: 1048576, nullable: false),
TryCount = table.Column<short>(type: "smallint", nullable: false, defaultValue: (short)0),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
NextTryTime = table.Column<DateTime>(type: "datetime2", nullable: false),
LastTryTime = table.Column<DateTime>(type: "datetime2", nullable: true),
IsAbandoned = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
Priority = table.Column<byte>(type: "tinyint", nullable: false, defaultValue: (byte)15),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Required = table.Column<bool>(type: "bit", nullable: false),
IsStatic = table.Column<bool>(type: "bit", nullable: false),
Regex = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
RegexDescription = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<int>(type: "int", nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id);
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpClaimTypes",
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Required = table.Column<bool>(type: "bit", nullable: false),
IsStatic = table.Column<bool>(type: "bit", nullable: false),
Regex = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
RegexDescription = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<int>(type: "int", nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true)
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
@ -718,11 +733,6 @@ namespace MyCompanyName.MyProjectName.Migrations
table: "AbpAuditLogs",
columns: new[] { "TenantId", "UserId", "ExecutionTime" });
migrationBuilder.CreateIndex(
name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime",
table: "AbpBackgroundJobs",
columns: new[] { "IsAbandoned", "NextTryTime" });
migrationBuilder.CreateIndex(
name: "IX_AbpEntityChanges_AuditLogId",
table: "AbpEntityChanges",
@ -738,6 +748,23 @@ namespace MyCompanyName.MyProjectName.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -909,15 +936,18 @@ namespace MyCompanyName.MyProjectName.Migrations
migrationBuilder.DropTable(
name: "AbpAuditLogActions");
migrationBuilder.DropTable(
name: "AbpBackgroundJobs");
migrationBuilder.DropTable(
name: "AbpClaimTypes");
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

91
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220825013015_Initial.Designer.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220909024110_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20220825013015_Initial")]
[Migration("20220909024110_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,95 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

59
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220825013015_Initial.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20220909024110_Initial.cs

@ -63,6 +63,42 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -579,6 +615,23 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -726,6 +779,12 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

91
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220825013032_Initial.Designer.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220909024111_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20220825013032_Initial")]
[Migration("20220909024111_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -288,6 +288,95 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

59
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220825013032_Initial.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20220909024111_Initial.cs

@ -63,6 +63,42 @@ namespace MyCompanyName.MyProjectName.Migrations
table.PrimaryKey("PK_AbpClaimTypes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureGroups",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ParentName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
DefaultValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
IsVisibleToClients = table.Column<bool>(type: "bit", nullable: false),
IsAvailableToHost = table.Column<bool>(type: "bit", nullable: false),
AllowedProviders = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ValueType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpFeatures", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpFeatureValues",
columns: table => new
@ -579,6 +615,23 @@ namespace MyCompanyName.MyProjectName.Migrations
table: "AbpEntityPropertyChanges",
column: "EntityChangeId");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureGroups_Name",
table: "AbpFeatureGroups",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_GroupName",
table: "AbpFeatures",
column: "GroupName");
migrationBuilder.CreateIndex(
name: "IX_AbpFeatures_Name",
table: "AbpFeatures",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey",
table: "AbpFeatureValues",
@ -726,6 +779,12 @@ namespace MyCompanyName.MyProjectName.Migrations
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpFeatureGroups");
migrationBuilder.DropTable(
name: "AbpFeatures");
migrationBuilder.DropTable(
name: "AbpFeatureValues");

89
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

@ -286,6 +286,95 @@ namespace MyCompanyName.MyProjectName.Migrations
b.ToTable("AbpEntityPropertyChanges", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("AllowedProviders")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DefaultValue")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("GroupName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("IsAvailableToHost")
.HasColumnType("bit");
b.Property<bool>("IsVisibleToClients")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ParentName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ValueType")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("GroupName");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatures", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpFeatureGroups", (string)null);
});
modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b =>
{
b.Property<Guid>("Id")

Loading…
Cancel
Save