Browse Source

Merge pull request #24838 from abpframework/MvcCachedApplicationConfigurationClient-Concurrency

Concurrently retrieving configuration/localization info in `MvcCachedApplicationConfigurationClient`.
pull/24847/head
Engincan VESKE 8 months ago
committed by GitHub
parent
commit
8c895d2711
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      framework/Volo.Abp.slnx
  2. 33
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
  3. 17
      framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo.Abp.AspNetCore.Mvc.Client.Tests.csproj
  4. 11
      framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientTestBase.cs
  5. 17
      framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientTestModule.cs
  6. 105
      framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient_Tests.cs

1
framework/Volo.Abp.slnx

@ -181,6 +181,7 @@
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo.Abp.AspNetCore.Mvc.Client.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj" />

33
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
@ -7,6 +8,7 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Threading;
using Volo.Abp.Users;
@ -82,28 +84,47 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
return configuration;
}
private async Task<ApplicationConfigurationDto> GetRemoteConfigurationAsync()
protected virtual async Task<ApplicationConfigurationDto> GetRemoteConfigurationAsync()
{
var config = await ApplicationConfigurationAppService.GetAsync(
var cultureName = CultureInfo.CurrentUICulture.Name;
var configTask = ApplicationConfigurationAppService.GetAsync(
new ApplicationConfigurationRequestOptions
{
IncludeLocalizationResources = false
}
);
var localizationDto = await ApplicationLocalizationClientProxy.GetAsync(
new ApplicationLocalizationRequestDto {
CultureName = config.Localization.CurrentCulture.Name,
var localizationTask = ApplicationLocalizationClientProxy.GetAsync(
new ApplicationLocalizationRequestDto
{
CultureName = cultureName,
OnlyDynamics = true
}
);
await Task.WhenAll(configTask, localizationTask);
var config = configTask.Result;
var localizationDto = localizationTask.Result;
if (!CultureHelper.IsCompatibleCulture(config.Localization.CurrentCulture.Name, cultureName))
{
localizationDto = await ApplicationLocalizationClientProxy.GetAsync(
new ApplicationLocalizationRequestDto
{
CultureName = config.Localization.CurrentCulture.Name,
OnlyDynamics = true
}
);
}
config.Localization.Resources = localizationDto.Resources;
return config;
}
public ApplicationConfigurationDto Get()
public virtual ApplicationConfigurationDto Get()
{
string? cacheKey = null;
var httpContext = HttpContextAccessor?.HttpContext;

17
framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo.Abp.AspNetCore.Mvc.Client.Tests.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Mvc.Client\Volo.Abp.AspNetCore.Mvc.Client.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
</Project>

11
framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientTestBase.cs

@ -0,0 +1,11 @@
using Volo.Abp.Testing;
namespace Volo.Abp.AspNetCore.Mvc.Client;
public abstract class AbpAspNetCoreMvcClientTestBase : AbpIntegratedTest<AbpAspNetCoreMvcClientTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}

17
framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientTestModule.cs

@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Client;
[DependsOn(
typeof(AbpAspNetCoreMvcClientModule),
typeof(AbpAutofacModule)
)]
public class AbpAspNetCoreMvcClientTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpContextAccessor();
}
}

105
framework/test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient_Tests.cs

@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NSubstitute;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies;
using Volo.Abp.Localization;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Client;
public class MvcCachedApplicationConfigurationClient_Tests : AbpAspNetCoreMvcClientTestBase
{
private AbpApplicationConfigurationClientProxy _configProxy;
private AbpApplicationLocalizationClientProxy _localizationProxy;
private readonly ICachedApplicationConfigurationClient _applicationConfigurationClient;
public MvcCachedApplicationConfigurationClient_Tests()
{
_applicationConfigurationClient = GetRequiredService<ICachedApplicationConfigurationClient>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
_configProxy = Substitute.For<AbpApplicationConfigurationClientProxy>();
_localizationProxy = Substitute.For<AbpApplicationLocalizationClientProxy>();
services.Replace(ServiceDescriptor.Transient(_ => _configProxy));
services.Replace(ServiceDescriptor.Transient(_ => _localizationProxy));
}
[Fact]
public async Task Should_Use_CurrentUICulture_For_Localization_Request()
{
var cultureName = "en";
using (CultureHelper.Use(cultureName))
{
var configTcs = new TaskCompletionSource<ApplicationConfigurationDto>();
_configProxy.GetAsync(Arg.Any<ApplicationConfigurationRequestOptions>()).Returns(configTcs.Task);
var expectedResources = new Dictionary<string, ApplicationLocalizationResourceDto>
{
["TestResource"] = new()
};
_localizationProxy.GetAsync(Arg.Any<ApplicationLocalizationRequestDto>()).Returns(new ApplicationLocalizationDto { Resources = expectedResources });
var resultTask = _applicationConfigurationClient.GetAsync();
// Localization request should be fired before config completes (concurrent).
await _localizationProxy.Received(1).GetAsync(Arg.Is<ApplicationLocalizationRequestDto>(x => x.CultureName == cultureName && x.OnlyDynamics == true));
// Now let config complete.
configTcs.SetResult(CreateConfigDto(cultureName));
var result = await resultTask;
result.Localization.Resources.ShouldBe(expectedResources);
await _configProxy.Received(1).GetAsync(Arg.Is<ApplicationConfigurationRequestOptions>(x => x.IncludeLocalizationResources == false));
}
}
[Fact]
public async Task Should_Refetch_Localization_When_Culture_Differs()
{
var currentCulture = "en";
var serverCulture = "tr";
using (CultureHelper.Use(currentCulture))
{
_configProxy.GetAsync(Arg.Any<ApplicationConfigurationRequestOptions>()).Returns(CreateConfigDto(serverCulture));
var wrongResources = new Dictionary<string, ApplicationLocalizationResourceDto>();
var correctResources = new Dictionary<string, ApplicationLocalizationResourceDto>
{
["TestResource"] = new()
};
_localizationProxy.GetAsync(Arg.Is<ApplicationLocalizationRequestDto>(x => x.CultureName == currentCulture)).Returns(new ApplicationLocalizationDto { Resources = wrongResources });
_localizationProxy.GetAsync(Arg.Is<ApplicationLocalizationRequestDto>(x => x.CultureName == serverCulture)).Returns(new ApplicationLocalizationDto { Resources = correctResources });
var result = await _applicationConfigurationClient.GetAsync();
result.Localization.Resources.ShouldBe(correctResources);
await _localizationProxy.Received(1).GetAsync(Arg.Is<ApplicationLocalizationRequestDto>(x => x.CultureName == currentCulture));
await _localizationProxy.Received(1).GetAsync(Arg.Is<ApplicationLocalizationRequestDto>(x => x.CultureName == serverCulture));
}
}
private static ApplicationConfigurationDto CreateConfigDto(string cultureName)
{
return new ApplicationConfigurationDto
{
Localization =
{
CurrentCulture = new CurrentCultureDto { Name = cultureName }
}
};
}
}
Loading…
Cancel
Save