自动映射器 |如果不是默认/初始化值,则仅映射

Automapper | Only Map if not default / initilized value

我希望有一种通用的方法来仅在属性发生更改而不是默认值时映射它们。 所以假设你有这两个 类

public class Source
{
    public string Foo { get; set; } = "Foo";
    public string Bar { get; set; } = "Bar";
}
public class Destination
{
    public string Foo { get; set; } = "Foo of Destination";
    public string Bar { get; set; } = "Bar of Destination";
}

现在我想将 Source 映射到 Destination,如果 Source.Foo 的 属性 仍然是“Foo”,它应该更改为“Foo of Destination”,但是当 [=16 的值=] 与“Foo”(默认值)不同,它应该真正映射 属性。 我找不到使用 automapper 执行此操作的方法。

[TestMethod]
public void ChangeAllProperties()
{
    var source = new Source();
    var destination = _mapper.Map<Destination>(source);

    var defaultDestination = new Destination();

    Assert.AreEqual(destination.Bar, defaultDestination.Bar);   // Should be the Default Value of "Destination"
    Assert.AreEqual(destination.Foo, defaultDestination.Foo);   // Should be the Default Value of "Destination"
}

[TestMethod]
public void ChangeDefaultProperty()
{
    var source = new Source();
    source.Bar = "Custom Bar!";

    var destination = _mapper.Map<Destination>(source);
    var defaultDestination = new Destination();

    Assert.AreEqual(destination.Bar, source.Bar);             // Should be Value of Source
    Assert.AreEqual(destination.Foo, defaultDestination.Foo); // Should be the Default Value of "Destination"
}

[TestMethod]
public void AlLChanged()
{
    var source = new Source();
    source.Foo = "Custom Foo!";
    source.Bar = "Custom Bar!";

    var destination = _mapper.Map<Destination>(source);

    Assert.AreEqual(destination.Bar, source.Bar); // Should be Value of Source
    Assert.AreEqual(destination.Foo, source.Foo); // Should be Value of Source
}

查看 Automapper 条件映射 - link

基本上,您可以这样做(来自示例):

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<Foo,Bar>()
    .ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0)));
});

opt.Condition() 确保在实际对该特定成员进行任何映射之前满足您的条件。

Plamen Yordanov 的 opt.Condition() 想法是正确的道路。我能够创建更通用的东西,所以我不需要为每个成员定义它并检查属性的默认值。 这是我想出的解决方案。如果目标成员也存在于具有相同名称和类型的源中,它只会检查默认值。

public class FooBarProfile: Profile
{
    public FooBarProfile()
    {
        CreateMap<Source, Destination>()
            .ForAllMembers(opt => opt.Condition(src => OnlyIfChanged(src, opt.DestinationMember)));

        // And if you need reversed...
        CreateMap<Destination, Source>()
            .ForAllMembers(opt => opt.Condition(src => OnlyIfChanged(src, opt.DestinationMember)));
    }

    private static bool OnlyIfChanged<TSource>(TSource src, MemberInfo destinationMember) where TSource : new()
    {
        // Check if DestinationMember exists in TSource (same Name & Type)
        var sourceMember = typeof(TSource).GetMembers().FirstOrDefault(e => e.Name == destinationMember.Name && e.MemberType == destinationMember.MemberType);
        if (sourceMember != null)
        {
            var defaultOfSource = new TSource();
            // Map only if Value of Source is differnent than the default soruce value
            return (GetValue(sourceMember,src)?.Equals(GetValue(sourceMember,defaultOfSource)) == false);
        }
        return true;
    }


    // Helper-Method to get Value from a MemberInfo
    private static object GetValue(MemberInfo memberInfo, object forObject)
    {
        switch (memberInfo.MemberType)
        {
            case MemberTypes.Field:
                return ((FieldInfo)memberInfo).GetValue(forObject);
            case MemberTypes.Property:
                return ((PropertyInfo)memberInfo).GetValue(forObject);
            default:
                throw new ArgumentException("Member is not Field or Property.", nameof(memberInfo));
        }
    }
}