Skip to content

Commit bebad43

Browse files
authored
Merge pull request #61 from BlaiseD/master
Fix for Issue 33: Type Binary and Conversion expressions are not being mapped as expected.
2 parents 58f647c + 6600b12 commit bebad43

File tree

5 files changed

+154
-4
lines changed

5 files changed

+154
-4
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<Authors>Jimmy Bogard</Authors>
44
<LangVersion>latest</LangVersion>
5-
<VersionPrefix>3.1.0-preview01</VersionPrefix>
5+
<VersionPrefix>3.1.0-preview02</VersionPrefix>
66
<WarningsAsErrors>true</WarningsAsErrors>
77
<NoWarn>$(NoWarn);1701;1702;1591</NoWarn>
88
</PropertyGroup>

src/AutoMapper.Extensions.ExpressionMapping/MapperExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,30 @@ public static Dictionary<Type, Type> AddTypeMapping(this Dictionary<Type, Type>
272272
{
273273
typeMappings.AddUnderlyingTypes(configurationProvider, sourceType, destType);
274274
typeMappings.FindChildPropertyTypeMaps(configurationProvider, sourceType, destType);
275+
typeMappings.AddIncludedTypeMaps(configurationProvider, sourceType, destType);
275276
}
276277
}
277278

278279
return typeMappings;
279280
}
280281

282+
private static void AddIncludedTypeMaps(this Dictionary<Type, Type> typeMappings, IConfigurationProvider configurationProvider, Type source, Type dest)
283+
{
284+
AddTypeMaps(configurationProvider.ResolveTypeMap(source, dest));
285+
286+
void AddTypeMaps(TypeMap typeMap)
287+
{
288+
if (typeMap == null)
289+
return;
290+
291+
foreach (var includedBase in typeMap.IncludedBaseTypes)
292+
typeMappings.AddTypeMapping(configurationProvider, includedBase.SourceType, includedBase.DestinationType);
293+
294+
foreach (var includedDerived in typeMap.IncludedDerivedTypes)
295+
typeMappings.AddTypeMapping(configurationProvider, includedDerived.SourceType, includedDerived.DestinationType);
296+
}
297+
}
298+
281299
/// <summary>
282300
/// Replaces a type in the source expression with the corresponding destination type.
283301
/// </summary>

src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,28 @@ Expression DoVisitConditional(Expression test, Expression ifTrue, Expression ifF
205205
}
206206
}
207207

