Browse Source

Merge pull request #26128 from abpframework/auto-merge-forward/rel-10.7-to-dev-28

Auto-merge forward rel-10.7 → dev
pull/26126/merge
Volosoft Agent 23 hours ago
committed by GitHub
parent
commit
dd480d88cd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs
  2. 34
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/EnumDataTypeAttributeAdapter.cs
  3. 18
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/ChildUnitOfWork.cs
  4. 72
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider_Tests.cs
  5. 68
      framework/test/Volo.Abp.Uow.Tests/Volo/Abp/Uow/UnitOfWork_Child_Events_Tests.cs

5
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs

@ -33,6 +33,11 @@ public class AbpValidationAttributeAdapterProvider : IValidationAttributeAdapter
return new DynamicRangeAttributeAdapter((DynamicRangeAttribute)attribute, stringLocalizer);
}
if (type == typeof(EnumDataTypeAttribute))
{
return new EnumDataTypeAttributeAdapter((EnumDataTypeAttribute)attribute, stringLocalizer);
}
return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer);
}
}

34
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/EnumDataTypeAttributeAdapter.cs

@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations;
public class EnumDataTypeAttributeAdapter : AttributeAdapterBase<EnumDataTypeAttribute>
{
public EnumDataTypeAttributeAdapter(
EnumDataTypeAttribute attribute,
IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
Check.NotNull(context, nameof(context));
//There is no built-in client side validation rule for enum values.
//This adapter is used to localize the error message on the server side.
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
Check.NotNull(validationContext, nameof(validationContext));
return GetErrorMessage(
validationContext.ModelMetadata,
validationContext.ModelMetadata.GetDisplayName()
);
}
}

18
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/ChildUnitOfWork.cs

@ -22,8 +22,19 @@ internal class ChildUnitOfWork : IUnitOfWork
public string? ReservationName => _parent.ReservationName;
public event EventHandler<UnitOfWorkFailedEventArgs> Failed = default!;
public event EventHandler<UnitOfWorkEventArgs> Disposed = default!;
// Forwarded directly to the parent, so a child does not have to subscribe
// (and leak) a handler on the parent for its own lifetime.
public event EventHandler<UnitOfWorkFailedEventArgs> Failed
{
add => _parent.Failed += value;
remove => _parent.Failed -= value;
}
public event EventHandler<UnitOfWorkEventArgs> Disposed
{
add => _parent.Disposed += value;
remove => _parent.Disposed -= value;
}
public IServiceProvider ServiceProvider => _parent.ServiceProvider;
@ -38,9 +49,6 @@ internal class ChildUnitOfWork : IUnitOfWork
_parent = parent;
_parent.IncrementActiveChildUnitOfWorkCount();
_parent.Failed += (sender, args) => { Failed.InvokeSafely(sender!, args); };
_parent.Disposed += (sender, args) => { Disposed.InvokeSafely(sender!, args); };
}
public void SetOuter(IUnitOfWork? outer)

72
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider_Tests.cs

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Localization;
using Shouldly;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations;
public class AbpValidationAttributeAdapterProvider_Tests
{
private readonly AbpValidationAttributeAdapterProvider _provider = new(new ValidationAttributeAdapterProvider());
[Fact]
public void Should_Return_An_Adapter_For_The_EnumDataTypeAttribute()
{
//ASP.NET Core does not provide an adapter for the EnumDataTypeAttribute.
new ValidationAttributeAdapterProvider()
.GetAttributeAdapter(new EnumDataTypeAttribute(typeof(MyEnum)), null)
.ShouldBeNull();
_provider.GetAttributeAdapter(new EnumDataTypeAttribute(typeof(MyEnum)), null)
.ShouldBeOfType<EnumDataTypeAttributeAdapter>();
}
[Fact]
public void Should_Localize_The_Error_Message_Of_The_EnumDataTypeAttribute()
{
var attribute = new EnumDataTypeAttribute(typeof(MyEnum)) { ErrorMessage = "MyEnumIsInvalid" };
var adapter = _provider.GetAttributeAdapter(attribute, new TestStringLocalizer())!;
adapter.GetErrorMessage(CreateValidationContext()).ShouldBe("Localized:MyEnumIsInvalid");
}
private static ModelValidationContextBase CreateValidationContext()
{
var metadataProvider = new EmptyModelMetadataProvider();
return new ClientModelValidationContext(
new ActionContext(),
metadataProvider.GetMetadataForProperty(typeof(MyModel), nameof(MyModel.Value)),
metadataProvider,
new Dictionary<string, string>()
);
}
public enum MyEnum
{
Value1 = 1
}
public class MyModel
{
public MyEnum Value { get; set; }
}
private class TestStringLocalizer : IStringLocalizer
{
public LocalizedString this[string name] => new(name, "Localized:" + name);
public LocalizedString this[string name, params object[] arguments] => new(name, "Localized:" + name);
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
return new List<LocalizedString>();
}
}
}

68
framework/test/Volo.Abp.Uow.Tests/Volo/Abp/Uow/UnitOfWork_Child_Events_Tests.cs

@ -0,0 +1,68 @@
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Uow;
public class UnitOfWork_Child_Events_Tests : AbpIntegratedTest<AbpUnitOfWorkModule>
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public UnitOfWork_Child_Events_Tests()
{
_unitOfWorkManager = ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
}
[Fact]
public void Child_UnitOfWorks_Should_Not_Accumulate_Event_Handlers_On_The_Parent()
{
using (var parentUow = _unitOfWorkManager.Begin())
{
var failedHandlerCount = GetEventHandlerCount(parentUow, nameof(IUnitOfWork.Failed));
var disposedHandlerCount = GetEventHandlerCount(parentUow, nameof(IUnitOfWork.Disposed));
for (var i = 0; i < 100; i++)
{
using (var childUow = _unitOfWorkManager.Begin())
{
childUow.Id.ShouldBe(parentUow.Id); //It's a child of the parent UOW.
}
}
//Disposed child UOWs should not leave any handler behind on the parent.
GetEventHandlerCount(parentUow, nameof(IUnitOfWork.Failed)).ShouldBe(failedHandlerCount);
GetEventHandlerCount(parentUow, nameof(IUnitOfWork.Disposed)).ShouldBe(disposedHandlerCount);
}
}
[Fact]
public void Should_Trigger_Disposed_Event_Subscribed_Over_A_Child_UnitOfWork()
{
var disposed = false;
using (var parentUow = _unitOfWorkManager.Begin())
{
using (var childUow = _unitOfWorkManager.Begin())
{
childUow.Disposed += (sender, args) => disposed = true;
}
disposed.ShouldBeFalse(); //The parent UOW has not been disposed yet!
}
disposed.ShouldBeTrue();
}
private static int GetEventHandlerCount(IUnitOfWork unitOfWork, string eventName)
{
var field = unitOfWork.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
field.ShouldNotBeNull();
return field.GetValue(unitOfWork) is Delegate handler
? handler.GetInvocationList().Length
: 0;
}
}
Loading…
Cancel
Save