From 15a8f2224722260928912b6bd076f248ebab0ee2 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Fri, 16 Jan 2026 19:06:04 +0700 Subject: [PATCH] fix: restore netstandard2.0 support for Mapster Fixes #639 Includes packaging changes to ship a netstandard2.0 assembly again, plus test/CI updates to keep net48 tests running only on Windows. --- .../Benchmark.Development.csproj | 2 +- src/Benchmark/Benchmark.csproj | 6 +-- src/Directory.Build.props | 2 +- .../ExpressionDebugger.csproj | 6 +-- .../ExpressionTranslator.csproj | 6 +-- src/Mapster.Async.Tests/AsyncTest.cs | 5 +-- .../Mapster.Async.Tests.csproj | 9 +++-- src/Mapster.Async/Mapster.Async.csproj | 2 +- .../InjectionTest.cs | 12 +++--- .../Mapster.DependencyInjection.Tests.csproj | 11 +++--- .../Mapster.DependencyInjection.csproj | 4 +- .../Mapster.EFCore.Tests.csproj | 8 ++-- .../Mapster.Immutable.Tests.csproj | 11 +++--- .../Mapster.Immutable.csproj | 4 +- .../Mapster.JsonNet.Tests.csproj | 9 +++-- src/Mapster.JsonNet/Mapster.JsonNet.csproj | 4 +- .../Mapster.SourceGenerator.csproj | 2 +- src/Mapster.Tests/Mapster.Tests.csproj | 21 ++++++---- .../Polyfills/CompileWithDebugInfo.net48.cs | 19 ++++++++++ .../RequiredMemberAttributes.net48.cs | 38 +++++++++++++++++++ .../TypeGetConstructorOverload.net48.cs | 14 +++++++ .../WhenExplicitMappingRequired.cs | 4 +- src/Mapster.Tests/WhenMappingToInterface.cs | 1 - .../WhenPerformingAfterMapping.cs | 2 +- .../WhenPerformingBeforeMapping.cs | 10 ++--- .../Mapster.Tool.Tests.csproj | 15 +++++--- .../Polyfills/IsExternalInit.net48.cs | 8 ++++ src/Mapster.Tool/Mapster.Tool.csproj | 2 +- src/Mapster/Adapters/BaseAdapter.cs | 8 ++-- src/Mapster/Adapters/BaseClassAdapter.cs | 13 +++---- src/Mapster/Compile/CompileArgument.cs | 3 +- src/Mapster/Interfaces/IMapFrom.cs | 8 +++- src/Mapster/Mapster.csproj | 10 ++++- src/Mapster/TypeAdapter.cs | 18 +++------ src/Mapster/Utils/CodeAnalysisAttributes.cs | 20 ++++++++++ src/Mapster/Utils/ExpressionEx.cs | 7 ++-- src/Mapster/Utils/IsExternalInit.cs | 8 ++++ src/Mapster/Utils/ReflectionUtils.cs | 10 +++-- .../Sample.AspNetCore.csproj | 2 +- src/Sample.CodeGen/Sample.CodeGen.csproj | 2 +- src/TemplateTest/TemplateTest.csproj | 11 +++--- 41 files changed, 241 insertions(+), 116 deletions(-) create mode 100644 src/Mapster.Tests/Polyfills/CompileWithDebugInfo.net48.cs create mode 100644 src/Mapster.Tests/Polyfills/RequiredMemberAttributes.net48.cs create mode 100644 src/Mapster.Tests/Polyfills/TypeGetConstructorOverload.net48.cs create mode 100644 src/Mapster.Tool.Tests/Polyfills/IsExternalInit.net48.cs create mode 100644 src/Mapster/Utils/CodeAnalysisAttributes.cs create mode 100644 src/Mapster/Utils/IsExternalInit.cs diff --git a/src/Benchmark.Development/Benchmark.Development.csproj b/src/Benchmark.Development/Benchmark.Development.csproj index 2213d7a4..1d50d2d6 100644 --- a/src/Benchmark.Development/Benchmark.Development.csproj +++ b/src/Benchmark.Development/Benchmark.Development.csproj @@ -10,7 +10,7 @@ Benchmark.Development.snk False 7.4.0 - 12.0 + 12 diff --git a/src/Benchmark/Benchmark.csproj b/src/Benchmark/Benchmark.csproj index ba4b6b9f..efffc9b7 100644 --- a/src/Benchmark/Benchmark.csproj +++ b/src/Benchmark/Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 true **/*.g.cs @@ -19,11 +19,11 @@ - + - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 151e793e..484bc49f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,6 +20,6 @@ Mapper;AutoMapper;Fast;Mapping icon.png false - 10 + 12 \ No newline at end of file diff --git a/src/ExpressionDebugger/ExpressionDebugger.csproj b/src/ExpressionDebugger/ExpressionDebugger.csproj index 59e98815..52342210 100644 --- a/src/ExpressionDebugger/ExpressionDebugger.csproj +++ b/src/ExpressionDebugger/ExpressionDebugger.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0;net48 True Chaowlert Chaisrichalermpol Step into debugging from linq expressions @@ -14,12 +14,12 @@ ExpressionDebugger.snk 10.0.0-pre01 https://github.com/chaowlert/ExpressionDebugger/blob/master/LICENSE - 8.0 + 12 enable - + diff --git a/src/ExpressionTranslator/ExpressionTranslator.csproj b/src/ExpressionTranslator/ExpressionTranslator.csproj index d2da32b5..cb68a2f1 100644 --- a/src/ExpressionTranslator/ExpressionTranslator.csproj +++ b/src/ExpressionTranslator/ExpressionTranslator.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0 + net10.0;net9.0;net8.0;net48 True Chaowlert Chaisrichalermpol Translate from linq expressions to C# code @@ -17,7 +17,7 @@ MIT icon.png - 8.0 + 12 enable @@ -25,7 +25,7 @@ - + diff --git a/src/Mapster.Async.Tests/AsyncTest.cs b/src/Mapster.Async.Tests/AsyncTest.cs index caefe5ce..5fa4777e 100644 --- a/src/Mapster.Async.Tests/AsyncTest.cs +++ b/src/Mapster.Async.Tests/AsyncTest.cs @@ -45,15 +45,14 @@ public async Task AsyncError() } } - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestMethod] public void Sync() { TypeAdapterConfig.NewConfig() .AfterMappingAsync(async dest => { dest.Name = await GetName(); }); var poco = new Poco {Id = "foo"}; - var dto = poco.Adapt(); - dto.Name.ShouldBe("bar"); + Should.Throw(() => poco.Adapt()); } [TestMethod] diff --git a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj index c031e5cf..e2ab217a 100644 --- a/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj +++ b/src/Mapster.Async.Tests/Mapster.Async.Tests.csproj @@ -1,15 +1,16 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 true false - - - + + + diff --git a/src/Mapster.Async/Mapster.Async.csproj b/src/Mapster.Async/Mapster.Async.csproj index 8c980a39..a1500c90 100644 --- a/src/Mapster.Async/Mapster.Async.csproj +++ b/src/Mapster.Async/Mapster.Async.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0; + netstandard2.0;net10.0;net9.0;net8.0 Async supports for Mapster true Mapster;Async diff --git a/src/Mapster.DependencyInjection.Tests/InjectionTest.cs b/src/Mapster.DependencyInjection.Tests/InjectionTest.cs index a1a1cb15..f8b6a70d 100644 --- a/src/Mapster.DependencyInjection.Tests/InjectionTest.cs +++ b/src/Mapster.DependencyInjection.Tests/InjectionTest.cs @@ -31,11 +31,12 @@ public void Injection() } } - [TestMethod, ExpectedException(typeof(InvalidOperationException))] + [TestMethod] public void NoServiceAdapter_InjectionError() { - var expectedValue = MapContext.Current.GetService().GetName(); - var config = ConfigureMapping(expectedValue); + var config = new TypeAdapterConfig(); + config.NewConfig() + .Map(dto => dto.Name, _ => MapContext.Current.GetService().GetName()); IServiceCollection sc = new ServiceCollection(); sc.AddScoped(); @@ -46,8 +47,9 @@ public void NoServiceAdapter_InjectionError() var sp = sc.BuildServiceProvider(); using var scope = sp.CreateScope(); - var mapper = scope.ServiceProvider.GetService(); - MapToDto(mapper, expectedValue); + var mapper = scope.ServiceProvider.GetRequiredService(); + var ex = Should.Throw(() => mapper.Map(new Poco { Id = "bar" })); + ex.Message.ShouldBe("Mapping must be called using ServiceAdapter"); } private static TypeAdapterConfig ConfigureMapping(string expectedValue) diff --git a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj index 4a3b0ed0..daf8cd82 100644 --- a/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj +++ b/src/Mapster.DependencyInjection.Tests/Mapster.DependencyInjection.Tests.csproj @@ -1,16 +1,17 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 true false - - - - + + + + diff --git a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj index 246ad210..80e974d5 100644 --- a/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj +++ b/src/Mapster.DependencyInjection/Mapster.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0; + netstandard2.0;net10.0;net9.0;net8.0 Dependency Injection supports for Mapster true Mapster;DependencyInjection @@ -13,7 +13,7 @@ - + diff --git a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj index 94549270..64e8e3f0 100644 --- a/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj +++ b/src/Mapster.EFCore.Tests/Mapster.EFCore.Tests.csproj @@ -8,10 +8,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj index 027742ff..94fca312 100644 --- a/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj +++ b/src/Mapster.Immutable.Tests/Mapster.Immutable.Tests.csproj @@ -1,16 +1,17 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 true false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Mapster.Immutable/Mapster.Immutable.csproj b/src/Mapster.Immutable/Mapster.Immutable.csproj index 1f36f685..d9daf41c 100644 --- a/src/Mapster.Immutable/Mapster.Immutable.csproj +++ b/src/Mapster.Immutable/Mapster.Immutable.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0; + netstandard2.0;net10.0;net9.0;net8.0 Immutable collection supports for Mapster true Mapster;Immutable @@ -14,7 +14,7 @@ - + diff --git a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj index b4c020b6..1d65526b 100644 --- a/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj +++ b/src/Mapster.JsonNet.Tests/Mapster.JsonNet.Tests.csproj @@ -1,15 +1,16 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 true false - - - + + + diff --git a/src/Mapster.JsonNet/Mapster.JsonNet.csproj b/src/Mapster.JsonNet/Mapster.JsonNet.csproj index 3201ca43..0494197c 100644 --- a/src/Mapster.JsonNet/Mapster.JsonNet.csproj +++ b/src/Mapster.JsonNet/Mapster.JsonNet.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0; + netstandard2.0;net10.0;net9.0;net8.0 Json.net conversion supports for Mapster true Mapster;Json.net @@ -11,7 +11,7 @@ - + diff --git a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj index b4b88947..a57b36f4 100644 --- a/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj +++ b/src/Mapster.SourceGenerator/Mapster.SourceGenerator.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj index d6b78546..34a2291e 100644 --- a/src/Mapster.Tests/Mapster.Tests.csproj +++ b/src/Mapster.Tests/Mapster.Tests.csproj @@ -1,26 +1,31 @@ - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 true false Mapster.Tests Mapster.Tests.snk true true - 11.0 + 12 - - - - + + + + - + + + + + @@ -34,4 +39,4 @@ - \ No newline at end of file + diff --git a/src/Mapster.Tests/Polyfills/CompileWithDebugInfo.net48.cs b/src/Mapster.Tests/Polyfills/CompileWithDebugInfo.net48.cs new file mode 100644 index 00000000..92302ac1 --- /dev/null +++ b/src/Mapster.Tests/Polyfills/CompileWithDebugInfo.net48.cs @@ -0,0 +1,19 @@ +#if NET48 +using System; + +namespace System.Linq.Expressions +{ + internal static class CompileWithDebugInfoPolyfill + { + public static Delegate CompileWithDebugInfo(this LambdaExpression node) + { + return node.Compile(); + } + + public static T CompileWithDebugInfo(this Expression node) + { + return node.Compile(); + } + } +} +#endif diff --git a/src/Mapster.Tests/Polyfills/RequiredMemberAttributes.net48.cs b/src/Mapster.Tests/Polyfills/RequiredMemberAttributes.net48.cs new file mode 100644 index 00000000..1d87245e --- /dev/null +++ b/src/Mapster.Tests/Polyfills/RequiredMemberAttributes.net48.cs @@ -0,0 +1,38 @@ +#if NET48 +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] + internal sealed class RequiredMemberAttribute : Attribute + { + public RequiredMemberAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] + internal sealed class CompilerFeatureRequiredAttribute : Attribute + { + public CompilerFeatureRequiredAttribute(string featureName) + { + FeatureName = featureName; + } + + public string FeatureName { get; } + + public bool IsOptional { get; set; } + } +} + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + internal sealed class SetsRequiredMembersAttribute : Attribute + { + public SetsRequiredMembersAttribute() + { + } + } +} +#endif diff --git a/src/Mapster.Tests/Polyfills/TypeGetConstructorOverload.net48.cs b/src/Mapster.Tests/Polyfills/TypeGetConstructorOverload.net48.cs new file mode 100644 index 00000000..31737b23 --- /dev/null +++ b/src/Mapster.Tests/Polyfills/TypeGetConstructorOverload.net48.cs @@ -0,0 +1,14 @@ +#if NET48 +using System; + +namespace System.Reflection +{ + internal static class TypeGetConstructorOverloadPolyfill + { + public static ConstructorInfo? GetConstructor(this Type type, BindingFlags bindingAttr, Type[] types) + { + return type.GetConstructor(bindingAttr, binder: null, types: types, modifiers: null); + } + } +} +#endif diff --git a/src/Mapster.Tests/WhenExplicitMappingRequired.cs b/src/Mapster.Tests/WhenExplicitMappingRequired.cs index 25b43474..6463ba04 100644 --- a/src/Mapster.Tests/WhenExplicitMappingRequired.cs +++ b/src/Mapster.Tests/WhenExplicitMappingRequired.cs @@ -133,12 +133,12 @@ public void Mapped_Classes_Succeed_When_List_To_IList_Is_Mapped() results.Count.ShouldBe(2); } - [TestMethod, ExpectedException(typeof(CompileException))] + [TestMethod] public void UnmappedChildPocoShouldFailed() { var config = new TypeAdapterConfig {RequireExplicitMapping = true}; var setter = config.NewConfig(); - setter.Compile(); // Should fail here + Should.Throw(() => setter.Compile()); } [TestMethod] diff --git a/src/Mapster.Tests/WhenMappingToInterface.cs b/src/Mapster.Tests/WhenMappingToInterface.cs index 00af2104..dbe7e429 100644 --- a/src/Mapster.Tests/WhenMappingToInterface.cs +++ b/src/Mapster.Tests/WhenMappingToInterface.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json.Serialization; using Newtonsoft.Json; namespace Mapster.Tests diff --git a/src/Mapster.Tests/WhenPerformingAfterMapping.cs b/src/Mapster.Tests/WhenPerformingAfterMapping.cs index 68b06a62..0996a12d 100644 --- a/src/Mapster.Tests/WhenPerformingAfterMapping.cs +++ b/src/Mapster.Tests/WhenPerformingAfterMapping.cs @@ -71,7 +71,7 @@ public void MapToType_Support_Destination_Parameter() }; // check expression is successfully compiled - Assert.ThrowsException(() => poco.Adapt()); + Should.Throw(() => poco.Adapt()); } [TestMethod] diff --git a/src/Mapster.Tests/WhenPerformingBeforeMapping.cs b/src/Mapster.Tests/WhenPerformingBeforeMapping.cs index 5c000802..63aa26a5 100644 --- a/src/Mapster.Tests/WhenPerformingBeforeMapping.cs +++ b/src/Mapster.Tests/WhenPerformingBeforeMapping.cs @@ -43,11 +43,11 @@ public void MapToType_Support_Destination_Parameter() { Id = Guid.NewGuid(), Name = "test", - }; - - // check expression is successfully compiled - Assert.ThrowsException(() => poco.Adapt()); - } + }; + + // check expression is successfully compiled + Should.Throw(() => poco.Adapt()); + } [TestMethod] public void MapToTarget_Support_Destination_Parameter() diff --git a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj index 702c02e6..8e0da2f0 100644 --- a/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj +++ b/src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj @@ -2,6 +2,7 @@ net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 enable enable true @@ -9,15 +10,16 @@ - - + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -46,7 +48,8 @@ - + + diff --git a/src/Mapster.Tool.Tests/Polyfills/IsExternalInit.net48.cs b/src/Mapster.Tool.Tests/Polyfills/IsExternalInit.net48.cs new file mode 100644 index 00000000..1b6e9e4f --- /dev/null +++ b/src/Mapster.Tool.Tests/Polyfills/IsExternalInit.net48.cs @@ -0,0 +1,8 @@ +#if NET48 +namespace System.Runtime.CompilerServices +{ + internal sealed class IsExternalInit + { + } +} +#endif diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj index 8abc6c96..80126dfe 100644 --- a/src/Mapster.Tool/Mapster.Tool.csproj +++ b/src/Mapster.Tool/Mapster.Tool.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index a3ffb648..21d03398 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -215,11 +215,11 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de } Expression? set; - var requiremembers = arg.DestinationType.GetProperties() - .Where(x => x.GetCustomAttributes() - .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))); + var hasRequiredMembers = arg.DestinationType.GetProperties() + .Any(x => x.GetCustomAttributes() + .Any(y => y.GetType().FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute")); - if (requiremembers.Count() != 0) + if (hasRequiredMembers) set = CreateInlineExpression(source, arg, true); else set = CreateInstantiationExpression(transformedSource, destination, arg); diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index a50c4b73..ebaac676 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -50,7 +50,7 @@ select fn(src, destinationMember, arg)) s.Visit(getter); - var match = arg.Settings.ProjectToTypeResolvers.GetValueOrDefault(s.MemeberName); + arg.Settings.ProjectToTypeResolvers.TryGetValue(s.MemeberName, out var match); if (match != null) { @@ -119,7 +119,7 @@ select fn(src, destinationMember, arg)) && destinationMember.Info is PropertyInfo propinfo) { if (propinfo.GetCustomAttributes() - .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))) + .Any(y => y.GetType().FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute")) { getter = destinationMember.Type.CreateDefault(); } @@ -281,11 +281,8 @@ protected virtual ClassModel GetSetterModel(CompileArgument arg) protected void IgnoreNonMapped (ClassModel classModel, CompileArgument arg) { - var notMappingToIgnore = classModel.Members - .ExceptBy(arg.Settings.Resolvers.Select(x => x.DestinationMemberName), - y => y.Name); - - foreach (var item in notMappingToIgnore) + var mappedMembers = new HashSet(arg.Settings.Resolvers.Select(x => x.DestinationMemberName)); + foreach (var item in classModel.Members.Where(x => !mappedMembers.Contains(x.Name))) { arg.Settings.Ignore.TryAdd(item.Name, new IgnoreDictionary.IgnoreItem()); } @@ -298,7 +295,7 @@ protected virtual ClassModel GetOnlyRequiredPropertySetterModel(CompileArgument Members = arg.DestinationType.GetFieldsAndProperties(true) .Where(x => x.GetType() == typeof(PropertyModel)) .Where(y => ((PropertyInfo)y.Info).GetCustomAttributes() - .Any(y => y.GetType() == typeof(System.Runtime.CompilerServices.RequiredMemberAttribute))) + .Any(y => y.GetType().FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute")) }; } diff --git a/src/Mapster/Compile/CompileArgument.cs b/src/Mapster/Compile/CompileArgument.cs index e9015449..ad40dff0 100644 --- a/src/Mapster/Compile/CompileArgument.cs +++ b/src/Mapster/Compile/CompileArgument.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Mapster.Utils; namespace Mapster { @@ -44,4 +45,4 @@ select split return _constructUsing; } } -} \ No newline at end of file +} diff --git a/src/Mapster/Interfaces/IMapFrom.cs b/src/Mapster/Interfaces/IMapFrom.cs index f1b004c7..cce85a67 100644 --- a/src/Mapster/Interfaces/IMapFrom.cs +++ b/src/Mapster/Interfaces/IMapFrom.cs @@ -2,8 +2,12 @@ namespace Mapster; public interface IMapFrom { - public void ConfigureMapping(TypeAdapterConfig config) +#if NETSTANDARD2_0 + void ConfigureMapping(TypeAdapterConfig config); +#else + void ConfigureMapping(TypeAdapterConfig config) { config.NewConfig(typeof(TSource), GetType()); } -} \ No newline at end of file +#endif +} diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj index bc6f2dd1..59935a8c 100644 --- a/src/Mapster/Mapster.csproj +++ b/src/Mapster/Mapster.csproj @@ -4,7 +4,7 @@ A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Copyright (c) 2016 Chaowlert Chaisrichalermpol, Eric Swann chaowlert;eric_swann - net10.0;net9.0;net8.0; + netstandard2.0;net10.0;net9.0;net8.0; Mapster A fast, fun and stimulating object to object mapper. Kind of like AutoMapper, just simpler and way, way faster. Mapster @@ -17,6 +17,7 @@ true Mapster 10.0.0-pre01 + 12 enable 1701;1702;8618 @@ -26,7 +27,12 @@ + + + + + - \ No newline at end of file + diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 218045bf..2e14fda3 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -115,20 +115,14 @@ public static TDestination Adapt(this TSource source, TDe private static TDestination UpdateFuncFromPackedinObject(TSource source, TDestination destination, TypeAdapterConfig config, Type sourceType, Type destinationType) { - dynamic del = config.GetMapToTargetFunction(sourceType, destinationType); - + var del = config.GetMapToTargetFunction(sourceType, destinationType); if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible) - { - dynamic objfn = del; - return objfn((dynamic)source, (dynamic)destination); - } - else - { - //NOTE: if type is non-public, we cannot use dynamic - //DynamicInvoke is slow, but works with non-public - return (TDestination)del.DynamicInvoke(source, destination); - } + return ((dynamic)del)((dynamic)source, (dynamic)destination); + + //NOTE: if type is non-public, we cannot use dynamic + //DynamicInvoke is slow, but works with non-public + return (TDestination)del.DynamicInvoke(source, destination); } /// diff --git a/src/Mapster/Utils/CodeAnalysisAttributes.cs b/src/Mapster/Utils/CodeAnalysisAttributes.cs new file mode 100644 index 00000000..212fb714 --- /dev/null +++ b/src/Mapster/Utils/CodeAnalysisAttributes.cs @@ -0,0 +1,20 @@ +#if NETSTANDARD2_0 +using System; + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class NotNullWhenAttribute : Attribute + { + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + public bool ReturnValue { get; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] + public sealed class NotNullIfNotNullAttribute : Attribute + { + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + public string ParameterName { get; } + } +} +#endif diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs index f8011b5b..8d66dc3a 100644 --- a/src/Mapster/Utils/ExpressionEx.cs +++ b/src/Mapster/Utils/ExpressionEx.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Linq.Expressions; using System.Reflection; namespace Mapster.Utils @@ -40,7 +39,9 @@ public static Expression PropertyOrFieldPath(Expression expr, string path) // For dynamically built types, it is possible to have periods in the property name. // Rejoin an incrementing number of parts with periods to try and find a property match. - if (IsPropertyOrFieldPathWithPeriods(current, props[i..], out next, out int combinationLength)) + var remaining = new string[props.Length - i]; + Array.Copy(props, i, remaining, 0, remaining.Length); + if (IsPropertyOrFieldPathWithPeriods(current, remaining, out next, out int combinationLength)) { current = next; i += combinationLength - 1; @@ -64,7 +65,7 @@ private static bool IsPropertyOrFieldPathWithPeriods(Expression expr, string[] p for (int count = 2; count <= path.Length; count++) { - string prop = string.Join('.', path[..count]); + string prop = string.Join(".", path, 0, count); if (IsPropertyOrField(expr, prop, out propExpr)) { combinationLength = count; diff --git a/src/Mapster/Utils/IsExternalInit.cs b/src/Mapster/Utils/IsExternalInit.cs new file mode 100644 index 00000000..33338a32 --- /dev/null +++ b/src/Mapster/Utils/IsExternalInit.cs @@ -0,0 +1,8 @@ +#if NETSTANDARD2_0 +namespace System.Runtime.CompilerServices +{ + internal sealed class IsExternalInit + { + } +} +#endif diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 9abe09f8..fbb37321 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -99,13 +99,15 @@ IEnumerable GetFieldsFunc(Type t, MemberInfo[] overlapMembers) = public static IEnumerable DropHiddenMembers(this IEnumerable allMembers, ICollection currentTypeMembers) where T : MemberInfo { - var compareMemberNames = allMembers.IntersectBy(currentTypeMembers.Select(x => x.Name), x => x.Name).Select(x => x.Name); + var currentTypeMemberMetadataTokenByName = currentTypeMembers + .GroupBy(x => x.Name) + .ToDictionary(x => x.Key, x => x.First().MetadataToken); foreach (var member in allMembers) { - if (compareMemberNames.Contains(member.Name)) + if (currentTypeMemberMetadataTokenByName.TryGetValue(member.Name, out var metadataToken)) { - if (currentTypeMembers.First(x => x.Name == member.Name).MetadataToken == member.MetadataToken) + if (metadataToken == member.MetadataToken) yield return member; } else @@ -446,4 +448,4 @@ public static bool isDefaultCtor(this Type type) return type.GetConstructor(new Type[] { }) is not null ? true : false; } } -} \ No newline at end of file +} diff --git a/src/Sample.AspNetCore/Sample.AspNetCore.csproj b/src/Sample.AspNetCore/Sample.AspNetCore.csproj index a681b6fd..853f199a 100644 --- a/src/Sample.AspNetCore/Sample.AspNetCore.csproj +++ b/src/Sample.AspNetCore/Sample.AspNetCore.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj index 78092145..31c00843 100644 --- a/src/Sample.CodeGen/Sample.CodeGen.csproj +++ b/src/Sample.CodeGen/Sample.CodeGen.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/TemplateTest/TemplateTest.csproj b/src/TemplateTest/TemplateTest.csproj index ae4dff09..82771eea 100644 --- a/src/TemplateTest/TemplateTest.csproj +++ b/src/TemplateTest/TemplateTest.csproj @@ -1,17 +1,18 @@  - net10.0;net9.0;net8.0; + net10.0;net9.0;net8.0 + $(TargetFrameworks);net48 enable true false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive