I have this model:
namespace WebApplication1Sample.Models
{
public class ModelOne
{
public int Id { get; set; }
public float? Prop1 { get; set; }
public float? Prop2 { get; set; }
}
}
with this AutoMapper:
using WebApplication1Sample.Models;
using AutoMapper;
using Newtonsoft.Json.Linq;
using System.Reflection;
namespace App.Mappings
{
public class AutoMapperModelOne : Profile
{
public AutoMapperModelOne()
{
CreateMap<JToken, ModelOne>()
.ForAllMembers(opt =>
{
MemberInfo propDestination = opt.DestinationMember;
string propName = propDestination.Name;
opt.MapFrom(src => src.SelectToken(propName));
opt.Condition((src, des, srcMember) =>
{
if (srcMember == null)
{
return false;
}
//map all
return true;
});
});
}
}
}
In the automapper, I check if the srcMember
is null, if it is I don't map it.
which I call with this:
[HttpPost]
[Route("mo")]
public void ModelOneMap()
{
JToken tokenSample = new JObject();
tokenSample["Id"] = 1232323;
tokenSample["Prop1"] = 1;
tokenSample["Prop2"] = null;
ModelOne app = _mapper.Map<ModelOne>(tokenSample);
Console.WriteLine(app.Id);
}
As you can see, Prop2 is nullable. But when I try to pass null into it, it throws an exception:
AutoMapper.AutoMapperMappingException: Error mapping types.
Mapping types:
JToken -> ModelOne
Newtonsoft.Json.Linq.JToken -> WebApplication1Sample.Models.ModelOne
Type Map configuration:
JToken -> ModelOne
Newtonsoft.Json.Linq.JToken -> WebApplication1Sample.Models.ModelOne
Destination Member:
Prop2
---> System.ArgumentException: Can not convert Null to Single.
at Newtonsoft.Json.Linq.JToken.op_Explicit(JToken value)
at lambda_method4(Closure , Object , ModelOne , ResolutionContext )
--- End of inner exception stack trace ---
at lambda_method4(Closure , Object , ModelOne , ResolutionContext )
at WebApplication1Sample.Controllers.WeatherForecastController.ModelOneMap() in C:\Users\prosy.arceno\Desktop\C sharp\WebApplication1Sample\WebApplication1Sample\Controllers\WeatherForecastController.cs:line 35
at Microsoft.Extensions.Internal.ObjectMethodExecutor.<>c__DisplayClass33_0.<WrapVoidMethod>b__0(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.VoidResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
I don't understand why it's behaving this way since my destination property is nullable float.
How do I achieve the result that I want where I refrain from mapping all null values from source into the destination?
You could try check with precondition like:
var nullable = opt.DestinationMember.GetMemberType().IsNullableType();
opt.PreCondition((src,context) =>
{
if (nullable&& src.SelectToken(opt.DestinationMember.Name).Type== JTokenType.Null)
{
return false;
}
else
{
return true;
}
});