diff --git a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperMultiLingualDtoExtensions.cs b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperMultiLingualDtoExtensions.cs new file mode 100644 index 0000000000..83cb2b35b9 --- /dev/null +++ b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperMultiLingualDtoExtensions.cs @@ -0,0 +1,54 @@ +using Volo.Abp.MultiLingualObject; +using Volo.Abp.Threading; + +namespace AutoMapper +{ + public static class AbpAutoMapperMultiLingualDtoExtensions + { + public static CreateMultiLingualMapResult CreateMultiLingualMap(this Profile profile) + where TTranslation : class, IMultiLingualTranslation + where TSource : IHasMultiLingual + { + + return new( + profile.CreateMap().BeforeMap>(), + profile.CreateMap()); + } + } + + public class AbpMultiLingualMapperAction : IMappingAction + where TTranslation : class, IMultiLingualTranslation + where TSource : IHasMultiLingual + { + private readonly IMultiLingualObjectManager _multiLingualObjectManager; + + public AbpMultiLingualMapperAction(IMultiLingualObjectManager multiLingualObjectManager) + { + _multiLingualObjectManager = multiLingualObjectManager; + } + + public void Process(TSource source, TDestination destination, ResolutionContext context) + { + var translation = AsyncHelper.RunSync(() => _multiLingualObjectManager.GetTranslationAsync(source)); + if (translation != null) + { + context.Mapper.Map(translation, destination); + } + } + } + + public class CreateMultiLingualMapResult + { + public IMappingExpression EntityMap { get; } + + public IMappingExpression TranslateMap { get; } + + public CreateMultiLingualMapResult( + IMappingExpression entityMap, + IMappingExpression translateMap) + { + EntityMap = entityMap; + TranslateMap = translateMap; + } + } +} diff --git a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj index 9f028d31ee..4a5f44f937 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj +++ b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj @@ -16,6 +16,7 @@ + diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs index 25707e3899..48a88867a8 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs +++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs @@ -25,8 +25,10 @@ namespace Volo.Abp.AutoMapper { context.Services.AddAutoMapperObjectMapper(); - context.Services.AddSingleton(provider => CreateMappings(provider)); + context.Services.AddSingleton(CreateMappings); context.Services.AddSingleton(provider => provider.GetRequiredService()); + + context.Services.AddTransient(typeof(AbpMultiLingualMapperAction<,,>)); } private MapperAccessor CreateMappings(IServiceProvider serviceProvider) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj index 87879334af..e676f24313 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj @@ -20,6 +20,7 @@ + diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs index 523a6ee4f2..0ebbe859bd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs @@ -6,6 +6,7 @@ using Volo.Abp.EventBus; using Volo.Abp.ExceptionHandling; using Volo.Abp.Guids; using Volo.Abp.Modularity; +using Volo.Abp.MultiLingualObject; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Specifications; @@ -25,6 +26,7 @@ namespace Volo.Abp.Domain typeof(AbpTimingModule), typeof(AbpUnitOfWorkModule), typeof(AbpObjectMappingModule), + typeof(AbpMultiLingualObjectModule), typeof(AbpExceptionHandlingModule), typeof(AbpSpecificationsModule) )] diff --git a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualObjectManager.cs b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualObjectManager.cs index 5180588411..e6ddc840e5 100644 --- a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualObjectManager.cs +++ b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualObjectManager.cs @@ -4,8 +4,17 @@ namespace Volo.Abp.MultiLingualObject { public interface IMultiLingualObjectManager { - Task GetTranslationAsync(TMultiLingual multiLingual, - bool fallbackToParentCultures = true, string culture = null) + TTranslation GetTranslation( + TMultiLingual multiLingual, + bool fallbackToParentCultures = true, + string culture = null) + where TMultiLingual : IHasMultiLingual + where TTranslation : class, IMultiLingualTranslation; + + Task GetTranslationAsync( + TMultiLingual multiLingual, + bool fallbackToParentCultures = true, + string culture = null) where TMultiLingual : IHasMultiLingual where TTranslation : class, IMultiLingualTranslation; } diff --git a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualTranslation.cs b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualTranslation.cs index 4490f569fe..3676e08b44 100644 --- a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualTranslation.cs +++ b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/IMultiLingualTranslation.cs @@ -4,12 +4,4 @@ { string Language { get; set; } } - - public interface IMultiLingualTranslation : IMultiLingualTranslation - where T : class - { - T Core { get; set; } - - TPrimaryKeyOfMultiLingualObject CoreId { get; set; } - } } diff --git a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/MultiLingualObjectManager.cs b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/MultiLingualObjectManager.cs index 68fc083e21..dc6a82a28d 100644 --- a/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/MultiLingualObjectManager.cs +++ b/framework/src/Volo.Abp.MultiLingualObject/Volo/Abp/MultiLingualObject/MultiLingualObjectManager.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Localization; using Volo.Abp.Settings; +using Volo.Abp.Threading; namespace Volo.Abp.MultiLingualObject { @@ -20,7 +21,19 @@ namespace Volo.Abp.MultiLingualObject SettingProvider = settingProvider; } - public virtual async Task GetTranslationAsync(TMultiLingual multiLingual, + public TTranslation GetTranslation( + TMultiLingual multiLingual, + bool fallbackToParentCultures = true, + string culture = null) + where TMultiLingual : IHasMultiLingual + where TTranslation : class, IMultiLingualTranslation + { + return AsyncHelper.RunSync(() => + GetTranslationAsync(multiLingual, fallbackToParentCultures, culture)); + } + + public virtual async Task GetTranslationAsync( + TMultiLingual multiLingual, bool fallbackToParentCultures = true, string culture = null) where TMultiLingual : IHasMultiLingual @@ -33,8 +46,7 @@ namespace Volo.Abp.MultiLingualObject return null; } - var translation = - multiLingual.Translations.FirstOrDefault(pt => pt.Language == culture); + var translation = multiLingual.Translations.FirstOrDefault(pt => pt.Language == culture); if (translation != null) { return translation; diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/AutoMapper/AbpAutoMapperMultiLingualDto_Tests.cs b/framework/test/Volo.Abp.AutoMapper.Tests/AutoMapper/AbpAutoMapperMultiLingualDto_Tests.cs index ea59a35ce5..0efb72625b 100644 --- a/framework/test/Volo.Abp.AutoMapper.Tests/AutoMapper/AbpAutoMapperMultiLingualDto_Tests.cs +++ b/framework/test/Volo.Abp.AutoMapper.Tests/AutoMapper/AbpAutoMapperMultiLingualDto_Tests.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp; using Volo.Abp.AutoMapper; using Volo.Abp.Localization; -using Volo.Abp.MultiLingualObject; using Volo.Abp.MultiLingualObject.TestObjects; using Volo.Abp.Testing; using Xunit; @@ -16,12 +14,10 @@ namespace AutoMapper public class AbpAutoMapperMultiLingualDto_Tests : AbpIntegratedTest { private readonly Volo.Abp.ObjectMapping.IObjectMapper _objectMapper; - private readonly IMultiLingualObjectManager _multiLingualObjectManager; private readonly MultiLingualBook _book; public AbpAutoMapperMultiLingualDto_Tests() { - _multiLingualObjectManager = ServiceProvider.GetRequiredService(); _objectMapper = ServiceProvider.GetRequiredService(); var id = Guid.NewGuid(); @@ -32,17 +28,13 @@ namespace AutoMapper var en = new MultiLingualBookTranslation { - CoreId = id, Language = "en", Name = "C# in Depth", - Core = _book }; var zh = new MultiLingualBookTranslation { - CoreId = id, Language = "zh-Hans", Name = "深入理解C#", - Core = _book }; _book.Translations.Add(en); @@ -55,15 +47,11 @@ namespace AutoMapper } [Fact] - public async Task Should_Map_Current_UI_Culture() + public void Should_Map_Current_UI_Culture() { using (CultureHelper.Use("zh-Hans")) { - var translation = - await _multiLingualObjectManager - .GetTranslationAsync(_book); - - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("深入理解C#"); bookDto.Price.ShouldBe(_book.Price); @@ -72,15 +60,11 @@ namespace AutoMapper } [Fact] - public async Task Should_Map_Fallback_UI_Culture() + public void Should_Map_Fallback_UI_Culture() { using (CultureHelper.Use("en-us")) { - var translation = - await _multiLingualObjectManager - .GetTranslationAsync(_book); - - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("C# in Depth"); bookDto.Price.ShouldBe(_book.Price); @@ -89,15 +73,11 @@ namespace AutoMapper } [Fact] - public async Task Should_Map_Default_Language() + public void Should_Map_Default_Language() { using (CultureHelper.Use("tr")) { - var translation = - await _multiLingualObjectManager - .GetTranslationAsync(_book); - - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("C# in Depth"); bookDto.Price.ShouldBe(_book.Price); @@ -106,14 +86,15 @@ namespace AutoMapper } [Fact] - public async Task Should_Map_Specified_Language() + public void NoTranslations_ShouldStillMapObject() { - using (CultureHelper.Use("zh-Hans")) + _book.Translations.Clear(); + + using (CultureHelper.Use("tr")) { - var translation = await _multiLingualObjectManager.GetTranslationAsync(_book, culture:"en"); - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); - bookDto.Name.ShouldBe("C# in Depth"); + bookDto.Name.ShouldBeNull(); bookDto.Price.ShouldBe(_book.Price); bookDto.Id.ShouldBe(_book.Id); } @@ -124,9 +105,10 @@ namespace AutoMapper { public BookProfile() { - CreateMap() - .ForMember(d => d.Price, m => m.MapFrom(s => s.Core.Price)) - .ForMember(d => d.Id, m => m.MapFrom(s => s.CoreId)); + var mapResult = this.CreateMultiLingualMap(); + + mapResult.EntityMap.ValidateMemberList(MemberList.None); + mapResult.TranslateMap.ValidateMemberList(MemberList.None); } } } diff --git a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/AbpMultiLingualObjectTestModule.cs b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/AbpMultiLingualObjectTestModule.cs index c4fef4dc66..f8f79a3065 100644 --- a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/AbpMultiLingualObjectTestModule.cs +++ b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/AbpMultiLingualObjectTestModule.cs @@ -17,9 +17,5 @@ namespace Volo.Abp.MultiLingualObject )] public class AbpMultiLingualObjectTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - base.ConfigureServices(context); - } } } diff --git a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObjectManager_Tests.cs b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObjectManager_Tests.cs new file mode 100644 index 0000000000..269bf91b54 --- /dev/null +++ b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObjectManager_Tests.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Localization; +using Volo.Abp.MultiLingualObject.TestObjects; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.MultiLingualObject +{ + public class MultiLingualObjectManager_Tests : AbpIntegratedTest + { + private readonly IMultiLingualObjectManager _multiLingualObjectManager; + private readonly MultiLingualBook _book; + + public MultiLingualObjectManager_Tests() + { + _multiLingualObjectManager = ServiceProvider.GetRequiredService(); + + var id = Guid.NewGuid(); + _book = new MultiLingualBook(id, 100) + { + Translations = new List() + }; + + var en = new MultiLingualBookTranslation + { + Language = "en", + Name = "C# in Depth", + }; + var zh = new MultiLingualBookTranslation + { + Language = "zh-Hans", + Name = "深入理解C#", + }; + + _book.Translations.Add(en); + _book.Translations.Add(zh); + } + + [Fact] + public void GetTranslation() + { + using (CultureHelper.Use("en-us")) + { + var translation = _multiLingualObjectManager.GetTranslation(_book); + + translation.Name.ShouldBe("C# in Depth"); + } + } + + [Fact] + public async Task GetTranslationAsync() + { + using (CultureHelper.Use("en-us")) + { + var translation = await _multiLingualObjectManager.GetTranslationAsync(_book); + + translation.Name.ShouldBe("C# in Depth"); + } + } + + [Fact] + public async Task Should_Get_Specified_Language() + { + using (CultureHelper.Use("zh-Hans")) + { + var translation = await _multiLingualObjectManager.GetTranslationAsync(_book, culture: "en"); + + translation.Name.ShouldBe("C# in Depth"); + } + } + } +} diff --git a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObject_Tests.cs b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObject_Tests.cs index f52eb17c19..917a11a2a8 100644 --- a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObject_Tests.cs +++ b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/MultiLingualObject_Tests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.DependencyInjection; @@ -15,13 +14,11 @@ namespace Volo.Abp.MultiLingualObject public class MultiLingualObject_Tests : AbpIntegratedTest { private readonly IObjectMapper _objectMapper; - private readonly IMultiLingualObjectManager _multiLingualObjectManager; private readonly MultiLingualBook _book; public MultiLingualObject_Tests() { _objectMapper = ServiceProvider.GetRequiredService(); - _multiLingualObjectManager = ServiceProvider.GetRequiredService(); var id = Guid.NewGuid(); _book = new MultiLingualBook(id, 100) @@ -31,17 +28,13 @@ namespace Volo.Abp.MultiLingualObject var en = new MultiLingualBookTranslation { - CoreId = id, Language = "en", Name = "C# in Depth", - Core = _book }; var zh = new MultiLingualBookTranslation { - CoreId = id, Language = "zh-Hans", Name = "深入理解C#", - Core = _book }; _book.Translations.Add(en); @@ -54,12 +47,11 @@ namespace Volo.Abp.MultiLingualObject } [Fact] - public async Task Should_Map_Current_UI_Culture() + public void Should_Map_Current_UI_Culture() { using (CultureHelper.Use("zh-Hans")) { - var translation = await _multiLingualObjectManager.GetTranslationAsync(_book); - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("深入理解C#"); bookDto.Price.ShouldBe(_book.Price); @@ -68,12 +60,11 @@ namespace Volo.Abp.MultiLingualObject } [Fact] - public async Task Should_Map_Fallback_UI_Culture() + public void Should_Map_Fallback_UI_Culture() { using (CultureHelper.Use("en-us")) { - var translation = await _multiLingualObjectManager.GetTranslationAsync(_book); - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("C# in Depth"); bookDto.Price.ShouldBe(_book.Price); @@ -82,12 +73,11 @@ namespace Volo.Abp.MultiLingualObject } [Fact] - public async Task Should_Map_Default_Language() + public void Should_Map_Default_Language() { using (CultureHelper.Use("tr")) { - var translation = await _multiLingualObjectManager.GetTranslationAsync(_book); - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); bookDto.Name.ShouldBe("C# in Depth"); bookDto.Price.ShouldBe(_book.Price); @@ -96,35 +86,45 @@ namespace Volo.Abp.MultiLingualObject } [Fact] - public async Task Should_Map_Specified_Language() + public void NoTranslations_ShouldStillMapObject() { - using (CultureHelper.Use("zh-Hans")) + _book.Translations.Clear(); + + using (CultureHelper.Use("tr")) { - var translation = await _multiLingualObjectManager.GetTranslationAsync(_book, culture:"en"); - var bookDto = _objectMapper.Map(translation); + var bookDto = _objectMapper.Map(_book); - bookDto.Name.ShouldBe("C# in Depth"); + bookDto.Name.ShouldBeNull(); bookDto.Price.ShouldBe(_book.Price); bookDto.Id.ShouldBe(_book.Id); } } } - public class MultiLingualBookObjectMapper : IObjectMapper, + public class MultiLingualBookObjectMapper : IObjectMapper, ITransientDependency { + private readonly IMultiLingualObjectManager _multiLingualObjectManager; - public MultiLingualBookDto Map(MultiLingualBookTranslation source) + public MultiLingualBookObjectMapper(IMultiLingualObjectManager multiLingualObjectManager) { + _multiLingualObjectManager = multiLingualObjectManager; + } + + public MultiLingualBookDto Map(MultiLingualBook source) + { + var translation = + _multiLingualObjectManager.GetTranslation(source); + return new MultiLingualBookDto { - Price = source.Core.Price, - Id = source.CoreId, - Name = source.Name + Price = source.Price, + Id = source.Id, + Name = translation?.Name }; } - public MultiLingualBookDto Map(MultiLingualBookTranslation source, MultiLingualBookDto destination) + public MultiLingualBookDto Map(MultiLingualBook source, MultiLingualBookDto destination) { return default; } diff --git a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/TestObjects/MultiLingualBookTranslation.cs b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/TestObjects/MultiLingualBookTranslation.cs index 26c3c13d84..5f35554078 100644 --- a/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/TestObjects/MultiLingualBookTranslation.cs +++ b/framework/test/Volo.Abp.MultiLingualObject.Tests/Volo/Abp/MultiLingualObject/TestObjects/MultiLingualBookTranslation.cs @@ -2,14 +2,10 @@ namespace Volo.Abp.MultiLingualObject.TestObjects { - public class MultiLingualBookTranslation : IMultiLingualTranslation + public class MultiLingualBookTranslation : IMultiLingualTranslation { public string Name { get; set; } public string Language { get; set; } - - public MultiLingualBook Core { get; set; } - - public Guid CoreId { get; set; } } }