Browse Source

Merge branch 'dev' into pr/24049

pull/24049/head
maliming 5 months ago
parent
commit
d7ae2b9b8f
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      common.props
  2. 2
      docs/en/Community-Articles/2025-09-02-training-campaign/post.md
  3. 31
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs
  4. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs
  5. 8
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQOptions.cs
  6. 11
      framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs
  7. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  8. 4
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs
  9. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  10. 98
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
  11. 33
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/PostConfigureAbpRabbitMqEventBusOptions.cs
  12. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs
  13. 2
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
  14. 2
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs
  15. 2
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs
  16. 2
      framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ParameterRebinder.cs
  17. 2
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs
  18. 27
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithJsonProperty.cs
  19. 21
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
  20. 98
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs
  21. 165
      framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs
  22. 5
      modules/docs/app/VoloDocs.Web/Pages/Error.cshtml.cs

4
common.props

@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>10.1.0-preview</Version>
<LeptonXVersion>5.1.0-preview</LeptonXVersion>
<Version>10.2.0-preview</Version>
<LeptonXVersion>5.2.0-preview</LeptonXVersion>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

2
docs/en/Community-Articles/2025-09-02-training-campaign/post.md

@ -1,6 +1,6 @@
# IMPROVE YOUR ABP SKILLS WITH 33% OFF LIVE TRAININGS!
We have exciting news to share\! As you know, we offer live training packages to help you improve your skills and knowledge of ABP. From September 8th to 19th, we are giving you 33% OFF our live trainings, so you can learn more about the product at a discounted price\!
We have exciting news to share\! As you know, we offer live training packages to help you improve your skills and knowledge of ABP. For a limited time, we are giving you 33% OFF our live trainings, so you can learn more about the product at a discounted price\!
#### Why Join ABP.IO Training?

