diff --git a/src/Mapster.Tests.InternalsVisibleAssembly/DtoStubs.cs b/src/Mapster.Tests.InternalsVisibleAssembly/DtoStubs.cs
new file mode 100644
index 00000000..b7116739
--- /dev/null
+++ b/src/Mapster.Tests.InternalsVisibleAssembly/DtoStubs.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Runtime.CompilerServices;
+
+//Expose this to Mapster.Tests
+[assembly: InternalsVisibleTo("Mapster.Tests")]
+
+namespace Mapster.Tests.InternalsVisibleAssembly
+{
+ public class DtoInternal
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Age { get; } = -1;
+ public string Prop { get; set; }
+ public string OtherProp { get; set; }
+
+ internal DtoInternal() { }
+ }
+
+ public class DtoPrivate
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Age { get; } = -1;
+ public string Prop { get; set; }
+
+ private DtoPrivate() { }
+ }
+}
+
diff --git a/src/Mapster.Tests.InternalsVisibleAssembly/Mapster.Tests.InternalsVisibleAssembly.csproj b/src/Mapster.Tests.InternalsVisibleAssembly/Mapster.Tests.InternalsVisibleAssembly.csproj
new file mode 100644
index 00000000..86ea3bbe
--- /dev/null
+++ b/src/Mapster.Tests.InternalsVisibleAssembly/Mapster.Tests.InternalsVisibleAssembly.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netcoreapp2.1
+
+
+
diff --git a/src/Mapster.Tests/Mapster.Tests.csproj b/src/Mapster.Tests/Mapster.Tests.csproj
index 144b5e3b..d8c1e436 100644
--- a/src/Mapster.Tests/Mapster.Tests.csproj
+++ b/src/Mapster.Tests/Mapster.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.0
+ netcoreapp2.1
false
Mapster.Tests
Mapster.Tests.snk
@@ -19,6 +19,7 @@
+
diff --git a/src/Mapster.Tests/WhenMappingInternalConstructor.cs b/src/Mapster.Tests/WhenMappingInternalConstructor.cs
new file mode 100644
index 00000000..157a399b
--- /dev/null
+++ b/src/Mapster.Tests/WhenMappingInternalConstructor.cs
@@ -0,0 +1,42 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Shouldly;
+using Mapster.Tests.InternalsVisibleAssembly;
+using System;
+
+namespace Mapster.Tests
+{
+ [TestClass]
+ public class WhenMappingInternalConstructor
+ {
+
+ [TestMethod]
+ public void MapToConstructor_InternalVisible()
+ {
+ var poco = new Poco { Id = Guid.NewGuid(), Name = "Test", Prop = "Prop", OtherProp = "OtherProp" };
+ var dto = TypeAdapter.Adapt(poco);
+
+ dto.Id.ShouldBe(poco.Id);
+ dto.Name.ShouldBe(poco.Name);
+ dto.Age.ShouldBe(-1);
+ dto.Prop.ShouldBe(poco.Prop);
+ }
+
+
+ [TestMethod]
+ public void MapToConstructor_PrivateVisible_ShouldThrow()
+ {
+ var poco = new Poco { Id = Guid.NewGuid(), Name = "Test", Prop = "Prop", OtherProp = "OtherProp" };
+ var ex = Assert.ThrowsException(() => poco.Adapt());
+ Assert.IsInstanceOfType(ex.InnerException, typeof(InvalidOperationException));
+ Assert.IsTrue(ex.InnerException.Message.Contains("No default constructor for type"));
+ }
+
+ public class Poco
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public string Prop { get; set; }
+ public string OtherProp { get; set; }
+ }
+ }
+}
diff --git a/src/Mapster.sln b/src/Mapster.sln
index 2fd8a0cd..567b1913 100644
--- a/src/Mapster.sln
+++ b/src/Mapster.sln
@@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mapster.SourceGenerator", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mapster.Core", "Mapster.Core\Mapster.Core.csproj", "{1A7D2FD4-DDEC-4E11-93FF-1310F34F67CF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapster.Tests.InternalsVisibleAssembly", "Mapster.Tests.InternalsVisibleAssembly\Mapster.Tests.InternalsVisibleAssembly.csproj", "{927CC36B-F45C-4B6E-A55D-D162D06570BD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -137,6 +139,10 @@ Global
{1A7D2FD4-DDEC-4E11-93FF-1310F34F67CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A7D2FD4-DDEC-4E11-93FF-1310F34F67CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A7D2FD4-DDEC-4E11-93FF-1310F34F67CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {927CC36B-F45C-4B6E-A55D-D162D06570BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {927CC36B-F45C-4B6E-A55D-D162D06570BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {927CC36B-F45C-4B6E-A55D-D162D06570BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {927CC36B-F45C-4B6E-A55D-D162D06570BD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -156,6 +162,7 @@ Global
{DE045991-6268-46EE-B5D3-79DE75820976} = {916FA044-B9E5-44F2-991A-85AA43C08255}
{D5DF0FB7-44A5-4326-9EC4-5B8F7FCCE00F} = {D33E5A90-ABCA-4B2D-8758-2873CC5AB847}
{3CB56440-5449-4DE5-A8D3-549C87C1B36A} = {EF7E343F-592E-4EAC-A0A4-92EB4B95CB89}
+ {927CC36B-F45C-4B6E-A55D-D162D06570BD} = {D33E5A90-ABCA-4B2D-8758-2873CC5AB847}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83B87DBA-277C-49F1-B597-E3B78C2C8275}
diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs
index 0064f889..0908613d 100644
--- a/src/Mapster/Adapters/BaseAdapter.cs
+++ b/src/Mapster/Adapters/BaseAdapter.cs
@@ -5,6 +5,7 @@
using System.Reflection;
using Mapster.Models;
using Mapster.Utils;
+using System.Runtime.CompilerServices;
namespace Mapster.Adapters
{
@@ -360,9 +361,9 @@ protected virtual Expression CreateInstantiationExpression(Expression source, Ex
//if there is default constructor, use default constructor
else if (arg.DestinationType.HasDefaultConstructor())
- {
+ {
return Expression.New(arg.DestinationType);
- }
+ }
//if mapToTarget or include derived types, allow mapping & throw exception on runtime
//instantiation is not needed
@@ -382,6 +383,12 @@ protected virtual Expression CreateInstantiationExpression(Expression source, Ex
return Expression.New(DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType));
}
+ //if the assembly has an otherwise accessible constructor use it
+ else if (HasInternalAccessibleDefaultConstructor(arg.DestinationType, arg.SourceType))
+ {
+ return Expression.New(arg.DestinationType);
+ }
+
//otherwise throw
else
{
@@ -389,6 +396,35 @@ protected virtual Expression CreateInstantiationExpression(Expression source, Ex
}
}
+
+
+#if !NETSTANDARD1_3
+ private bool HasInternalAccessibleDefaultConstructor(Type fromType, Type toType)
+ {
+
+ bool internalsVisibleTo() =>
+ fromType.Assembly.GetCustomAttributes(typeof(InternalsVisibleToAttribute), false)
+ .Cast()
+ .Any(x => x.AssemblyName == toType.Assembly.GetName().Name);
+
+ bool hasAccessibleConstructor() =>
+ fromType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)
+ .Any(c => c.GetParameters().Length == 0 && c.IsAssembly);
+
+ return (internalsVisibleTo() && hasAccessibleConstructor());
+ }
+#else
+ private bool HasInternalAccessibleDefaultConstructor(Type fromType, Type toType)
+ {
+ var fromTypeInfo = fromType.GetTypeInfo();
+ var fromAssembly = fromTypeInfo.Assembly;
+ var toAssembly = toType.GetTypeInfo().Assembly;
+ bool internalsVisibleTo() => fromAssembly.GetCustomAttributes().Any(x => x.AssemblyName == toAssembly.GetName().Name);
+ bool hasAccessibleConstructor() => fromTypeInfo.DeclaredConstructors.Any(x => x.GetParameters().Length == 0 && x.IsAssembly);
+
+ return (internalsVisibleTo() && hasAccessibleConstructor());
+ }
+#endif
private static Expression CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping = null, Expression? destination = null)
{
var mapType = arg.MapType == MapType.MapToTarget && destination == null ? MapType.Map :