Shawn Mclean
Shawn Mclean

Reputation: 57469

Automapper null string to empty

When I try to map an object that has a null string property, the destination is also null. Is there a global settings I can turn on that says all null string should be mapped to empty?

Upvotes: 16

Views: 16636

Answers (4)

B Duffy
B Duffy

Reputation: 21

I'm using EF Reverse Engineering and it has a nasty habit of defining

string Name { get; set; } = null!;

The null on a non-nullable string causes many headaches. Finally, I added this mapping function and created "ghost" intermediate objects to absorb the mapping.

/// <inheritdoc />
[PublicAPI]
internal class MapperProfile<TSrcType, TDstType> : Profile
    where TSrcType : class
    where TDstType : class, new()
{
    public MapperProfile()
    {
        CreateMap<TSrcType, TDstType>(MemberList.None)
            .ConvertUsing((src, dst) =>
            {
                ArgumentNullException.ThrowIfNull(src);
                dst ??= new TDstType();

                var dstType = dst.GetType()!;
                var srcType = src.GetType()!;

                foreach (var dstInfo in dstType.GetProperties()
                    .Where(x => x.CanWrite)
                    .Select(x => dstType.GetProperty(x.Name)!))
                {
                    var srcInfo = srcType.GetProperties()
                        .SingleOrDefault(info => info.Name == dstType.Name && info.CanRead);

                    try
                    {
                        var value = (srcInfo is not null)
                            ? srcInfo.GetValue(src, null)
                            : dstInfo.CanRead
                                ? dstInfo.GetValue(dst, null)
                                : dstInfo.PropertyType.IsValueType
                                    ? Activator.CreateInstance(dstInfo.PropertyType)
                                    : null;   // I give up..., anyone have an idea?

                        if (dstInfo.PropertyType == typeof(string))
                            value ??= string.Empty;

                        dstInfo.SetValue(dst, value);
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine(e);
                        Debugger.Break();
                    }
                }

                return dst;
            });
    }
}

You may wish to further qualify which strings get remapped. I used the NotNullAttribute, and I had control of the sources so adding it to those strings that I wanted to insure non-null-ness was fairly easy.

Upvotes: 0

Joe
Joe

Reputation: 7311

Similar to David Wick's answer, you can also use ConvertUsing with a lambda expression, which eliminates the requirement for an additional class.

Mapper.CreateMap<string, string>().ConvertUsing(s => s ?? string.Empty);

Upvotes: 14

Nick Josevski
Nick Josevski

Reputation: 4216

If you need a non-global setting, and want to do it per property:

Mapper.CreateMap<X, Y>()
.ForMember(
    dest => dest.FieldA,
    opt => opt.NullSubstitute(string.Empty)
);

Upvotes: 20

David Wick
David Wick

Reputation: 7095

Something like this should work:

public class NullStringConverter : ITypeConverter<string, string>
  {
    public string Convert(string source)
    {
      return source ?? string.Empty;
    }
  }

And in your configuration class:

public class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.CreateMap<string, string>().ConvertUsing<NullStringConverter>();

        Mapper.AddProfile(new SomeViewModelMapper());
        Mapper.AddProfile(new SomeOtherViewModelMapper());
        ...
    }
}

Upvotes: 27

Related Questions