diff --git a/src/AutoMapper/Mappers/ExpressionMapper.cs b/src/AutoMapper/Mappers/ExpressionMapper.cs index ac4ee454a0..6c67aa8a8b 100644 --- a/src/AutoMapper/Mappers/ExpressionMapper.cs +++ b/src/AutoMapper/Mappers/ExpressionMapper.cs @@ -91,13 +91,16 @@ protected override Expression VisitBinary(BinaryExpression node) var newLeft = Visit(node.Left); var newRight = Visit(node.Right); - if (newLeft.Type != newRight.Type && newRight.Type == typeof(string)) + // check if the non-string expression is a null constent + // as this would lead to a "null.ToString()" and thus an error when executing the expression + if (newLeft.Type != newRight.Type && newRight.Type == typeof(string) && !IsNullConstant(newLeft)) newLeft = Call(newLeft, typeof(object).GetDeclaredMethod("ToString")); - if (newRight.Type != newLeft.Type && newLeft.Type == typeof(string)) + if (newRight.Type != newLeft.Type && newLeft.Type == typeof(string) && !IsNullConstant(newRight)) newRight = Call(newRight, typeof(object).GetDeclaredMethod("ToString")); CheckNullableToNonNullableChanges(node.Left, node.Right, ref newLeft, ref newRight); CheckNullableToNonNullableChanges(node.Right, node.Left, ref newRight, ref newLeft); return MakeBinary(node.NodeType, newLeft, newRight); + bool IsNullConstant(Expression expression) => expression is ConstantExpression constant && constant.Value == null; } private static void CheckNullableToNonNullableChanges(Expression left, Expression right, ref Expression newLeft, ref Expression newRight) diff --git a/src/AutoMapperSamples.EF/EffortQueryTests.cs b/src/AutoMapperSamples.EF/EffortQueryTests.cs index 0220564671..c2acb346b2 100644 --- a/src/AutoMapperSamples.EF/EffortQueryTests.cs +++ b/src/AutoMapperSamples.EF/EffortQueryTests.cs @@ -15,6 +15,9 @@ namespace AutoMapperSamples.EF [TestFixture] public class EffortQueryTests { + private Mapper _mapper; + private MapperConfiguration _config; + static EffortQueryTests() { Effort.Provider.EffortProviderConfiguration.RegisterProvider(); @@ -23,7 +26,10 @@ static EffortQueryTests() [SetUp] public void SetUp() { - Mapper.Initialize(MappingConfiguration.Configure); + //Mapper.Initialize(MappingConfiguration.Configure); + + _config = new MapperConfiguration(MappingConfiguration.Configure); + _mapper = new Mapper(_config); } [Test] @@ -34,8 +40,8 @@ public void Effort_FilterByDto() IQueryable sourceResult = new OrderDto[0] .AsQueryable() .Where(s => s.FullName.EndsWith("Bestellung")) - .Map(context.OrderSet) - .ProjectTo(); // projection added + .Map(context.OrderSet, _config) + .ProjectTo(_config); // projection added var dtos = sourceResult.ToList(); @@ -54,8 +60,8 @@ public void Effort_FilterByMappedQuery() IQueryable sourceResult = new OrderDto[0] .AsQueryable() .Where(s => s.FullName.EndsWith("Bestellung")) - .Map(context.OrderSet) - .ProjectTo(); // projection added + .Map(context.OrderSet, _config) + .ProjectTo(_config); // projection added var dtos = sourceResult.ToList(); Assert.AreEqual(2, dtos.Count); @@ -70,8 +76,8 @@ public void Effort_FilterByMappedQuery() { IQueryable sourceResult2 = new OrderDto[0] .AsQueryable() - .Map(context.OrderSet) - .ProjectTo(); // projection added + .Map(context.OrderSet, _config) + .ProjectTo(_config); // projection added var dtos2 = sourceResult .Where(s => s.FullName.EndsWith("Bestellung")) .ToList(); @@ -81,9 +87,9 @@ public void Effort_FilterByMappedQuery() catch (NotSupportedException) { } - + // Using "AsDataSource" - IQueryable sourceResult4 = context.OrderSet.UseAsDataSource(Mapper.Configuration).For(); + IQueryable sourceResult4 = context.OrderSet.UseAsDataSource(_config).For(); var dtos4 = sourceResult4.Where(d => d.FullName.EndsWith("Bestellung")).ToList(); Assert.AreEqual(2, dtos4.Count); @@ -97,7 +103,7 @@ public void Effort_FilterByMappedQuery_InnerQueryOrderedAndFiltered() { var orders = context.OrderSet.Where(o => o.Price > 85D).OrderBy(o => o.Price); - IQueryable lazilyMappedQuery = orders.UseAsDataSource(Mapper.Configuration).For(); + IQueryable lazilyMappedQuery = orders.UseAsDataSource(_mapper).For(); var dtos3 = lazilyMappedQuery .Where(d => d.FullName.EndsWith("Bestellung")).ToList(); @@ -112,7 +118,7 @@ public void Effort_OrderByDto_FullName() { var orders = context.OrderSet; - IQueryable lazilyMappedQuery = orders.UseAsDataSource(Mapper.Configuration).For(); + IQueryable lazilyMappedQuery = orders.UseAsDataSource(_mapper).For(); var dtos3 = lazilyMappedQuery .OrderBy(dto => dto.FullName).Skip(2).ToList(); @@ -121,6 +127,22 @@ public void Effort_OrderByDto_FullName() } } + [Test] + public void Effort_FilterByDto_FullName_Contains() + { + using (var context = new TestDbContext(Effort.DbConnectionFactory.CreateTransient())) + { + var orders = context.OrderSet; + + IQueryable lazilyMappedQuery = orders.UseAsDataSource(_mapper).For(); + var dtos3 = lazilyMappedQuery + .Where(dto => dto.FullName.Contains("Zalando")).ToList(); + + Assert.AreEqual(1, dtos3.Count); + Assert.AreEqual("Zalando Bestellung", dtos3.Single().FullName); + } + } + [Test] public void Effort_OrderByDto_Price() { @@ -128,7 +150,7 @@ public void Effort_OrderByDto_Price() { var orders = context.OrderSet; - IQueryable lazilyMappedQuery = orders.UseAsDataSource(Mapper.Configuration).For(); + IQueryable lazilyMappedQuery = orders.UseAsDataSource(_mapper).For(); var dtos3 = lazilyMappedQuery .OrderBy(dto => dto.Price).Skip(2).ToList(); @@ -144,7 +166,7 @@ public void Effort_FilterByMappedQuery_SkipAndTake() { var orders = context.OrderSet.OrderBy(o => o.Name); - IQueryable lazilyMappedQuery = orders.UseAsDataSource(Mapper.Configuration).For(); + IQueryable lazilyMappedQuery = orders.UseAsDataSource(_mapper).For(); var dtos3 = lazilyMappedQuery .Skip(1).Take(1).ToList(); diff --git a/src/AutoMapperSamples.OData/ODataQueryTests.cs b/src/AutoMapperSamples.OData/ODataQueryTests.cs index beb4c24290..c2cd3bb005 100644 --- a/src/AutoMapperSamples.OData/ODataQueryTests.cs +++ b/src/AutoMapperSamples.OData/ODataQueryTests.cs @@ -35,7 +35,7 @@ public virtual void SetUp() // Start OWIN host _webApp = WebApp.Start(url: _baseAddress); } - + [TearDown] public virtual void TearDown() { @@ -325,7 +325,7 @@ public void CanFilter_OrderDateGt() // Act var response = client.GetAsync(_baseAddress + "api/Orders?$filter=OrderDate gt DateTime'2015-02-01T00:00:00'").Result; - + // Assert Console.WriteLine(response); Console.WriteLine(response.Content.ReadAsStringAsync().Result); diff --git a/src/AutoMapperSamples.OData/OrdersController.cs b/src/AutoMapperSamples.OData/OrdersController.cs index ef54dd9899..e81b01dae7 100644 --- a/src/AutoMapperSamples.OData/OrdersController.cs +++ b/src/AutoMapperSamples.OData/OrdersController.cs @@ -16,11 +16,14 @@ namespace AutoMapperSamples.OData public class OrdersController : ApiController { private TestDbContext context = null; + private MapperConfiguration _config; + private Mapper _mapper; internal static Action OnException { get; set; } public OrdersController() { - Mapper.Initialize(MappingConfiguration.Configure); + _config = new MapperConfiguration(MappingConfiguration.Configure); + _mapper = new Mapper(_config); context = new TestDbContext(Effort.DbConnectionFactory.CreateTransient()); } @@ -28,7 +31,7 @@ public OrdersController() [EnableQuery] public IQueryable Get() { - return context.OrderSet.Include("Customer").UseAsDataSource(Mapper.Instance) + return context.OrderSet.Include("Customer").UseAsDataSource(_mapper) // add an optional exceptionhandler that will be invoked // in case an exception is raised upon query execution. // otherwise it would get lost on the WebApi side and all we would get would be @@ -53,10 +56,10 @@ public IQueryable Get() var customerIds = enumerator.OfType().Select(o => o.Customer.Id); // add IDs of orders var customersOrders = context.CustomerSet - .Include("Orders") - .Where(c => customerIds.Contains(c.Id)) - .Select(c => new { CustomerId = c.Id, OrderIds = c.Orders.Select(o => o.Id) }) - .ToDictionary(c => c.CustomerId); + .Include("Orders") + .Where(c => customerIds.Contains(c.Id)) + .Select(c => new { CustomerId = c.Id, OrderIds = c.Orders.Select(o => o.Id) }) + .ToDictionary(c => c.CustomerId); // apply the list of IDs to each OrderDto foreach (var order in enumerator.OfType()) {