tags:

views:

131

answers:

2

I've updated my question as I realised that my code was the cause of the original issue. However in investigating the problem further I've now come across an exception that occurs in my code during the mapping process, but I am unable to capture in my mapping expression extension.

Basically the code below will throw a keynotfoundexception when "dictionaryKey" contains a value not found in the dictionary. As far as Automapper is concerned, the dictionary is held in the source object being mapped and the dictionaryKeys requested are from the properties on the target object (to be mapped to):

public dynamic GetValue(string dictionaryKey)
{
   return _dictionary[dictionaryKey].Value;
}

The automapper extension class is shown below in full, I've added comments to the line that causes the makes the call to the code above, throwing the exception. The problem is that it's not caught by it's surrounding code, instead is thrown all the way out to the Mapper.Map<...>(...) call. What causes this issue, why is the exception not caught in the try catch block (I've added break points to confirm exception is thrown in GetValue(...) by code within the try/catch block.

public static IMappingExpression<ActiveRecord, TDestination> ConvertFromDictionary<TDestination>(this IMappingExpression<ActiveRecord, TDestination> exp, Func<string, string> propertyNameMapper)
{
   foreach (
      PropertyInfo pi in typeof (TDestination).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
   {
      if (!pi.CanWrite)
         continue;

      string propertyName = pi.Name;
      propertyName = propertyNameMapper(propertyName);

      try
      {
         // The following code will fail when the target read/write property does not exist in the 
         // source dictionary.  This is thrown in GetValue as a KeyNotFoundException.  But it is not
         // caught in this try/catch.  It makes it's way all the way up to the calling code
         // i.e. var entity = Mapper.Map<ActiveRecord, EntityDetail>(activeRecord);
         exp.ForMember(propertyName, cfg => cfg.MapFrom(r => r.ActiveFields.GetValue(propertyName)));
      }
      catch (Exception ex)
      {
         // This is never reached by the exception above
         throw ex;
      }
   }
   return exp;
}

UPDATE Whilst the "key not found exception" is thrown in the GetValue call, it is wrapped up in a AutoMapper.AutoMappingException that is bubbled up on the line below:

Customer customer = Mapper.Map(record);

Of course, calling Mapper.Map for these objects will trigger my implementation of IMappingExpression to do the mapping as it was set up as:

Mapper.CreateMap().ConvertFromDictionary(propName => propName);

As Automapper is a static wrapper class around the inner workings of automapper is this the reason that the exception is not caught in the implementation of IMappingExpression, but is bubbled up to the code that triggered the map call itself?

A: 

dude this may be a silly question, but why don't you do containsKey before you allow data access to your dictionary?

stevebot
The actual question being asked here is why is the exception not handled by the try/catch block - instead it is handled by something else, re-raised and is caught higher up the stack - not something we want to happen.But it is a good point and the code here is just to replicate the issue in larger, more complext code. We don't want to add a check to ContainsKey if we can get away with it as that changes the code base just to work around automapper. Ideally our mapping expression will be able to handle that eventually (with no exceptions being raised by doing the correct checks there).
Paul Hadfield
A: 

This is a shot in the dark, but is it possible that the “Exception” being thrown actually doesn't derive from Exception? Note the answer to the question “Why is UnhandledExceptionEventArgs.ExceptionObject an object and not an Exception?”:

This cannot be typed to Exception because it's possible to throw objects in .Net that do not derive from System.Exception. This is not possible in C# or VB.Net but it is possible in other CLR based languages. Hence the API must support this possibility and uses the type object.

So while it shouldn't ever be null, it may not in fact be a System.Exception.

See CLI spec section 10.5 (specifically CLS rule 40) for more details

notJim
The exception thrown by my code is KeyNotFoundException (which would be expected). In debug mode I can see this being thrown, it then ends up being a inner exception of a Automapper.AutoMapperMappingException (which in turn is an inner exception of another Automapper.AutoMapperMappingException)
Paul Hadfield

related questions