Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace AgileObjects.AgileMapper.UnitTests.Configuration
{
using System;
using System.Collections.Generic;
using System.Linq;
using AgileMapper.Configuration;
using Common;
using TestClasses;
Expand Down Expand Up @@ -351,9 +353,81 @@ public void ShouldErrorIfRootTargetSimpleTypeMemberDataSourceConfigured()
});

configurationException.Message.ShouldContain("PublicProperty<int>.Value");
configurationException.Message.ShouldContain("'int' cannot be mapped to target type 'PublicField<long>'");
}

configurationException.Message.ShouldContain(
"'int' cannot be mapped to target type 'PublicField<long>'");
[Fact]
public void ShouldErrorIfRootEnumerableTargetNonEnumerableTypeMemberDataSourceConfigured()
{
var configurationException = Should.Throw<MappingConfigurationException>(() =>
{
using (var mapper = Mapper.CreateNew())
{
mapper.WhenMapping
.From<PublicProperty<Address>>()
.To<List<Address>>()
.Map(ctx => ctx.Source.Value)
.ToTarget();
}
});

configurationException.Message.ShouldContain("Non-enumerable PublicProperty<Address>.Value");
configurationException.Message.ShouldContain("cannot be mapped to enumerable target type 'List<Address>'");
}

[Fact]
public void ShouldErrorIfRootNonEnumerableTargetEnumerableTypeMemberDataSourceConfigured()
{
var configurationException = Should.Throw<MappingConfigurationException>(() =>
{
using (var mapper = Mapper.CreateNew())
{
mapper.WhenMapping
.From<PublicField<List<Customer>>>()
.To<PublicProperty<Customer>>()
.Map(ctx => ctx.Source.Value)
.ToTarget();
}
});

configurationException.Message.ShouldContain("Enumerable PublicField<List<Customer>>.Value");
configurationException.Message.ShouldContain("cannot be mapped to non-enumerable target type 'PublicProperty<Customer>'");
}

[Fact]
public void ShouldErrorIfConstantSpecifiedForTargetMember()
{
var configurationException = Should.Throw<MappingConfigurationException>(() =>
{
using (var mapper = Mapper.CreateNew())
{
mapper.WhenMapping
.From<PublicField<Customer>>()
.To<PublicProperty<Customer>>()
.Map(ctx => "Number!")
.To(ppc => "Number?");
}
});

configurationException.Message.ShouldContain("Unable to determine target member from '\"Number?\"'");
}

[Fact]
public void ShouldErrorIfProjectionSpecifiedForTargetMember()
{
var configurationException = Should.Throw<MappingConfigurationException>(() =>
{
using (var mapper = Mapper.CreateNew())
{
mapper.WhenMapping
.From<PublicField<IEnumerable<Customer>>>()
.To<PublicProperty<IEnumerable<Customer>>>()
.Map(ctx => new[] { "One", "Two", "Three" })
.To(ppc => ppc.Value.Select(c => c.Name));
}
});

configurationException.Message.ShouldContain("not writeable");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ private ConfiguredDataSourceFactory CreateForCtorParam<TParam>(ParameterInfo par
public IMappingConfigContinuation<TSource, TTarget> ToTarget()
{
ThrowIfSimpleSource(typeof(TTarget));
ThrowIfEnumerableSourceAndTargetMismatch(typeof(TTarget));

return RegisterDataSource<TTarget>(() => new ConfiguredDataSourceFactory(
_configInfo,
Expand All @@ -299,27 +300,65 @@ private void ThrowIfSimpleSource(Type targetMemberType)
return;
}

string sourceValue;
var sourceValue = GetSourceValue(customValue);

if (customValue.NodeType == ExpressionType.MemberAccess)
throw new MappingConfigurationException(string.Format(
CultureInfo.InvariantCulture,
"{0}'{1}' cannot be mapped to target type '{2}'",
sourceValue,
customValue.Type.GetFriendlyName(),
targetMemberType.GetFriendlyName()));
}

private void ThrowIfEnumerableSourceAndTargetMismatch(Type targetMemberType)
{
var customValue = _customValueLambda.Body;

if ((targetMemberType.IsDictionary() || customValue.Type.IsDictionary()) ||
(targetMemberType.IsEnumerable() == customValue.Type.IsEnumerable()))
{
var rootSourceMember = _configInfo.MapperContext.QualifiedMemberFactory.RootSource<TSource, TTarget>();
var sourceMember = customValue.ToSourceMember(_configInfo.MapperContext);
sourceValue = sourceMember.GetFriendlyMemberPath(rootSourceMember) + " of type ";
return;
}

string sourceEnumerableState, targetEnumerableState;

if (targetMemberType.IsEnumerable())
{
sourceEnumerableState = "Non-enumerable";
targetEnumerableState = "enumerable";
}
else
{
sourceValue = "Source type ";
sourceEnumerableState = "Enumerable";
targetEnumerableState = "non-enumerable";
}

var sourceValue = GetSourceValue(customValue);

throw new MappingConfigurationException(string.Format(
CultureInfo.InvariantCulture,
"{0}'{1}' cannot be mapped to target type '{2}'",
"{0} {1}'{2}' cannot be mapped to {3} target type '{4}'",
sourceEnumerableState,
sourceValue,
customValue.Type.GetFriendlyName(),
targetEnumerableState,
targetMemberType.GetFriendlyName()));
}

private string GetSourceValue(Expression customValue)
{
if (customValue.NodeType != ExpressionType.MemberAccess)
{
return "Source type ";
}

var rootSourceMember = _configInfo.MapperContext.QualifiedMemberFactory.RootSource<TSource, TTarget>();
var sourceMember = customValue.ToSourceMember(_configInfo.MapperContext);
var sourceValue = sourceMember.GetFriendlyMemberPath(rootSourceMember) + " of type ";

return sourceValue;
}

private MappingConfigContinuation<TSource, TTarget> RegisterDataSource<TTargetValue>(
Func<ConfiguredDataSourceFactory> factoryFactory)
{
Expand Down
12 changes: 11 additions & 1 deletion AgileMapper/Configuration/UserConfiguredItemBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@ protected UserConfiguredItemBase(MappingConfigInfo configInfo, LambdaExpression

private static QualifiedMember GetTargetMemberOrThrow(LambdaExpression lambda, MappingConfigInfo configInfo)
{
var targetMember = lambda.ToTargetMember(configInfo.MapperContext);
QualifiedMember targetMember;

try
{
targetMember = lambda.ToTargetMember(configInfo.MapperContext);
}
catch (NotSupportedException)
{
throw new MappingConfigurationException(
$"Unable to determine target member from '{lambda.Body.ToReadableString()}'");
}

if (targetMember == null)
{
Expand Down
2 changes: 1 addition & 1 deletion AgileMapper/Members/MemberExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public static QualifiedMember ToTargetMember(this LambdaExpression memberAccess,
mapperContext);
}

internal static QualifiedMember CreateMember(
private static QualifiedMember CreateMember(
Expression memberAccessExpression,
Func<Type, Member> rootMemberFactory,
Func<Type, IList<Member>> membersFactory,
Expand Down