3

以下のコード例では、「Nullable オブジェクトには値が必要です」というメッセージが表示されます。これを機能させるために次の修正が必要なのはなぜですか。

.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

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

nullable にキャストするだけintです:

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

誰かを助けるかもしれません。

オートマッパー内の無効な値の列挙を解決しようとするコードがあったときに、この例外が発生していました。

また、オートマッパー内のロジックに基づいて例外を作成している可能性があるシナリオも考えられます。

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 に答える