31
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@ -23,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelperService<TTagHelper>
where TTagHelper : AbpDatePickerBaseTagHelper<TTagHelper>
{
protected readonly Dictionary<Type, Func<object, string>> SupportedInputTypes;
protected readonly FrozenDictionary<Type, Func<object, string>> SupportedInputTypes;
protected readonly IJsonSerializer JsonSerializer;
protected readonly IHtmlGenerator Generator;
@ -103,7 +104,7 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
return string.Empty;
}
}
};
}.ToFrozenDictionary();
}
protected virtual T? GetAttribute<T>() where T : Attribute
@ -136,7 +137,7 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
? await ProcessButtonAndGetContentAsync(context, output, "calendar", "open")
: "";
var clearButtonContent = TagHelper.ClearButton == true || (!TagHelper.ClearButton.HasValue && TagHelper.AutoUpdateInput != true)
? await ProcessButtonAndGetContentAsync(context, output, "times", "clear", visible:!TagHelper.SingleOpenAndClearButton)
? await ProcessButtonAndGetContentAsync(context, output, "times", "clear", visible: !TagHelper.SingleOpenAndClearButton)
: "";
var labelContent = await GetLabelAsHtmlAsync(context, output, TagHelperOutput);
@ -269,7 +270,7 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
{
var attrList = new TagHelperAttributeList();
if(options == null)
if (options == null)
{
return attrList;
}
@ -401,29 +402,29 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
attrList.Add("data-visible-date-format", options.VisibleDateFormat);
}
if(!options.InputDateFormat.IsNullOrEmpty())
if (!options.InputDateFormat.IsNullOrEmpty())
{
attrList.Add("data-input-date-format", options.InputDateFormat);
}
if(options.Ranges != null && options.Ranges.Any())
if (options.Ranges != null && options.Ranges.Any())
{
var ranges = options.Ranges.ToDictionary(r => r.Label, r => r.Dates);
attrList.Add("data-ranges", JsonSerializer.Serialize(ranges));
}
if(options.AlwaysShowCalendars != null)
if (options.AlwaysShowCalendars != null)
{
attrList.Add("data-always-show-calendars", options.AlwaysShowCalendars.ToString()!.ToLowerInvariant());
}
if(options.ShowCustomRangeLabel == false)
if (options.ShowCustomRangeLabel == false)
{
attrList.Add("data-show-custom-range-label", options.ShowCustomRangeLabel.ToString()!.ToLowerInvariant());
}
if(options.Options != null)
if (options.Options != null)
{
attrList.Add("data-options", JsonSerializer.Serialize(options.Options));
}
@ -443,7 +444,7 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
attrList.Add("id", options.PickerId);
}
if(!options.SingleOpenAndClearButton)
if (!options.SingleOpenAndClearButton)
{
attrList.Add("data-single-open-and-clear-button", options.SingleOpenAndClearButton.ToString().ToLowerInvariant());
}
@ -614,7 +615,8 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
{
return string.Empty;
}
var labelTagHelper = new LabelTagHelper(Generator) {
var labelTagHelper = new LabelTagHelper(Generator)
{
ViewContext = TagHelper.ViewContext,
For = modelExpression
};
@ -764,7 +766,8 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
TagHelper.Size = attribute.Size;
}
return TagHelper.Size switch {
return TagHelper.Size switch
{
AbpFormControlSize.Small => "form-control-sm",
AbpFormControlSize.Medium => "form-control-md",
AbpFormControlSize.Large => "form-control-lg",
@ -785,14 +788,14 @@ public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelp
protected virtual async Task<string> GetValidationAsHtmlByInputAsync(TagHelperContext context,
TagHelperOutput output,
[NotNull]ModelExpression @for)
[NotNull] ModelExpression @for)
{
var validationMessageTagHelper =
new ValidationMessageTagHelper(Generator) { For = @for, ViewContext = TagHelper.ViewContext };
var attributeList = new TagHelperAttributeList { { "class", "text-danger" } };
if(!output.Attributes.TryGetAttribute("name", out var nameAttribute) || nameAttribute == null || nameAttribute.Value == null)
if (!output.Attributes.TryGetAttribute("name", out var nameAttribute) || nameAttribute == null || nameAttribute.Value == null)
{
if (nameAttribute != null)
{

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
@ -8,7 +9,8 @@ namespace Volo.Abp.ObjectExtending;
public static class MvcUiObjectExtensionPropertyInfoExtensions
{
private static readonly HashSet<Type> NumberTypes = new HashSet<Type> {
private static readonly FrozenSet<Type> NumberTypes = new HashSet<Type>
{
typeof(int),
typeof(long),
typeof(byte),
@ -33,7 +35,7 @@ public static class MvcUiObjectExtensionPropertyInfoExtensions
typeof(float?),
typeof(double?),
typeof(decimal?)
};
}.ToFrozenSet();
public static string? GetInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property)
{

8
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQOptions.cs

@ -5,11 +5,11 @@ namespace Volo.Abp.BackgroundWorkers.TickerQ;
public class AbpBackgroundWorkersTickerQOptions
{
private readonly Dictionary<Type, AbpBackgroundWorkersCronTickerConfiguration> _onfigurations;
private readonly Dictionary<Type, AbpBackgroundWorkersCronTickerConfiguration> _configurations;
public AbpBackgroundWorkersTickerQOptions()
{
_onfigurations = new Dictionary<Type, AbpBackgroundWorkersCronTickerConfiguration>();
_configurations = new Dictionary<Type, AbpBackgroundWorkersCronTickerConfiguration>();
}
public void AddConfiguration<TWorker>(AbpBackgroundWorkersCronTickerConfiguration configuration)
@ -19,7 +19,7 @@ public class AbpBackgroundWorkersTickerQOptions
public void AddConfiguration(Type workerType, AbpBackgroundWorkersCronTickerConfiguration configuration)
{
_onfigurations[workerType] = configuration;
_configurations[workerType] = configuration;
}
public AbpBackgroundWorkersCronTickerConfiguration? GetConfigurationOrNull<TJob>()
@ -29,6 +29,6 @@ public class AbpBackgroundWorkersTickerQOptions
public AbpBackgroundWorkersCronTickerConfiguration? GetConfigurationOrNull(Type workerType)
{
return _onfigurations.GetValueOrDefault(workerType);
return _configurations.GetValueOrDefault(workerType);
}
}

11
framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs

@ -1,5 +1,6 @@
using Blazorise;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@ -11,7 +12,8 @@ namespace Volo.Abp.BlazoriseUI;
public static class BlazoriseUiObjectExtensionPropertyInfoExtensions
{
private static readonly HashSet<Type> NumberTypes = new HashSet<Type> {
private static readonly FrozenSet<Type> NumberTypes = new HashSet<Type>
{
typeof(int),
typeof(long),
typeof(byte),
@ -36,13 +38,14 @@ public static class BlazoriseUiObjectExtensionPropertyInfoExtensions
typeof(float?),
typeof(double?),
typeof(decimal?)
};
}.ToFrozenSet();
private static readonly HashSet<Type> TextEditSupportedAttributeTypes = new HashSet<Type> {
private static readonly FrozenSet<Type> TextEditSupportedAttributeTypes = new HashSet<Type>
{
typeof(EmailAddressAttribute),
typeof(UrlAttribute),
typeof(PhoneAttribute)
};
}.ToFrozenSet();
public static string? GetDateEditInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property)
{

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -3,14 +3,12 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
@ -28,14 +26,12 @@ public class NpmPackagesUpdater : ITransientDependency
private readonly PackageJsonFileFinder _packageJsonFileFinder;
private readonly NpmGlobalPackagesChecker _npmGlobalPackagesChecker;
private readonly Dictionary<string, string> _fileVersionStorage = new Dictionary<string, string>();
private readonly CliHttpClientFactory _cliHttpClientFactory;
private readonly Dictionary<string, string> _fileVersionStorage = [];
public NpmPackagesUpdater(
PackageJsonFileFinder packageJsonFileFinder,
NpmGlobalPackagesChecker npmGlobalPackagesChecker,
ICancellationTokenProvider cancellationTokenProvider,
CliHttpClientFactory cliHttpClientFactory,
IInstallLibsService installLibsService,
ICmdHelper cmdHelper)
{
@ -44,7 +40,6 @@ public class NpmPackagesUpdater : ITransientDependency
CancellationTokenProvider = cancellationTokenProvider;
InstallLibsService = installLibsService;
CmdHelper = cmdHelper;
_cliHttpClientFactory = cliHttpClientFactory;
Logger = NullLogger<NpmPackagesUpdater>.Instance;
}

4
framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs

@ -5,10 +5,10 @@ namespace Volo.Abp.Logging;
public class DefaultInitLoggerFactory : IInitLoggerFactory
{
private readonly Dictionary<Type, object> _cache = new Dictionary<Type, object>();
private readonly Dictionary<Type, object> _cache = [];
public virtual IInitLogger<T> Create<T>()
{
return (IInitLogger<T>)_cache.GetOrAdd(typeof(T), () => new DefaultInitLogger<T>()); ;
return (IInitLogger<T>)_cache.GetOrAdd(typeof(T), () => new DefaultInitLogger<T>());
}
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
@ -13,14 +14,14 @@ namespace Volo.Abp.Reflection;
public static class TypeHelper
{
private static readonly HashSet<Type> FloatingTypes = new HashSet<Type>
private static readonly FrozenSet<Type> FloatingTypes = new HashSet<Type>
{
typeof(float),
typeof(double),
typeof(decimal)
};
}.ToFrozenSet();
private static readonly HashSet<Type> NonNullablePrimitiveTypes = new HashSet<Type>
private static readonly FrozenSet<Type> NonNullablePrimitiveTypes = new HashSet<Type>
{
typeof(byte),
typeof(short),
@ -37,7 +38,7 @@ public static class TypeHelper
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
};
}.ToFrozenSet();
public static bool IsNonNullablePrimitiveType(Type type)
{

98
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs

@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@ -108,12 +109,17 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
}
var entityType = entity.GetType();
var entityFullName = entityType.FullName!;
if (entityEntry.Metadata.HasSharedClrType && !entityEntry.Metadata.IsOwned())
{
entityFullName = entityEntry.Metadata.Name;
}
var entityChange = new EntityChangeInfo
{
ChangeType = changeType,
EntityEntry = entityEntry,
EntityId = entityId,
EntityTypeFullName = entityType.FullName,
EntityTypeFullName = entityFullName,
PropertyChanges = GetPropertyChanges(entityEntry),
EntityTenantId = GetTenantId(entity)
};
@ -181,48 +187,102 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
foreach (var property in properties)
{
if (entityEntry.Metadata.IsMappedToJson() && property.GetJsonPropertyName() == null)
{
continue;
}
var propertyEntry = entityEntry.Property(property.Name);
if (ShouldSavePropertyHistory(propertyEntry, isCreated || isDeleted) && !IsSoftDeleted(entityEntry))
{
var propertyType = DeterminePropertyTypeFromEntry(property, propertyEntry);
propertyChanges.Add(new EntityPropertyChangeInfo
{
NewValue = isDeleted ? null : JsonSerializer.Serialize(propertyEntry.CurrentValue!).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
OriginalValue = isCreated ? null : JsonSerializer.Serialize(propertyEntry.OriginalValue!).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
PropertyName = property.Name,
PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName!
PropertyTypeFullName = propertyType.FullName!
});
}
}
if (AbpEfCoreNavigationHelper != null)
if (AbpEfCoreNavigationHelper == null)
{
return propertyChanges;
}
foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i )))
{
foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i )))
var propertyInfo = navigationEntry.Metadata.PropertyInfo;
if (propertyInfo != null &&
propertyInfo.IsDefined(typeof(DisableAuditingAttribute), true))
{
var propertyInfo = navigationEntry.Metadata.PropertyInfo;
if (propertyInfo != null &&
propertyInfo.IsDefined(typeof(DisableAuditingAttribute), true))
continue;
}
if (navigationEntry.Metadata.TargetEntityType.IsMappedToJson() && navigationEntry is ReferenceEntry referenceEntry && referenceEntry.TargetEntry != null)
{
foreach (var propertyChange in GetPropertyChanges(referenceEntry.TargetEntry))
{
continue;
propertyChange.PropertyName = $"{referenceEntry.Metadata.Name}.{propertyChange.PropertyName}";
propertyChanges.Add(propertyChange);
}
if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index))
continue;
}
if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index))
{
var abpNavigationEntry = AbpEfCoreNavigationHelper.GetNavigationEntry(entityEntry, index);
var isCollection = navigationEntry.Metadata.IsCollection;
propertyChanges.Add(new EntityPropertyChangeInfo
{
var abpNavigationEntry = AbpEfCoreNavigationHelper.GetNavigationEntry(entityEntry, index);
var isCollection = navigationEntry.Metadata.IsCollection;
propertyChanges.Add(new EntityPropertyChangeInfo
{
PropertyName = navigationEntry.Metadata.Name,
PropertyTypeFullName = navigationEntry.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName!,
OriginalValue = GetNavigationPropertyValue(abpNavigationEntry?.OriginalValue, isCollection),
NewValue = GetNavigationPropertyValue(abpNavigationEntry?.CurrentValue, isCollection)
});
}
PropertyName = navigationEntry.Metadata.Name,
PropertyTypeFullName = navigationEntry.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName!,
OriginalValue = GetNavigationPropertyValue(abpNavigationEntry?.OriginalValue, isCollection),
NewValue = GetNavigationPropertyValue(abpNavigationEntry?.CurrentValue, isCollection)
});
}
}
return propertyChanges;
}
/// <summary>
/// Determines the CLR type of a property based on its EF Core metadata and the values in the given <see cref="PropertyEntry"/>.
/// </summary>
/// <param name="property">The EF Core property metadata that provides the declared CLR type.</param>
/// <param name="propertyEntry">The property entry that contains the current and original values for the property.</param>
/// <returns>
/// The most specific CLR type inferred for the property. This is normally the property's declared CLR type (with
/// nullable wrappers removed). If the declared type is <see cref="object"/>, the type is inferred from the
/// runtime type of <see cref="PropertyEntry.CurrentValue"/> or, if that is <c>null</c>, from
/// <see cref="PropertyEntry.OriginalValue"/>. If both values are <c>null</c>, the declared CLR type
/// (which may remain <see cref="object"/>) is returned.
/// </returns>
protected virtual Type DeterminePropertyTypeFromEntry(IProperty property, PropertyEntry propertyEntry)
{
var propertyType = property.ClrType.GetFirstGenericArgumentIfNullable();
if (propertyType != typeof(object))
{
return propertyType;
}
if (propertyEntry.CurrentValue != null)
{
propertyType = propertyEntry.CurrentValue.GetType().GetFirstGenericArgumentIfNullable();
}
else if (propertyEntry.OriginalValue != null)
{
propertyType = propertyEntry.OriginalValue.GetType().GetFirstGenericArgumentIfNullable();
}
return propertyType;
}
protected virtual string? GetNavigationPropertyValue(object? entity, bool isCollection)
{
switch (entity)

33
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/PostConfigureAbpRabbitMqEventBusOptions.cs

@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
@ -5,23 +6,23 @@ namespace Volo.Abp.EventBus.RabbitMq;
public class PostConfigureAbpRabbitMqEventBusOptions : IPostConfigureOptions<AbpRabbitMqEventBusOptions>
{
private readonly HashSet<string> _uint64QueueArguments =
[
"x-delivery-limit",
"x-expires",
"x-message-ttl",
"x-max-length",
"x-max-length-bytes",
"x-quorum-initial-group-size",
"x-quorum-target-group-size",
"x-stream-filter-size-bytes",
"x-stream-max-segment-size-bytes",
];
private readonly FrozenSet<string> _uint64QueueArguments = new HashSet<string>
{
"x-delivery-limit",
"x-expires",
"x-message-ttl",
"x-max-length",
"x-max-length-bytes",
"x-quorum-initial-group-size",
"x-quorum-target-group-size",
"x-stream-filter-size-bytes",
"x-stream-max-segment-size-bytes",
}.ToFrozenSet();
private readonly HashSet<string> _boolQueueArguments =
[
"x-single-active-consumer"
];
private readonly FrozenSet<string> _boolQueueArguments = new HashSet<string>
{
"x-single-active-consumer"
}.ToFrozenSet();
public virtual void PostConfigure(string? name, AbpRabbitMqEventBusOptions options)
{

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -10,7 +11,8 @@ internal static class ProxyScriptingJsFuncHelper
{
private const string ValidJsVariableNameChars = "abcdefghijklmnopqrstuxwvyzABCDEFGHIJKLMNOPQRSTUXWVYZ0123456789_";
private static readonly HashSet<string> ReservedWords = new HashSet<string> {
private static readonly FrozenSet<string> ReservedWords = new HashSet<string>
{
"abstract",
"else",
"instanceof",
@ -71,7 +73,7 @@ internal static class ProxyScriptingJsFuncHelper
"in",
"static",
"with"
};
}.ToFrozenSet();
public static string NormalizeJsVariableName(string name, string additionalChars = "")
{

2
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs

@ -6,7 +6,7 @@ namespace Volo.Abp.Localization;
public class LocalizationResourceDictionary : Dictionary<string, LocalizationResourceBase>
{
private readonly Dictionary<Type, LocalizationResourceBase> _resourcesByTypes = new();
private readonly Dictionary<Type, LocalizationResourceBase> _resourcesByTypes = [];
public LocalizationResource Add<TResouce>(string? defaultCultureName = null)
{

2
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs

@ -9,7 +9,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb;
public class MemoryDatabaseCollection<TEntity> : IMemoryDatabaseCollection<TEntity>
where TEntity : class, IEntity
{
private readonly Dictionary<string, byte[]> _dictionary = new Dictionary<string, byte[]>();
private readonly Dictionary<string, byte[]> _dictionary = [];
private readonly IMemoryDbSerializer _memoryDbSerializer;

2
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs

@ -17,7 +17,7 @@ public class MongoModelBuilder : IMongoModelBuilder
{
private readonly Dictionary<Type, object> _entityModelBuilders;
private static readonly object SyncObj = new object();
private static readonly object SyncObj = new();
public MongoModelBuilder()
{

2
framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ParameterRebinder.cs

@ -15,7 +15,7 @@ internal class ParameterRebinder : ExpressionVisitor
internal ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
_map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
_map = map ?? [];
}
internal static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map,

2
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs

@ -59,6 +59,8 @@ public class AbpAuditingTestModule : AbpModule
"AppEntityWithValueObject",
type => type == typeof(AppEntityWithValueObject) || type == typeof(AppEntityWithValueObjectAddress))
);
options.EntityHistorySelectors.Add(new NamedTypeSelector(nameof(AppEntityWithJsonProperty), type => type == typeof(AppEntityWithJsonProperty)));
});
context.Services.AddType<Auditing_Tests.MyAuditedObject1>();

27
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithJsonProperty.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing;
namespace Volo.Abp.Auditing.App.Entities;
public class AppEntityWithJsonProperty : FullAuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public JsonPropertyObject Data { get; set; }
public int Count { get; set; }
public AppEntityWithJsonProperty()
{
}
public AppEntityWithJsonProperty(Guid id, string name) : base(id)
{
Name = name;
}
}
public class JsonPropertyObject : Dictionary<string, object>
{
}

21
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs

@ -30,6 +30,7 @@ public class AbpAuditingTestDbContext : AbpDbContext<AbpAuditingTestDbContext>
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
public DbSet<AppEntityWithNavigationsAndDisableAuditing> AppEntityWithNavigationsAndDisableAuditing { get; set; }
public DbSet<AppEntityWithJsonProperty> EntitiesWithObjectProperty { get; set; }
public AbpAuditingTestDbContext(DbContextOptions<AbpAuditingTestDbContext> options)
: base(options)
@ -56,5 +57,25 @@ public class AbpAuditingTestDbContext : AbpDbContext<AbpAuditingTestDbContext>
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
});
modelBuilder.Entity<AppEntityWithJsonProperty>(b =>
{
b.ConfigureByConvention();
b.OwnsOne(x => x.Data, b2 =>
{
b2.ToJson();
b2.Property<object>("Name")
.HasConversion<string>(
v => v.ToString(),
v => v
);
b2.Property<object>("Value")
.HasConversion<string>(
v => v.ToString(),
v => v
);
});
});
}
}