208+
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
209+
{
210+
if (this.TypeMappings.TryGetValue(node.TypeOperand, out Type mappedType))
211+
return MapTypeBinary(this.Visit(node.Expression));
212+
213+
return base.VisitTypeBinary(node);
214+
215+
Expression MapTypeBinary(Expression mapped)
216+
{
217+
if (mapped == node.Expression)
218+
return base.VisitTypeBinary(node);
219+
220+
switch (node.NodeType)
221+
{
222+
case ExpressionType.TypeIs:
223+
return Expression.TypeIs(mapped, mappedType);
224+
}
225+
226+
return base.VisitTypeBinary(node);
227+
}
228+
}
229+
208230
protected override Expression VisitUnary(UnaryExpression node)
209231
{
210232
return DoVisitUnary(Visit(node.Operand));
@@ -214,10 +236,10 @@ Expression DoVisitUnary(Expression updated)
214236
if (this.TypeMappings.TryGetValue(node.Type, out Type mappedType))
215237
return Expression.MakeUnary
216238
(
217-
node.NodeType,
239+
node.NodeType,
218240
updated != node.Operand
219241
? updated
220-
: node.Operand,
242+
: node.Operand,
221243
mappedType
222244
);
223245

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Shouldly;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
using System.Text;
7+
using Xunit;
8+
9+
namespace AutoMapper.Extensions.ExpressionMapping.UnitTests
10+
{
11+
public class ExpressionMappingPropertyFromDerviedType : AutoMapperSpecBase
12+
{
13+
private List<BaseEntity> _source;
14+
private IQueryable<BaseEntity> entityQuery;
15+
16+
public class BaseDTO
17+
{
18+
public Guid Id { get; set; }
19+
}
20+
21+
public class BaseEntity
22+
{
23+
public Guid Id { get; set; }
24+
}
25+
26+
public class DTO : BaseDTO
27+
{
28+
public string Name { get; set; }
29+
public string Description { get; set; }
30+
}
31+
32+
public class Entity : BaseEntity
33+
{
34+
public string Name { get; set; }
35+
}
36+
37+
protected override MapperConfiguration Configuration
38+
{
39+
get
40+
{
41+
var config = new MapperConfiguration(cfg =>
42+
{
43+
cfg.AddExpressionMapping();
44+
45+
cfg.CreateMap<BaseEntity, BaseDTO>();
46+
cfg.CreateMap<BaseDTO, BaseEntity>();
47+
48+
cfg.CreateMap<Entity, DTO>()
49+
.ForMember(dest => dest.Description, opts => opts.MapFrom(src => string.Concat(src.Id.ToString(), " - ", src.Name)))
50+
.IncludeBase<BaseEntity, BaseDTO>();
51+
cfg.CreateMap<DTO, Entity>()
52+
.IncludeBase<BaseDTO, BaseEntity>();
53+
});
54+
return config;
55+
}
56+
}
57+
58+
protected override void Because_of()
59+
{
60+
//Arrange
61+
_source = new List<BaseEntity> {
62+
new Entity { Id = Guid.NewGuid(), Name = "Sofia" },
63+
new Entity { Id = Guid.NewGuid(), Name = "Rafael" },
64+
new BaseEntity { Id = Guid.NewGuid() }
65+
};
66+
}
67+
68+
[Fact]
69+
public void Should_support_propertypath_expressions_with_properties_from_sub_types_using_explicit_cast()
70+
{
71+
// Act
72+
Expression<Func<BaseDTO, bool>> dtoQueryExpression = r => (r is DTO ? ((DTO)r).Name : "") == "Sofia";
73+
Expression<Func<BaseEntity, bool>> entityQueryExpression = Mapper.MapExpression<Expression<Func<BaseEntity, bool>>>(dtoQueryExpression);
74+
entityQuery = _source.AsQueryable().Where(entityQueryExpression);
75+
76+
// Assert
77+
entityQuery.ToList().Count().ShouldBe(1);
78+
}
79+
80+
[Fact]
81+
public void Should_support_propertypath_expressions_with_properties_from_sub_types_using_as_keyword()
82+
{
83+
// Act
84+
Expression<Func<BaseDTO, bool>> dtoQueryExpression = r => (r is DTO ? (r as DTO).Name : "") == "Sofia";
85+
Expression<Func<BaseEntity, bool>> entityQueryExpression = Mapper.MapExpression<Expression<Func<BaseEntity, bool>>>(dtoQueryExpression);
86+
entityQuery = _source.AsQueryable().Where(entityQueryExpression);
87+
88+
// Assert
89+
entityQuery.ToList().Count().ShouldBe(1);
90+
}
91+
}
92+
}

tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/XpressionMapper.ForPath.Tests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ public void Works_for_inherited_properties()
2929
Assert.True(items.Count == 1);
3030
}
3131

32+
[Fact]
33+
public void Works_for_inherited_properties_on_base_types()
34+
{
35+
//Arrange
36+
Expression<Func<RootModel, bool>> selection = s => ((DerivedModel)s).Nested.NestedTitle2 == "nested test";
37+
38+
//Act
39+
Expression<Func<DataModel, bool>> selectionMapped = mapper.MapExpression<Expression<Func<DataModel, bool>>>(selection);
40+
List<DataModel> items = DataObjects.Where(selectionMapped).ToList();
41+
42+
//Assert
43+
Assert.True(items.Count == 1);
44+
}
45+
3246
[Fact]
3347
public void Works_for_top_level_string_member()
3448
{
@@ -217,9 +231,13 @@ public class ForPathCustomerProfile : Profile
217231
{
218232
public ForPathCustomerProfile()
219233
{
234+
CreateMap<RootModel, DataModel>()
235+
.Include<DerivedModel, DerivedDataModel>();
236+
220237
CreateMap<DerivedDataModel, DerivedModel>()
221238
.ForPath(d => d.Nested.NestedTitle, opt => opt.MapFrom(src => src.Title))
222-
.ForPath(d => d.Nested.NestedTitle2, opt => opt.MapFrom(src => src.Title2));
239+
.ForPath(d => d.Nested.NestedTitle2, opt => opt.MapFrom(src => src.Title2))
240+
.ReverseMap();
223241

224242
CreateMap<OrderDto, Order>()
225243
.ForPath(o => o.CustomerHolder.Customer.Name, o => o.MapFrom(s => s.Customer.Name))

0 commit comments

Comments
 (0)