Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoMapper - strongly typed data set

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)?

like image 482
redman Avatar asked Oct 03 '22 09:10

redman


1 Answers

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>>());
    
like image 81
smartcaveman Avatar answered Oct 13 '22 11:10

smartcaveman