8

I am throwing custom exceptions inside my resolvers, but they are being caught and wrapped by Automapper so we cant handle them elsewhere in the program. I've included a simple example of the problem, the desired outcome is to catch an InterfaceError but it only catches an AutoMapperException with the InterfaceError as an inner exception.

In class:

public Order MapOrder(InterfaceOrder iOrder)
{
    try
    {
        Order mappedOrder = Mapper.Map<InterfaceOrder, Order>(iOrder);
    }
    catch (InterfaceException ex)
    {
        Log("Interface error");
    }
    catch (Exception ex) 
    {
        Log("error");  
    }

    return mappedOrder;
}

Mapping:

Mapper.CreateMap<InterfaceOrder, Order>()                
      .ForMember(c => c.Name, op => op.ResolveUsing(data =>
        {
            if (Name.Length > 50) 
            { 
                throw new InterfaceException("Error!", ex);                                                                   
            }
            else 
            {
                return c.Name
            }
        }));
2
  • Duplicate question stackoverflow.com/questions/22350603/… Commented Mar 14, 2014 at 15:57
  • I saw your question but I felt mine is different in the sense that I'm not having a problem catching the exception, my problem is that the exception caught is an AutoMapper exception with the InterfaceException as an inner exception. I want to just catch the InterfaceException. Thank you. Commented Mar 17, 2014 at 9:43

4 Answers 4

10

Automapper doesn't let an exception escape directly from the mapping process, but tries and wrap it in its own Exception as you noticed. Your best bet would be to wrap the call to Map inside a function that catches AutoMapperMappingException and unwrap it

public ToClass Map<FromClass, ToClass>(FromClass fc)
{
    try
    {
        return Mapper.Map<FromClass, ToClass>(fc);
    }
    catch(AutoMapperMappingException autoMapperException)
    {
        throw autoMapperException.InnerException; 
        // this will break your call stack
        // you may not know where the error is called and rather
        // want to clone the InnerException or throw a brand new Exception
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

This doesn't work for me. InnerException is still "AutoMapperMappingException", not my custom exception
Ok, fixed it. It should be autoMapperException.InnerException.InnerException
1

An interesting note. The wrapping of the exception only happens, if it is thrown from ForMember(...MapFrom(...)) call. If the same exception is thrown in AfterMap(...), it is not wrapped.

1 Comment

For the sake of completness, BeforeMap() works too. Useful for precondition checks.
0

Based on comments from @Emaro I tried with BeforeMap()

public AgentMapper()
{
CreateMap<AgentDto,Agent> ().BeforeMap((src, dest) => CheckTypeExits(src.Ty))
.ForMember(dest => dest.Type, opt => opt.MapFrom<CustomResolverEnumToInt, string>(src => src.Type))
.ForMember(dest => dest.Settings, opts => opts.MapFrom(src => src.Settings));
}

///Checks if the type exists in enum if now will throw a bad request which was handled using a custom exception filter
public void CheckTypeExits(dynamic source)
{
var type= typeof(int) == source.GetType() ? source : source.Replace(" ", "_");
bool enumExists = Enum.IsDefined(typeof(typeEnum), type);
if (!enumExists)
        {
           throw new BadHttpRequestException(Resource.getMessage(nameof(Resource.ERP_TYPE_DOES_NOT_EXIST), new List<string>() { source }));
        }
}

///Function is to convert int to string enum values
public class CustomResolverEnumToInt : IMemberValueResolver<object, object, string, int>
{
       public int Resolve(object source, object destination, string sourceMember, int destMember, ResolutionContext context)
      {
           var erpType = sourceMember.Replace(" ", "_");
           return (int)(ERPTypeEnum)Enum.Parse(typeof(ERPTypeEnum), erpType);
      }
}


BeforeMap() worked fine for me.

Comments

0

Although late to the party and not a hundert percent to the question raised here, there is another solution to the underlaying problem, raising custom messages for mapping-problems:

You could use DataAnnotations at the source type where data types still are "open", like:

[EnumDataType(typeof(someType), ErrorMessage = "some_property is invalid")]
[Required(ErrorMessage = "some_property is missing")]
[StringLength(2, ErrorMessage = "some_property is too long")]
public string some_property{ get; set; }

And then you can do a manual Object validation, which will give you a list of all error messages that apply. If everything is ok, do the "normal" mapping into the destination type.

var context = new ValidationContext(sourceObject);
validationResults.Clear();

if (Validator.TryValidateObject(sourceObject, context, validationResults, true))
{
    var destinationObject= mapper.Map<destinationType>(sourceObject);
}
else
{
    // handle errors in validationResults, like thwrowing exceptions, etc...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.