3

在下面的示例代码中,我得到一个“可空对象必须有一个值”。为什么我需要以下修复来完成这项工作:

.ForMember(dest => dest.ShirtColor,
           dest => dest.MapFrom(src => src.ShirtColor != null
                                       ? new OptionSetValue((int) src.ShirtColor)
                                       : null))
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();

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

    public OptionSetValue(int value)
    {
        Number = value;
    }

    public int Number { get; set; }
}
4

4 回答 4

5

只需使用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)))

//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)))
于 2017-07-19T14:15:02.567 回答
3

你需要改变

.ForMember(dest => dest.ShirtColor,
           dest => dest.Condition(src => src.ShirtColor != null))

.ForMember(dest => dest.ShirtColor,
           opt => opt.Condition(src => !src.IsSourceValueNull))

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

.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName))

更新 - 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; }
}

映射配置

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);
    }
}

单元测试

[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);
    }
}
于 2012-12-03T22:20:42.123 回答
2

只需将其转换为可为 int的:

.ForMember((destination)         => destination.ShirtColor,
           (memberConfiguration) => memberConfiguration.MapFrom((source) => (int?)source.ShirtColor))
于 2015-09-25T09:47:21.193 回答
0

可能会帮助某人。

当有一个代码试图解析 automapper 中无效值的枚举时,我遇到了这个异常。

我也觉得其他人可能的情况可能是您正在根据您在 automapper 中的逻辑创建一个异常。

Mapper.CreateMap<Data.Employee, Domain.Entities.Employee>().
      .ForMember(dest => dest.EmployeeType, opt => opt.MapFrom(src =>(EmployeeTypeEnum)src.EmployeeTypeId))  
  // if the EmployeeTypeId has an invalid value which does not match the enum values we get exception
于 2015-08-27T09:27:45.423 回答