Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ignores on reverse map paths #2162

Merged
merged 2 commits into from Jun 26, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 5 additions & 8 deletions src/AutoMapper/Configuration/PathConfigurationExpression.cs
Expand Up @@ -28,6 +28,11 @@ public void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> sour
MapFromUntyped(sourceExpression);
}

public void Ignore()
{
PathMapActions.Add(pm => pm.Ignored = true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is simple enough, but there is some duplication here. We could just copy Ignored to PropertyMap.Ignored and handle both cases (PathMaps and PropertyMaps) in TryPopertyMap.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might take a bit more refactoring I think, since that method returns an expression and the PropertyMap piece above in CreateAssignmentFunc does a similar filter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would have to return null when CanResolve is false and filter for that.

}

public void MapFromUntyped(LambdaExpression sourceExpression)
{
_sourceExpression = sourceExpression;
Expand All @@ -39,14 +44,6 @@ public void MapFromUntyped(LambdaExpression sourceExpression)

public void Configure(TypeMap typeMap)
{
//var destMember = DestinationMember;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a reminder that I left smth out, but I guess somebody will eventually complain.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, should we open another GH issue then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather wait, but if you feel it matters right now...


//if(destMember.DeclaringType.IsGenericType())
//{
// var destTypeInfo = typeMap.Profile.CreateTypeDetails(destMember.DeclaringType);
// destMember = destTypeInfo.PublicReadAccessors.Single(m => m.Name == destMember.Name);
//}

var pathMap = typeMap.FindOrCreatePathMapFor(_destinationExpression, MemberPath, typeMap);

Apply(pathMap);
Expand Down
8 changes: 2 additions & 6 deletions src/AutoMapper/Execution/TypeMapPlanBuilder.cs
Expand Up @@ -188,20 +188,16 @@ private Expression CreateDestinationFunc(out bool constructorMapping)
private Expression CreateAssignmentFunc(Expression destinationFunc, bool constructorMapping)
{
var actions = new List<Expression>();
foreach(var propertyMap in _typeMap.GetPropertyMaps())
foreach(var propertyMap in _typeMap.GetPropertyMaps().Where(pm => pm.CanResolveValue()))
{
if(!propertyMap.CanResolveValue())
{
continue;
}
var property = TryPropertyMap(propertyMap);
if(constructorMapping && _typeMap.ConstructorParameterMatches(propertyMap.DestinationProperty.Name))
{
property = IfThen(NotEqual(_initialDestination, Constant(null)), property);
}
actions.Add(property);
}
foreach(var pathMap in _typeMap.PathMaps)
foreach(var pathMap in _typeMap.PathMaps.Where(pm => !pm.Ignored))
{
actions.Add(HandlePath(pathMap));
}
Expand Down
5 changes: 5 additions & 0 deletions src/AutoMapper/IPathConfigurationExpression.cs
Expand Up @@ -18,5 +18,10 @@ public interface IPathConfigurationExpression<TSource, out TDestination>
/// <typeparam name="TSourceMember">Member type of the source member to use</typeparam>
/// <param name="sourceMember">Expression referencing the source member to map against</param>
void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember);

/// <summary>
/// Ignore this member for configuration validation and skip during mapping
/// </summary>
void Ignore();
}
}
1 change: 1 addition & 0 deletions src/AutoMapper/PathMap.cs
Expand Up @@ -21,5 +21,6 @@ public PathMap(LambdaExpression destinationExpression, MemberPath memberPath, Ty
public LambdaExpression SourceExpression { get; set; }
public MemberPath MemberPath { get; }
public MemberInfo DestinationMember => MemberPath.Last;
public bool Ignored { get; set; }
}
}
49 changes: 49 additions & 0 deletions src/UnitTests/ForPath.cs
Expand Up @@ -131,6 +131,55 @@ public void Should_unflatten()
}
}

public class ForPathWithIgnoreShouldNotSetValue : AutoMapperSpecBase
{
public partial class TimesheetModel
{
public int ID { get; set; }
public DateTime? StartDate { get; set; }
public int? Contact { get; set; }
public ContactModel ContactNavigation { get; set; }
}

public class TimesheetViewModel
{
public int? Contact { get; set; }
public DateTime? StartDate { get; set; }
}

public class ContactModel
{
public int Id { get; set; }
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TimesheetModel, TimesheetViewModel>()
.ForMember(d => d.Contact, o => o.MapFrom(s => s.ContactNavigation.Id))
.ReverseMap()
.ForPath(s => s.ContactNavigation.Id, opt => opt.Ignore());
});

[Fact]
public void Should_not_set_value()
{
var source = new TimesheetModel
{
Contact = 6,
ContactNavigation = new ContactModel
{
Id = 5
}
};
var dest = new TimesheetViewModel
{
Contact = 10
};
Mapper.Map(dest, source);

source.ContactNavigation.Id.ShouldEqual(5);
}
}

public class ForPathWithPrivateSetters : AutoMapperSpecBase
{
Expand Down