diff --git a/AgileMapper.UnitTests.Common/AgileMapper.UnitTests.Common.csproj b/AgileMapper.UnitTests.Common/AgileMapper.UnitTests.Common.csproj
index 616ab412e..477f06402 100644
--- a/AgileMapper.UnitTests.Common/AgileMapper.UnitTests.Common.csproj
+++ b/AgileMapper.UnitTests.Common/AgileMapper.UnitTests.Common.csproj
@@ -11,6 +11,8 @@
+
+
diff --git a/AgileMapper.UnitTests.MoreTestClasses.Vb/AgileMapper.UnitTests.MoreTestClasses.Vb.vbproj b/AgileMapper.UnitTests.MoreTestClasses.Vb/AgileMapper.UnitTests.MoreTestClasses.Vb.vbproj
new file mode 100644
index 000000000..a3ee1de70
--- /dev/null
+++ b/AgileMapper.UnitTests.MoreTestClasses.Vb/AgileMapper.UnitTests.MoreTestClasses.Vb.vbproj
@@ -0,0 +1,14 @@
+
+
+
+ net35;netstandard1.0
+ AgileObjects.AgileMapper.UnitTests.MoreTestClasses.Vb
+ AgileObjects.AgileMapper.UnitTests.MoreTestClasses.Vb
+ false
+
+
+
+ $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client
+
+
+
diff --git a/AgileMapper.UnitTests.MoreTestClasses.Vb/PublicNamedIndex.vb b/AgileMapper.UnitTests.MoreTestClasses.Vb/PublicNamedIndex.vb
new file mode 100644
index 000000000..2482b16a7
--- /dev/null
+++ b/AgileMapper.UnitTests.MoreTestClasses.Vb/PublicNamedIndex.vb
@@ -0,0 +1,19 @@
+Public Class PublicNamedIndex(Of T)
+
+ Private _valueToReturn As T
+
+ Public WriteOnly Property ValueToReturn As T
+ Set
+ _valueToReturn = Value
+ End Set
+ End Property
+
+ Public ReadOnly Property Value(
+ Optional indexOne As Integer = 1,
+ Optional indexTwo As Integer = 2) As T
+ Get
+ Return _valueToReturn
+ End Get
+ End Property
+
+End Class
\ No newline at end of file
diff --git a/AgileMapper.UnitTests.MoreTestClasses/packages.config b/AgileMapper.UnitTests.MoreTestClasses/packages.config
deleted file mode 100644
index ff08b12e1..000000000
--- a/AgileMapper.UnitTests.MoreTestClasses/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
index 88010025d..ed0a8da6d 100644
--- a/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
+++ b/AgileMapper.UnitTests.Net35/AgileMapper.UnitTests.Net35.csproj
@@ -38,7 +38,6 @@
-
diff --git a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj
index 22fa55ed0..73d386da0 100644
--- a/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj
+++ b/AgileMapper.UnitTests.NetCore/AgileMapper.UnitTests.NetCore.csproj
@@ -34,8 +34,6 @@
-
-
diff --git a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj
index d85efbbb2..94398fbd0 100644
--- a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj
+++ b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj
@@ -35,7 +35,6 @@
-
diff --git a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj
index a2e953764..4032ac87f 100644
--- a/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj
+++ b/AgileMapper.UnitTests.NetCore3/AgileMapper.UnitTests.NetCore3.csproj
@@ -35,7 +35,6 @@
-
diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
index f4d38bc3f..aa897987e 100644
--- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
+++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj
@@ -24,8 +24,6 @@
-
-
diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
index b56e9d4da..434d7343a 100644
--- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
+++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj
@@ -30,7 +30,6 @@
-
diff --git a/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs
index 73a6bf857..cb7ed5c08 100644
--- a/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs
+++ b/AgileMapper.UnitTests/Configuration/WhenViewingMappingPlans.cs
@@ -5,6 +5,7 @@
using AgileMapper.Members;
using Common;
using Common.TestClasses;
+ using MoreTestClasses.Vb;
using TestClasses;
#if !NET35
using Xunit;
@@ -154,6 +155,16 @@ public void ShouldIncludeUnmappableReadOnlyArrayMemberDetails()
}
}
+ [Fact]
+ public void ShouldIncludeUnmappableIndexedPropertyDetails()
+ {
+ string plan = Mapper
+ .GetPlanFor>>()
+ .ToANew>>();
+
+ plan.ShouldContain("requires index(es) - indexOne: int, indexTwo: int");
+ }
+
[Fact]
public void ShouldIncludeUnmappableReadOnlyReadOnlyCollectionMemberDetails()
{
diff --git a/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs b/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs
index d766809df..ad049c2c2 100644
--- a/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs
+++ b/AgileMapper.UnitTests/WhenMappingToNewComplexTypes.cs
@@ -3,6 +3,7 @@
using System;
using AgileMapper.Extensions;
using Common;
+ using MoreTestClasses.Vb;
using TestClasses;
#if !NET35
using Xunit;
@@ -133,6 +134,38 @@ public void ShouldConditionallyUseConstructorsWhereArgumentsAreNull()
addressResult.Address.Line1.ShouldBe("Line 1!");
}
+ // See https://github.com/agileobjects/AgileMapper/issues/221
+ [Fact]
+ public void ShouldIgnoreSourceIndexedProperties()
+ {
+ var source = new PublicNamedIndex>
+ {
+ ValueToReturn = new PublicField { Value = "Test" }
+ };
+
+ var result = Mapper
+ .Map(source)
+ .ToANew>>();
+
+ result.ShouldNotBeNull().Value.ShouldBeNull();
+ }
+
+ // See https://github.com/agileobjects/AgileMapper/issues/221
+ [Fact]
+ public void ShouldIgnoreTargetIndexedProperties()
+ {
+ var source = new PublicField>
+ {
+ Value = new PublicField { Value = "Hello!" }
+ };
+
+ var result = Mapper
+ .Map(source)
+ .ToANew>>();
+
+ result.ShouldNotBeNull().get_Value().ShouldBeNull();
+ }
+
#region Helper Classes
private class CtorTester
diff --git a/AgileMapper.sln b/AgileMapper.sln
index a4a4939f3..d761be3df 100644
--- a/AgileMapper.sln
+++ b/AgileMapper.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30002.166
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32319.34
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05AB6D17-6066-41D5-8E79-31C342DFC2DC}"
ProjectSection(SolutionItems) = preProject
@@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.UnitTests.NetCo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.UnitTests.NetCore2", "AgileMapper.UnitTests.NetCore2\AgileMapper.UnitTests.NetCore2.csproj", "{72287439-1634-4164-ABAC-E4A462235C5D}"
EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "AgileMapper.UnitTests.MoreTestClasses.Vb", "AgileMapper.UnitTests.MoreTestClasses.Vb\AgileMapper.UnitTests.MoreTestClasses.Vb.vbproj", "{4D694869-5B53-4145-BD7F-9187652FD847}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -137,6 +139,10 @@ Global
{72287439-1634-4164-ABAC-E4A462235C5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72287439-1634-4164-ABAC-E4A462235C5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72287439-1634-4164-ABAC-E4A462235C5D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D694869-5B53-4145-BD7F-9187652FD847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D694869-5B53-4145-BD7F-9187652FD847}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D694869-5B53-4145-BD7F-9187652FD847}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D694869-5B53-4145-BD7F-9187652FD847}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -162,6 +168,7 @@ Global
{5085AA3F-A5B7-40E2-9309-7E0B81FBE113} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B}
{F9307FDB-0F73-40AB-B7AC-AE79CFA5521C} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B}
{72287439-1634-4164-ABAC-E4A462235C5D} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B}
+ {4D694869-5B53-4145-BD7F-9187652FD847} = {88D48136-E176-4AF7-ACA4-A2954F3BD55B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C39E9BDB-0077-445C-8F09-801CAE1AF8AB}
diff --git a/AgileMapper/Members/Member.cs b/AgileMapper/Members/Member.cs
index f42316b00..e36d748c3 100644
--- a/AgileMapper/Members/Member.cs
+++ b/AgileMapper/Members/Member.cs
@@ -19,7 +19,7 @@ internal class Member
public const string RootSourceMemberName = "Source";
public const string RootTargetMemberName = "Target";
- private static readonly int _ctorParameterHashCode =
+ private static readonly int _ctorParameterHashCode =
MemberType.ConstructorParameter.GetHashCode();
private readonly IAccessFactory _accessFactory;
@@ -30,6 +30,7 @@ private Member(
Type type,
MemberInfo memberInfo,
IAccessFactory accessFactory = null,
+ bool isIndexed = false,
bool isReadable = true,
bool isWriteable = true,
bool isRoot = false)
@@ -39,6 +40,7 @@ private Member(
memberInfo.DeclaringType,
type,
accessFactory,
+ isIndexed,
isReadable,
isWriteable,
isRoot)
@@ -52,6 +54,7 @@ private Member(
Type declaringType,
Type type,
IAccessFactory accessFactory = null,
+ bool isIndexed = false,
bool isReadable = true,
bool isWriteable = true,
bool isRoot = false)
@@ -61,6 +64,7 @@ private Member(
DeclaringType = declaringType;
Type = type;
_accessFactory = accessFactory;
+ IsIndexed = isIndexed;
IsReadable = isReadable;
IsWriteable = isWriteable;
IsRoot = isRoot;
@@ -136,13 +140,21 @@ public static Member Field(FieldInfo fieldInfo)
public static Member Property(PropertyInfo propertyInfo)
{
+ var isReadable = propertyInfo.IsReadable();
+ var isWritable = propertyInfo.IsWritable();
+
+ var isIndexed =
+ isReadable && propertyInfo.GetGetter().GetParameters().Any() ||
+ isWritable && propertyInfo.GetSetter().GetParameters().Length > 1;
+
return new Member(
MemberType.Property,
propertyInfo.PropertyType,
propertyInfo,
new PropertyInfoWrapper(propertyInfo),
- propertyInfo.IsReadable(),
- propertyInfo.IsWritable());
+ isIndexed,
+ isReadable,
+ isWritable);
}
public static Member GetMethod(MethodInfo methodInfo)
@@ -203,6 +215,8 @@ public static Member DictionaryEntry(string sourceMemberName, DictionaryTargetMe
public bool IsSimple { get; }
+ public bool IsIndexed { get; set; }
+
public bool IsReadable { get; }
public bool IsWriteable { get; }
@@ -240,6 +254,7 @@ public Member WithType(Type runtimeType)
runtimeType,
MemberInfo,
_accessFactory,
+ IsIndexed,
IsReadable,
IsWriteable,
IsRoot);
@@ -250,6 +265,7 @@ public Member WithType(Type runtimeType)
Name,
DeclaringType,
runtimeType,
+ isIndexed: IsIndexed,
isReadable: IsReadable,
isWriteable: IsWriteable,
isRoot: IsRoot);
diff --git a/AgileMapper/Members/MemberCache.cs b/AgileMapper/Members/MemberCache.cs
index 837ab19ab..28984a04a 100644
--- a/AgileMapper/Members/MemberCache.cs
+++ b/AgileMapper/Members/MemberCache.cs
@@ -1,14 +1,14 @@
namespace AgileObjects.AgileMapper.Members
{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
using Caching;
using Extensions;
using Extensions.Internal;
using NetStandardPolyfills;
using ReadableExpressions.Extensions;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
using static System.StringComparer;
internal class MemberCache
@@ -132,7 +132,11 @@ private static IEnumerable GetProperties(Type targetType, Func AllExceptBclComplexTypes(property.PropertyType);
- private static bool OnlyGettable(PropertyInfo property) => property.IsReadable();
+ private static bool OnlyGettable(PropertyInfo property)
+ {
+ var getter = property.GetGetter();
+ return getter != null && getter.GetParameters().None();
+ }
#endregion
diff --git a/AgileMapper/Members/MemberExtensionMethods.cs b/AgileMapper/Members/MemberExtensionMethods.cs
index 8e7c3c658..6f7bb3a7c 100644
--- a/AgileMapper/Members/MemberExtensionMethods.cs
+++ b/AgileMapper/Members/MemberExtensionMethods.cs
@@ -103,7 +103,7 @@ public static bool IsUnmappable(this QualifiedMember member, out string reason)
if (member.IsSimple || member.Type.IsValueType())
{
- reason = "readonly " + ((member.IsComplex) ? "struct" : member.Type.GetFriendlyName());
+ reason = "readonly " + (member.IsComplex ? "struct" : member.Type.GetFriendlyName());
return true;
}
@@ -113,6 +113,19 @@ public static bool IsUnmappable(this QualifiedMember member, out string reason)
return true;
}
+ if (member.IsIndexed)
+ {
+ var property = (PropertyInfo)member.LeafMember.MemberInfo;
+
+ var indexes = string.Join(", ", property
+ .GetGetter()
+ .GetParameters()
+ .ProjectToArray(p => p.Name + ": " + p.ParameterType.GetFriendlyName()));
+
+ reason = "requires index(es) - " + indexes;
+ return true;
+ }
+
reason = null;
return false;
}
@@ -285,7 +298,7 @@ public static QualifiedMember ToSourceMemberOrNull(
this Expression memberAccess,
MapperContext mapperContext)
{
- return memberAccess.ToSourceMember(mapperContext, nt => { });
+ return memberAccess.ToSourceMember(mapperContext, _ => { });
}
public static QualifiedMember ToSourceMemberOrNull(
@@ -294,7 +307,7 @@ public static QualifiedMember ToSourceMemberOrNull(
out string failureReason)
{
var hasUnsupportedNodeType = false;
- var sourceMember = memberAccess.ToSourceMember(mapperContext, nt => hasUnsupportedNodeType = true);
+ var sourceMember = memberAccess.ToSourceMember(mapperContext, _ => hasUnsupportedNodeType = true);
if (hasUnsupportedNodeType)
{
@@ -347,7 +360,7 @@ public static QualifiedMember ToTargetMemberOrNull(
out string failureReason)
{
var hasUnsupportedNodeType = false;
- var targetMember = memberAccess.ToTargetMember(mapperContext, nt => hasUnsupportedNodeType = true);
+ var targetMember = memberAccess.ToTargetMember(mapperContext, _ => hasUnsupportedNodeType = true);
if (hasUnsupportedNodeType)
{
diff --git a/AgileMapper/Members/QualifiedMember.cs b/AgileMapper/Members/QualifiedMember.cs
index 18fd2dbb2..ea501876d 100644
--- a/AgileMapper/Members/QualifiedMember.cs
+++ b/AgileMapper/Members/QualifiedMember.cs
@@ -180,6 +180,8 @@ public static QualifiedMember Create(Member[] memberChain, MapperContext mapperC
public bool IsReadable => LeafMember.IsReadable;
public bool IsReadOnly { get; set; }
+
+ public bool IsIndexed => LeafMember.IsIndexed;
public bool IsRecursion { get; }
diff --git a/AgileMapper/Members/SourceMemberMatchContext.cs b/AgileMapper/Members/SourceMemberMatchContext.cs
index a2bb6dc9b..9c1802745 100644
--- a/AgileMapper/Members/SourceMemberMatchContext.cs
+++ b/AgileMapper/Members/SourceMemberMatchContext.cs
@@ -55,10 +55,7 @@ public ConfiguredSourceMemberIgnoreBase GetSourceMemberIgnoreOrNull(IQualifiedMe
public SourceMemberMatch CreateSourceMemberMatch(IQualifiedMember matchingSourceMember = null, bool isUseable = true)
{
- if (matchingSourceMember == null)
- {
- matchingSourceMember = MatchingSourceMember;
- }
+ matchingSourceMember ??= MatchingSourceMember;
var ignoreCondition = GetSourceMemberCondition(matchingSourceMember);