Question

I have mappings defined like this:

Mapper.CreateMap<DsMyDataSet.TMyRow, MyRowDto>();

The MyRowDto is 1:1 copy of TMyRow but all properties are auto properties.

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
public string PositionFolder{
    get {
        try {
            return ((string)(this[this.tableTMyDataSet.PositionFolderColumn]));
        }
        catch (global::System.InvalidCastException e) {
            throw new global::System.Data.StrongTypingException("The value for column \'PositionFolder\' in table \'TMyDataSet\' is DBNull.", e);
        }
    }
    set {
        this[this.tableTMyDataSet.PositionFolderColumn] = value;
    }
}

When I call:

DsMyDataSet.TMyRow row = ....;
AutoMapper.Mapper.Map<MyRowDto>(row);

I get the StrongTypingException exception because the value in the column is null. The property is nullable but strongly typed data sets do not support nullable properties and you have to call IsNullable instea. How do I get around this problem in AutoMapper so that the mappings procceeds (ignoring the error and leaving null value)?

Was it helpful?

Solution

I think the easiest way to solve this problem is to use the IMemberConfigurationExpression<DsMyDataSet.TMyRow>.Condition() method and use a try-catch block to check if accessing the source value throws a StrongTypingException.

Here's what your code would end up looking like:

Mapper.CreateMap<DsMyDataSet.TMyRow, MyRowDto>()
      .ForMember( target => target.PositionFolder,
        options => options.Condition(source => { 
             try { return source.PositionFolder == source.PositionFolder; }
             catch(StrongTypingException) { return false; } 
      });

If this is a common occurrence then you have a few other options to avoid writing all this code for each member.

One way is to use an extension method:

Mapper
.CreateMap<Row,RowDto>()
.ForMember( target => target.PositionFolder, options => options.IfSafeAgainst<Row,StrongTypingException>(source => source.PositionFolder) )

when the following is in the solution:

 public static class AutoMapperSafeMemberAccessExtension
 {
     public static void IfSafeAgainst<T,TException>(this IMemberConfigurationExpression<T> options, Func<T,object> get)
         where TException : Exception
     {
         return options.Condition(source => {
             try { var value = get(source); return true; }
             catch(TException) { return false; }
         });
     }
 } 

AutoMapper also has some built in extensibility points that could be leveraged here as well. A couple possibilities that jump out at me are:

  1. Define a custom IValueResolver implementation. There is already a similar implementation in the solution that you could use: the NullReferenceExceptionSwallowingResolver. You could probably copy that code and then just change the part that specifies what kind of exception you're working with. Documentation for configuration is on the AutoMapper wiki, but the configuration code would look something like:

    Mapper.CreateMap<Row,RowDto>()
      .ForMember( target => target.PositionFolder, 
            options => options.ResolveUsing<ExceptionSwallowingValueResolver<StrongTypingException>>());
    

OTHER TIPS

Use this mapping:

Mapper.CreateMap<DsMyDataSet.TMyRow, MyRowDto>()
    .ForMember(s => s.PositionFolder, o => o.MapFrom(d => !d.IsPositionFolderNull() ? d.PositionFolder: null));

In newer versions of Automapper, it is possible to generally prevent DataRow properties from being mapped if they are DBNull by using IMemberConfigurationExpression<TSource, TDestination, TMember>.PreCondition():

      var config = new MapperConfiguration(
        cfg =>
          {
            cfg.CreateMap<DsMyDataSet.TMyRow, MyRowDto>();

            cfg.ForAllMaps((typeMap, map) =>
              {
                map.ForAllMembers(opt =>
                  {
                    opt.PreCondition((src, context) =>
                      {
                        var row = src as DataRow;
                        if (row != null)
                        {
                          return !row.IsNull(opt.DestinationMember.Name);
                        }

                        return true;
                      });
                  });
              });
          });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top