RPM1984
RPM1984

Reputation: 73112

AutoMapper Map If Not Null, Otherwise Custom Convert

Here's my code:

Mapper.CreateMap<Foo, Foo2>()
   .ForMember(dest => dest.Bar, opt => opt.MapFrom(src => src.Bar == null ? new BarViewModel() : src.Bar))

Basically, "BarViewModel" has a parameterless ctor which sets up properties in the class.

So i'm trying to say to AutoMapper:

If the value is null, then use the ctor for the class. otherwise use the mapping you have in place

The above is giving me a C# compiler error. And i'm guessing a cast wouldn't work either.

So is there a AutoMapper trick to do this?

Worst case i could remove that mapping for that property, and just do:

var mapped = Mapper.Map<Foo,Foo2>(src);
if (mapped.Bar == null) mapped.Bar = new BarViewModel();

But that's a tad ugly.

Ideas?

Upvotes: 36

Views: 50837

Answers (5)

DLeh
DLeh

Reputation: 24395

This can be done with the PreCondition() method. Here's an extension method I wrote to do this more easily:

    public static IMappingExpression<TSource, TDestination> MapIf<TSource, TDestination>(
        this IMappingExpression<TSource, TDestination> map, Expression<Func<TDestination, object>> selector,
        Func<TSource, bool> mapIfCondition, Expression<Func<TSource, object>> mapping)
    {
        map.ForMember(selector, c =>
        {
            c.MapFrom(mapping);
            c.PreCondition(mapIfCondition);
        });
        return map;
    }

Usage Example:

//if settings are null coming from the sender, then ignore them and keep the current settings
CreateMap<PresentationView, Presentation>()
   .MapIf(x => x.Settings, x => x.Settings is not null, v => v.Settings!)

Upvotes: 5

spottedmahn
spottedmahn

Reputation: 15991

As of Automapper 8, ResolveUsing is no longer an option but inline Func's, IValueResolver and IMemberValueResolver are 😊.

Inline Func Example

Mapper.Initialize(cfg =>
{
  cfg.CreateMap<Foo, FooViewModel>()
     .ForMember(dest => dest.BarViewModel,
       opt  => opt.MapFrom((src, dest) =>
       {
         if (src.Bar == null)
           return new BarViewModel ();

           return Mapper.Map<Bar, BarViewModel>(src.Bar);
        }));

  cfg.CreateMap<Bar, BarViewModel>();
});

IMemberValueResolver Example

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<Foo, FooViewModel>()
       .ForMember(dest => dest.BarViewModel,
                  opt  => opt.MapFrom<NullBarResolver, Bar>(src => src.Bar));

    cfg.CreateMap<Bar, BarViewModel>();
});

public class NullBarResolver : IMemberValueResolver<object, object, Bar, BarViewModel>
{
    public BarViewModel Resolve(object source, object destination, Bar sourceMember,
                                BarViewModel destMember, ResolutionContext context)
    {
        if (sourceMember == null)
            return new BarViewModel();

        return Mapper.Map(sourceMember, destMember);
    }
}

There's some good documentation on Custom Value Resolvers here.

Working demo

Upvotes: 9

Vijai
Vijai

Reputation: 2509

Now you can use .NullSubstitute() to replace NULL value to some custom value in Automapper, e.g.:

CreateMap<SMModel, VM_SMModel>()
                    .ForMember(d => d.myDate, o => o.NullSubstitute(new DateTime(2017,12,12)));

Upvotes: 10

k0stya
k0stya

Reputation: 4315

You can use custom value resolver. The following should work:

Mapper.CreateMap<Foo, Foo2>()
   .ForMember(dest => dest.Bar, opt => opt.ResolveUsing(src => src.Bar == null ? new Bar() : Mapper.Map<Bar,Bar2>(src.Bar)))

Upvotes: 38

devuxer
devuxer

Reputation: 42354

I don't get a compiler error for the following:

public class Foo
{
    public Bar Bar { get; set; }
}

public class Foo2
{
    public Bar Bar { get; set; }
}

public class Bar
{
    public int Id { get; set; }

    public Bar()
    {
        Id = 3;
    }
}

CreateMap<Foo, Foo2>()
    .ForMember(
        dest => dest.Bar,
        opt => opt.MapFrom(src => src.Bar == null ? new Bar() : src.Bar));

...so I'm wondering if the problem is not actually with your mapping?

Upvotes: 3

Related Questions