98
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs

@ -720,6 +720,104 @@ public class Auditing_Tests : AbpAuditingTestBase
x.EntityChanges[1].PropertyChanges[0].PropertyName == nameof(AppEntityWithNavigationChildManyToMany.ManyToMany) &&
x.EntityChanges[1].PropertyChanges[0].PropertyTypeFullName == typeof(List<AppEntityWithNavigations>).FullName));
#pragma warning restore 4014
}
[Fact]
public async Task Should_Write_AuditLog_For_Json_Property_Changes()
{
var entityId = Guid.NewGuid();
var repository = ServiceProvider.GetRequiredService<IBasicRepository<AppEntityWithJsonProperty, Guid>>();
using (var scope = _auditingManager.BeginScope())
{
using (var uow = _unitOfWorkManager.Begin())
{
var entity = new AppEntityWithJsonProperty(entityId, "Test Entity")
{
Data = new JsonPropertyObject()
{
{ "Name", "String Name" },
{ "Value", "String Value"}
},
Count = 10
};
await repository.InsertAsync(entity);
await uow.CompleteAsync();
await scope.SaveAsync();
}
}
#pragma warning disable 4014
AuditingStore.Received().SaveAsync(Arg.Is<AuditLogInfo>(x => x.EntityChanges.Count == 1 &&
x.EntityChanges[0].ChangeType == EntityChangeType.Created &&
x.EntityChanges[0].EntityTypeFullName == typeof(AppEntityWithJsonProperty).FullName &&
x.EntityChanges[0].PropertyChanges.Count == 4 &&
x.EntityChanges[0].PropertyChanges[0].OriginalValue == null &&
x.EntityChanges[0].PropertyChanges[0].NewValue == "10" &&
x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithJsonProperty.Count) &&
x.EntityChanges[0].PropertyChanges[0].PropertyTypeFullName == typeof(int).FullName &&
x.EntityChanges[0].PropertyChanges[1].OriginalValue == null &&
x.EntityChanges[0].PropertyChanges[1].NewValue == "\"Test Entity\"" &&
x.EntityChanges[0].PropertyChanges[1].PropertyName == nameof(AppEntityWithJsonProperty.Name) &&
x.EntityChanges[0].PropertyChanges[1].PropertyTypeFullName == typeof(string).FullName &&
x.EntityChanges[0].PropertyChanges[2].OriginalValue == null &&
x.EntityChanges[0].PropertyChanges[2].NewValue == "\"String Name\"" &&
x.EntityChanges[0].PropertyChanges[2].PropertyName == "Data.Name" &&
x.EntityChanges[0].PropertyChanges[2].PropertyTypeFullName == typeof(string).FullName &&
x.EntityChanges[0].PropertyChanges[3].OriginalValue == null &&
x.EntityChanges[0].PropertyChanges[3].NewValue == "\"String Value\"" &&
x.EntityChanges[0].PropertyChanges[3].PropertyName == "Data.Value" &&
x.EntityChanges[0].PropertyChanges[3].PropertyTypeFullName == typeof(string).FullName));
AuditingStore.ClearReceivedCalls();
#pragma warning restore 4014
using (var scope = _auditingManager.BeginScope())
{
using (var uow = _unitOfWorkManager.Begin())
{
var entity = await repository.GetAsync(entityId);
entity.Name = "Updated Test Entity";
entity.Data["Name"] = "Updated String Name";
entity.Data["Value"] = "Updated String Value";
await repository.UpdateAsync(entity);
await uow.CompleteAsync();
await scope.SaveAsync();
}
}
#pragma warning disable 4014
AuditingStore.Received().SaveAsync(Arg.Is<AuditLogInfo>(x => x.EntityChanges.Count == 1 &&
x.EntityChanges[0].ChangeType == EntityChangeType.Updated &&
x.EntityChanges[0].EntityTypeFullName == typeof(AppEntityWithJsonProperty).FullName &&
x.EntityChanges[0].PropertyChanges.Count == 3 &&
x.EntityChanges[0].PropertyChanges[0].OriginalValue == "\"Test Entity\"" &&
x.EntityChanges[0].PropertyChanges[0].NewValue == "\"Updated Test Entity\"" &&
x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithJsonProperty.Name) &&
x.EntityChanges[0].PropertyChanges[0].PropertyTypeFullName == typeof(string).FullName &&
x.EntityChanges[0].PropertyChanges[1].OriginalValue == "\"String Name\"" &&
x.EntityChanges[0].PropertyChanges[1].NewValue == "\"Updated String Name\"" &&
x.EntityChanges[0].PropertyChanges[1].PropertyName == "Data.Name" &&
x.EntityChanges[0].PropertyChanges[1].PropertyTypeFullName == typeof(string).FullName &&
x.EntityChanges[0].PropertyChanges[2].OriginalValue == "\"String Value\"" &&
x.EntityChanges[0].PropertyChanges[2].NewValue == "\"Updated String Value\"" &&
x.EntityChanges[0].PropertyChanges[2].PropertyName == "Data.Value" &&
x.EntityChanges[0].PropertyChanges[2].PropertyTypeFullName == typeof(string).FullName));
AuditingStore.ClearReceivedCalls();
#pragma warning restore 4014
}
}

