Skip to content

Commit 550cc8d

Browse files
committed
Ensuring projected enumerable members are materialised where appropriate / Simplifying ValueConverters / Increasing test coverage + removing unused code
1 parent de1d3b7 commit 550cc8d

32 files changed

+231
-257
lines changed

AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2
22
{
3+
using System;
4+
using System.Collections.Generic;
35
using System.Threading.Tasks;
46
using Infrastructure;
7+
using Microsoft.EntityFrameworkCore;
8+
using TestClasses;
59
using Xunit;
610

711
public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers<EfCore2TestDbContext>
@@ -14,5 +18,30 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context)
1418
[Fact]
1519
public Task ShouldProjectToAComplexTypeCollectionMember()
1620
=> RunShouldProjectToAComplexTypeCollectionMember();
21+
22+
[Fact]
23+
public Task ShouldMaterialiseEnumerableMembers()
24+
{
25+
return RunTest(async context =>
26+
{
27+
var order = new OrderUk
28+
{
29+
DatePlaced = DateTime.Now.AddHours(-2),
30+
Items = new List<OrderItem> { new OrderItem { ProductName = "Monster Feet" } }
31+
};
32+
33+
await context.Orders.AddAsync(order);
34+
await context.SaveChangesAsync();
35+
36+
var orderDto = await context
37+
.Orders
38+
.Project().To<OrderDto>()
39+
.FirstAsync();
40+
41+
orderDto.DatePlaced.ShouldBe(order.DatePlaced);
42+
orderDto.HasItems.ShouldBe(1);
43+
orderDto.Items.ShouldBeOfType<List<OrderItemDto>>();
44+
});
45+
}
1746
}
1847
}

AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration.Inline
22
{
3+
using System;
4+
using System.Linq;
35
using System.Threading.Tasks;
46
using Infrastructure;
57
using TestClasses;
@@ -66,5 +68,55 @@ public Task ShouldApplyAConfiguredConstantToANestedMemberInline()
6668
personDto.Address.Postcode.ShouldBe("Postcode");
6769
});
6870
}
71+
72+
[Fact]
73+
public Task ShouldApplyAConfiguredExpressionUsingSourceMembersInline()
74+
{
75+
return RunTest(async context =>
76+
{
77+
var person1 = new Person
78+
{
79+
Name = "Person One",
80+
Address = new Address { Line1 = "One Street", Line2 = string.Empty, Postcode = "PSTCD" }
81+
};
82+
83+
var person2 = new Person
84+
{
85+
Name = "Person Two",
86+
Address = new Address { Line1 = "Two Street", Line2 = "Two City", Postcode = "PSTCD" }
87+
};
88+
89+
await context.Persons.AddRange(person1, person2);
90+
await context.SaveChanges();
91+
92+
var personDtos = context
93+
.Persons
94+
.OrderBy(p => p.PersonId)
95+
.Project().To<PersonDto>(cfg => cfg
96+
.WhenMapping
97+
.From<Address>()
98+
.ProjectedTo<AddressDto>()
99+
.If(a => string.Equals(a.Line1 + (object)a.Line2, a.Line1, StringComparison.OrdinalIgnoreCase))
100+
.Map(a => a.Line1)
101+
.To(a => a.Line2))
102+
.ToArray();
103+
104+
personDtos.Length.ShouldBe(2);
105+
106+
personDtos[0].Id.ShouldBe(person1.PersonId);
107+
personDtos[0].Name.ShouldBe("Person One");
108+
personDtos[0].Address.ShouldNotBeNull();
109+
personDtos[0].Address.Line1.ShouldBe("One Street");
110+
personDtos[0].Address.Line2.ShouldBe("One Street");
111+
personDtos[0].Address.Postcode.ShouldBe("PSTCD");
112+
113+
personDtos[1].Id.ShouldBe(person2.PersonId);
114+
personDtos[1].Name.ShouldBe("Person Two");
115+
personDtos[1].Address.ShouldNotBeNull();
116+
personDtos[1].Address.Line1.ShouldBe("Two Street");
117+
personDtos[1].Address.Line2.ShouldBe("Two City");
118+
personDtos[1].Address.Postcode.ShouldBe("PSTCD");
119+
});
120+
}
69121
}
70122
}

AgileMapper.UnitTests/ShouldExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static void ShouldBe<TActual, TExpected>(this TActual value, TExpected ex
5050
{
5151
if (!AreEqual(expectedValue, value))
5252
{
53-
Asplode(expectedValue.ToString(), value.ToString());
53+
Asplode(expectedValue.ToString(), value?.ToString());
5454
}
5555
}
5656

AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToStrings.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public void ShouldMapANullNullableDoubleToAString()
175175
}
176176

177177
[Fact]
178-
public void ShouldMapAnCharacterOverAString()
178+
public void ShouldMapACharacterOverAString()
179179
{
180180
var source = new PublicField<char> { Value = 'Z' };
181181
var target = new PublicField<string> { Value = "Nope" };
@@ -184,6 +184,24 @@ public void ShouldMapAnCharacterOverAString()
184184
result.Value.ShouldBe("Z");
185185
}
186186

