当我使用 Condition 时,为什么会出现“可为 Null 的对象必须有值”异常?

Dav*_*tes 6 automapper

我在下面的示例代码中得到一个“可为空的对象必须有一个值”。为什么我需要以下修复才能使其正常工作:

.ForMember(dest => dest.ShirtColor,
           dest => dest.MapFrom(src => src.ShirtColor != null
                                       ? new OptionSetValue((int) src.ShirtColor)
                                       : null))
Run Code Online (Sandbox Code Playgroud)
.ForMember(dest => dest.ShirtColor,
           dest => dest.MapFrom(src => src.ShirtColor != null
                                       ? new OptionSetValue((int) src.ShirtColor)
                                       : null))
Run Code Online (Sandbox Code Playgroud)

下面是 OptionSet 的定义方式: public class OptionSetValue { public OptionSetValue(){}

AutoMapper.Mapper.CreateMap<PersonA, PersonB>()
    .ForMember(dest => dest.FirstName, dest => dest.MapFrom(src => src.FirstName))
    .ForMember(dest => dest.LastName, dest => dest.MapFrom(src => src.LastName))

    // Condition to avoid overwriting existing data!!!
    .ForMember(dest => dest.ShirtColor,
               dest => dest.Condition(src => src.ShirtColor != null))
    .ForMember(dest => dest.ShirtColor,
               dest => dest.MapFrom(
                   src => new OptionSetValue((int)src.ShirtColor)))
    // Fix that should not be needed due to condition:
    //.ForMember(dest => dest.ShirtColor,
    //           dest => dest.MapFrom(
    //               src => src.ShirtColor != null
    //                      ? new OptionSetValue((int) src.ShirtColor)
    //                      : null));


PersonA source = new PersonA();
source.FirstName = "Thomas";
source.LastName = "Jefferson";
source.ShirtColor = null;  // nullable int

PersonB destination = new PersonB();
destination.FirstName = "Thomas";
destination.LastName = "Jefferson";
destination.ShirtColor = new OptionSetValue(4);

// Results in: "Nullable object must have a value" despite the fact that
// condition should have been met!
Mapper.Map<PersonA, PersonB>(source, destination);

Debug.Assert(destination.ShirtColor != null);
Console.WriteLine("Our existing data was not overwritten!!");

Console.WriteLine("Hit enter to exit");
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)

Cic*_*cio 8

只需使用PreCondition而不是Condition..

所以改变你的

//Condition to avoid overwriting existing data!!!
.ForMember(dest => dest.ShirtColor, dest => dest.Condition(src => src.ShirtColor != null))
    .ForMember(dest => dest.ShirtColor, dest => dest.MapFrom(src => new OptionSetValue((int)src.ShirtColor)))
Run Code Online (Sandbox Code Playgroud)

//Condition to avoid overwriting existing data!!!
.ForMember(dest => dest.ShirtColor, dest => dest.PreCondition(src => src.ShirtColor != null))
    .ForMember(dest => dest.ShirtColor, dest => dest.MapFrom(src => new OptionSetValue((int)src.ShirtColor)))
Run Code Online (Sandbox Code Playgroud)


Mig*_*uke 3

你需要改变

.ForMember(dest => dest.ShirtColor,
           dest => dest.Condition(src => src.ShirtColor != null))
Run Code Online (Sandbox Code Playgroud)

.ForMember(dest => dest.ShirtColor,
           opt => opt.Condition(src => !src.IsSourceValueNull))
Run Code Online (Sandbox Code Playgroud)

请注意,您也不需要以下内容,因为它们是按约定映射的:

.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName))
Run Code Online (Sandbox Code Playgroud)

更新 - 2

该问题与条件和映射尚未组合这一事实有关。我创建了一个简单的扩展方法来实现它。

这是一个测试来证明它

课程

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PersonA : Person
{
    public int? ShirtColor { get; set; }
}

public class PersonB : Person
{
    public OptionSetValue ShirtColor { get; set; }
}

public class OptionSetValue
{
    public int OptionSet { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

映射配置

public class MyProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<PersonA, PersonB>()
              .ForMember(dest => dest.ShirtColor,
                         opt => opt.ConditionallyMapFrom(
                             src => new OptionSetValue
                                 {
                                     OptionSet = src.ShirtColor.Value
                                 },
                             src => src.ShirtColor.HasValue));
    }
}

public static class AutoMapperExtensions
{
    public static void ConditionallyMapFrom<TSource, TMember>(
                         this IMemberConfigurationExpression<TSource> expression,
                         Expression<Func<TSource, TMember>> sourceMember,
                         Func<TSource, bool> condition)
    {
        expression.Condition(condition);
        expression.MapFrom(sourceMember);
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试

[TestFixture]
public class MappingTests
{
    [Test]
    public void AutoMapper_Configuration_IsValid()
    {
        Mapper.Initialize(m => m.AddProfile<MyProfile>());
        Mapper.AssertConfigurationIsValid();
    }

    [Test]
    public void AutoMapper_ClientMapping_IsValid()
    {
        Mapper.Initialize(m => m.AddProfile<MyProfile>());
        Mapper.AssertConfigurationIsValid();

        var source = new PersonA
            {
                FirstName = "FirstA",
                LastName = "LastA",
                ShirtColor = null
            };

        var destination = new PersonB
            {
                FirstName = "FirstB",
                LastName = "LastB"
            };

        destination = Mapper.Map(source, destination);

        Assert.That(destination, Is.Not.Null);
        Assert.That(destination.FirstName, Is.EqualTo("FirstA"));
        Assert.That(destination.LastName, Is.EqualTo("LastA"));
        Assert.That(destination.ShirtColor, Is.Null);
    }
}
Run Code Online (Sandbox Code Playgroud)