165
framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs

@ -1,37 +1,38 @@
using System;
using System.Collections.Generic;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.AutoMapper;
using Volo.Abp.Localization;
using Volo.Abp.MultiLingualObjects.TestObjects;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.MultiLingualObjects;
public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingualObjectsTestModule>
{
private readonly IMultiLingualObjectManager _multiLingualObjectManager;
private readonly MultiLingualBook _book;
private readonly List<MultiLingualBook> _books;
private readonly IMapperAccessor _mapperAccessor;
private readonly Dictionary<string, string> _testTranslations = new()
{
["ar"] = "C# التعمق في",
["zh-Hans"] = "深入理解C#",
["en"] = "C# in Depth"
};
using Volo.Abp.Localization;
using Volo.Abp.MultiLingualObjects.TestObjects;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.MultiLingualObjects;
public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingualObjectsTestModule>
{
private readonly IMultiLingualObjectManager _multiLingualObjectManager;
private readonly MultiLingualBook _book;
private readonly List<MultiLingualBook> _books;
private readonly IMapperAccessor _mapperAccessor;
private readonly FrozenDictionary<string, string> _testTranslations = new Dictionary<string, string>
{
["ar"] = "C# التعمق في",
["zh-Hans"] = "深入理解C#",
["en"] = "C# in Depth"
}.ToFrozenDictionary();
public MultiLingualObjectManager_Tests()
{
public MultiLingualObjectManager_Tests()
{
_multiLingualObjectManager = ServiceProvider.GetRequiredService<IMultiLingualObjectManager>();
//Single Lookup
_book = GetTestBook("en", "zh-Hans");
//Bulk lookup
_book = GetTestBook("en", "zh-Hans");
//Bulk lookup
_books = new List<MultiLingualBook>
{
//has no translations
@ -45,14 +46,14 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingual
//arabic + english + chineese
GetTestBook("en", "ar", "zh-Hans")
};
_mapperAccessor = ServiceProvider.GetRequiredService<IMapperAccessor>();
_mapperAccessor = ServiceProvider.GetRequiredService<IMapperAccessor>();
}
MultiLingualBook GetTestBook(params string[] included)
{
var id = Guid.NewGuid();
//Single book
var res = new MultiLingualBook(id, 100);
var id = Guid.NewGuid();
//Single book
var res = new MultiLingualBook(id, 100);
foreach (var language in included)
{
res.Translations.Add(new MultiLingualBookTranslation
@ -65,45 +66,45 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingual
return res;
}
[Fact]
public async Task GetTranslationAsync()
{
using (CultureHelper.Use("en-us"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync<MultiLingualBook, MultiLingualBookTranslation>(_book);
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
}
[Fact]
public async Task GetTranslationFromListAsync()
{
using (CultureHelper.Use("en-us"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync(_book.Translations);
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
}
[Fact]
public async Task Should_Get_Specified_Language()
{
using (CultureHelper.Use("zh-Hans"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync<MultiLingualBook, MultiLingualBookTranslation>(_book, culture: "en");
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
[Fact]
public async Task GetTranslationAsync()
{
using (CultureHelper.Use("en-us"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync<MultiLingualBook, MultiLingualBookTranslation>(_book);
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
}
[Fact]
public async Task GetTranslationFromListAsync()
{
using (CultureHelper.Use("en-us"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync(_book.Translations);
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
}
[Fact]
public async Task Should_Get_Specified_Language()
{
using (CultureHelper.Use("zh-Hans"))
{
var translation = await _multiLingualObjectManager.GetTranslationAsync<MultiLingualBook, MultiLingualBookTranslation>(_book, culture: "en");
translation.ShouldNotBeNull();
translation.Name.ShouldBe(_testTranslations["en"]);
}
}
[Fact]
public async Task GetBulkTranslationsAsync()
{
using (CultureHelper.Use("en-us"))
{
[Fact]
public async Task GetBulkTranslationsAsync()
{
using (CultureHelper.Use("en-us"))
{
var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync<MultiLingualBook, MultiLingualBookTranslation>(_books);
foreach (var (entity, translation) in translations)
{
@ -117,26 +118,26 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingual
translation.ShouldBeNull();
}
}
}
}
[Fact]
public async Task GetBulkTranslationsFromListAsync()
}
}
[Fact]
public async Task GetBulkTranslationsFromListAsync()
{
using (CultureHelper.Use("en-us"))
using (CultureHelper.Use("en-us"))
{
var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync(_books.Select(x => x.Translations));
foreach (var translation in translations)
{
translation?.Name.ShouldBe(_testTranslations["en"]);
}
}
}
[Fact]
}
}
[Fact]
public async Task TestBulkMapping()
{
using (CultureHelper.Use("en-us"))
using (CultureHelper.Use("en-us"))
{
var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync<MultiLingualBook, MultiLingualBookTranslation>(_books);
var translationsDict = translations.ToDictionary(x => x.entity.Id, x => x.translation);
@ -152,5 +153,5 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest<AbpMultiLingual
Assert.Equal(og.Translations.FirstOrDefault(x => x.Language == "en")?.Name, m.Name);
}
}
}
}
}
}

5
modules/docs/app/VoloDocs.Web/Pages/Error.cshtml.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Diagnostics;
@ -48,7 +49,7 @@ namespace VoloDocs.Web.Pages
#region Error Messages
/*For more ASCII arts http://patorjk.com/software/taag/#p=display&h=0&f=Big&t=400*/
private readonly Dictionary<int, string> _errorMessages = new Dictionary<int, string>
private readonly FrozenDictionary<int, string> _errorMessages = new Dictionary<int, string>
{
{
400, @"
@ -131,7 +132,7 @@ Ooops! Our server is experiencing a mild case of the hiccups."
Looks like we're having some server issues."
}
};
}.ToFrozenDictionary();
#endregion
}
}
Loading…
Cancel
Save