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
6 changes: 5 additions & 1 deletion src/AutoMapper/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Compat issues with assembly AutoMapper:
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.AutoMapAttribute' changed from '[AttributeUsageAttribute(1036, AllowMultiple=true)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple=true)]' in the implementation.
InterfacesShouldHaveSameMembers : Interface member 'public TMappingExpression AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void AutoMapper.Configuration.MappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.IgnoreAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MapAtRuntimeAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MappingOrderAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
Expand All @@ -8,4 +12,4 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.UseExistingValueAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
Total Issues: 9
Total Issues: 13
3 changes: 2 additions & 1 deletion src/AutoMapper/Configuration/IMappingExpressionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ public interface IMappingExpressionBase<TSource, TDestination, out TMappingExpre
/// <summary>
/// Create at runtime a proxy type implementing the destination interface.
/// </summary>
void AsProxy();
/// <returns>Itself</returns>
TMappingExpression AsProxy();
/// <summary>
/// Skip normal member mapping and convert using a <see cref="ITypeConverter{TSource,TDestination}"/> instantiated during mapping
/// Use this method if you need to specify the converter type at runtime
Expand Down
3 changes: 2 additions & 1 deletion src/AutoMapper/Configuration/MappingExpressionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -520,13 +520,14 @@ public TMappingExpression IgnoreAllSourcePropertiesWithAnInaccessibleSetter()
public void ConvertUsing(Expression<Func<TSource, TDestination>> mappingFunction) =>
TypeMapActions.Add(tm => tm.CustomMapExpression = mappingFunction);

public void AsProxy()
public TMappingExpression AsProxy()
{
if (!DestinationType.IsInterface)
{
throw new InvalidOperationException("Only interfaces can be proxied. " + DestinationType);
}
TypeMapActions.Add(tm => tm.AsProxy = true);
return this as TMappingExpression;
}
}
}
117 changes: 113 additions & 4 deletions src/UnitTests/InterfaceMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,18 +309,27 @@ public class When_mapping_a_concrete_type_to_an_interface_type : AutoMapperSpecB
public class Source
{
public int Value { get; set; }
public int Value1 { get; set; }
}

public interface IDestination
{
int Value { get; set; }
int Value2 { get; set; }
string Value3 { get; set; }
}

protected override MapperConfiguration CreateConfiguration() => new(cfg =>cfg.CreateMap<Source, IDestination>().AsProxy());
protected override MapperConfiguration CreateConfiguration() => new(cfg =>cfg.CreateMap<Source, IDestination>().AsProxy()
.ForMember(x => x.Value2, o => o.MapFrom(x => x.Value1))
.ForMember(x => x.Value3, o => o.Ignore())
.AfterMap((_, d) =>
{
d.Value3 = "value 3";
}));

protected override void Because_of()
{
_result = Mapper.Map<Source, IDestination>(new Source {Value = 5});
_result = Mapper.Map<Source, IDestination>(new Source {Value = 5, Value1 = 50});
}

[Fact]
Expand All @@ -329,10 +338,110 @@ public void Should_create_an_implementation_of_the_interface()
_result.Value.ShouldBe(5);
}

[Fact]
public void Should_apply_rules_after_proxying()
{
_result.Value2.ShouldBe(50);
_result.Value3.ShouldBe("value 3");
}

[Fact]
public void Should_not_derive_from_INotifyPropertyChanged()
{
_result.ShouldNotBeOfType<INotifyPropertyChanged>();
_result.ShouldNotBeOfType<INotifyPropertyChanged>();
}
}

public class When_mapping_an_interface_type_to_a_concrete_type_and_reverse : AutoMapperSpecBase
{
public interface ISource
{
int Value { get; set; }
}

public class Destination
{
public int Value { get; set; }
}

protected override MapperConfiguration CreateConfiguration() => new(cfg =>
cfg.CreateMap<ISource, Destination>().ReverseMap());

[Fact]
public void Should_not_convert_to_interface()
{
Should.Throw<AutoMapperMappingException>(() => Mapper.Map<Destination, ISource>(new Destination {Value = 5}))
.Message.ShouldStartWith("Cannot create interface " + typeof(ISource).FullName);
}
}

public class When_mapping_an_interface_type_to_an_interface_type_and_reverse : AutoMapperSpecBase
{
public interface ISource
{
int Value { get; set; }
}

public class Source: ISource
{
public int Value { get; set; }
}

public interface IDestination
{
int Value { get; set; }
}

protected override MapperConfiguration CreateConfiguration() => new(cfg =>
cfg.CreateMap<ISource, IDestination>().AsProxy().ReverseMap().AsProxy());

[Fact]
public void Should_create_an_implementation_of_the_destination_interface()
{
var destination = Mapper.Map<ISource, IDestination>(new Source {Value = 5});
destination.Value.ShouldBe(5);
}

[Fact]
public void Should_map_implementation_of_the_interface_to_the_proxied_implementation()
{
var destination = Mapper.Map<ISource, IDestination>(new Source {Value = 5});
var reversed = Mapper.Map<IDestination, ISource>(destination);

reversed.ShouldNotBeOfType<Source>();
reversed.Value.ShouldBe(5);
}
}

public class When_mapping_a_concrete_type_to_an_interface_type_and_reverse : AutoMapperSpecBase
{
public class Source
{
public int Value { get; set; }
}

public interface IDestination
{
int Value { get; set; }
}

protected override MapperConfiguration CreateConfiguration() => new(cfg =>
cfg.CreateMap<Source, IDestination>().AsProxy().ReverseMap());

[Fact]
public void Should_create_an_implementation_of_the_destination_interface()
{
var destination = Mapper.Map<Source, IDestination>(new Source {Value = 5});
destination.Value.ShouldBe(5);
}

[Fact]
public void Should_map_implementation_of_the_interface_to_the_class()
{
var destination = Mapper.Map<Source, IDestination>(new Source {Value = 5});
var reversed = Mapper.Map<IDestination, Source>(destination);

reversed.Value.ShouldBe(5);
}
}

Expand Down Expand Up @@ -378,7 +487,7 @@ public void Should_notify_property_changes()
var count = 0;
_result.PropertyChanged += (o, e) => {
count++;
o.ShouldBeSameAs(_result);
o.ShouldBeSameAs(_result);
e.PropertyName.ShouldBe("Value");
};

Expand Down