187+
[Fact]
188+
public void ShouldMapANullableBoolToAString()
189+
{
190+
var source = new PublicProperty<bool?> { Value = true };
191+
var result = Mapper.Map(source).ToANew<PublicField<string>>();
192+
193+
result.Value.ShouldBe("true");
194+
}
195+
196+
[Fact]
197+
public void ShouldMapANullNullableBoolToAString()
198+
{
199+
var source = new PublicProperty<bool?> { Value = null };
200+
var result = Mapper.Map(source).ToANew<PublicField<string>>();
201+
202+
result.Value.ShouldBeNull();
203+
}
204+
187205
[Fact]
188206
public void ShouldMapAnEnumOnToAString()
189207
{

AgileMapper/Extensions/Internal/ExpressionExtensions.CanBeProjected.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ public static bool Check(LambdaExpression lambda)
3030
return checker._canBeProjected;
3131
}
3232

33-
protected override Expression VisitIndex(IndexExpression indexAccess)
34-
{
35-
_canBeProjected = false;
36-
37-
return base.VisitIndex(indexAccess);
38-
}
39-
4033
protected override Expression VisitInvocation(InvocationExpression invocation)
4134
{
4235
_canBeProjected = false;

AgileMapper/Extensions/Internal/ExpressionExtensions.cs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,7 @@ public static Expression GetIsNotDefaultComparison(this Expression expression)
102102

103103
var typeDefault = expression.Type.ToDefaultExpression();
104104

105-
if (!expression.Type.IsValueType() || !expression.Type.IsComplex())
106-
{
107-
return Expression.NotEqual(expression, typeDefault);
108-
}
109-
110-
var objectEquals = typeof(object).GetPublicStaticMethod("Equals");
111-
112-
var objectEqualsCall = Expression.Call(
113-
null,
114-
objectEquals,
115-
expression.GetConversionToObject(),
116-
typeDefault.GetConversionToObject());
117-
118-
return Expression.IsFalse(objectEqualsCall);
105+
return Expression.NotEqual(expression, typeDefault);
119106
}
120107

121108
public static Expression GetIndexAccess(this Expression indexedExpression, Expression indexValue)
@@ -223,11 +210,11 @@ public static Expression GetConversionTo(this Expression expression, Type target
223210
return Expression.Convert(expression, targetType);
224211
}
225212

226-
public static bool IsLinqToArrayOrToListCall(this MethodCallExpression call)
213+
public static bool IsLinqSelectCall(this MethodCallExpression call)
227214
{
228-
return call.Method.IsStatic && call.Method.IsGenericMethod &&
229-
(ReferenceEquals(call.Method, _linqToListMethod) ||
230-
ReferenceEquals(call.Method, _linqToArrayMethod));
215+
return call.Method.IsStatic && call.Method.IsGenericMethod && ReferenceEquals(
216+
call.Method.GetGenericMethodDefinition(),
217+
EnumerablePopulationBuilder.EnumerableSelectWithoutIndexMethod);
231218
}
232219

233220
public static Expression WithOrderingLinqCall(

AgileMapper/Members/ExpressionInfoFinder.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,6 @@ private static bool IndexDoesNotUseParameter(Expression indexExpression)
242242
return true;
243243
}
244244

245-
protected override MemberBinding VisitMemberBinding(MemberBinding binding)
246-
{
247-
return base.VisitMemberBinding(binding);
248-
}
249-
250245
protected override Expression VisitMethodCall(MethodCallExpression methodCall)
251246
{
252247
if ((methodCall.Object != _mappingDataObject) &&

AgileMapper/Members/MemberTypeExtensions.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,5 @@ public static bool IsReadable(this MemberType memberType)
1313

1414
return true;
1515
}
16-
17-
public static bool IsMethod(this MemberType memberType)
18-
{
19-
switch (memberType)
20-
{
21-
case MemberType.GetMethod:
22-
case MemberType.SetMethod:
23-
return true;
24-
}
25-
26-
return false;
27-
}
2816
}
2917
}

AgileMapper/Members/QualifiedMember.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ public IQualifiedMember RelativeTo(IQualifiedMember otherMember)
249249

250250
IQualifiedMember IQualifiedMember.WithType(Type runtimeType)
251251
{
252+
if (runtimeType == Type)
253+
{
254+
return this;
255+
}
256+
252257
var typedMember = WithType(runtimeType);
253258

254259
if (runtimeType.IsDictionary())

AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,7 @@ public Expression GetEnumerableConversion(Expression instance, bool allowEnumera
135135
}
136136

137137
private static bool ValueIsNotEnumerableInterface(Expression instance)
138-
{
139-
return instance.Type != typeof(IEnumerable<>).MakeGenericType(instance.Type.GetEnumerableElementType());
140-
}
138+
=> instance.Type != typeof(IEnumerable<>).MakeGenericType(instance.Type.GetEnumerableElementType());
141139

142140
public Expression GetCountFor(Expression instance, Type countType = null)
143141
=> instance.GetCount(countType, exp => CollectionInterfaceType);

0 commit comments

Comments
 (0)