5

オートマッパーを使用して親クラスをマッピングするのに問題があります。次のクラスを指定して、マッピング プロファイルを作成しました。

マッピング クラス:

public class SourceClass
{
    public int SourceProperty1 { get; set; }
    public int SourceProperty2 { get; set; }
    public string SourceProperty3 { get; set; }
    public string SourceProperty4 { get; set; }
}

public class TargetBaseClass
{
    public int TargetProperty1 { get; set; }
    public int TargetProperty2 { get; set; }
}

public class TargetClass1: TargetBaseClass
{
    public string TargetProperty3 { get; set; }
}

public class TargetClass2: TargetBaseClass
{
    public string TargetProperty4 { get; set; }
}

地図:

public class MappingProfile: Profile
{
    protected override void Configure()
    {
        CreateMap<SourceClass, TargetBaseClass>()
            .Include<SourceClass, TargetClass1>()
            .Include<SourceClass, TargetClass2>()
            .ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
            .ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));
        CreateMap<SourceClass, TargetClass1>()
            .ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3));
        CreateMap<SourceClass, TargetClass2>()
            .ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4));
    }
}

そして最後に私のプログラム:

static void Main(string[] args)
{
    Mapper.Initialize(x => x.AddProfile<MappingProfile>());
    var sourceClass = new SourceClass
        {
            SourceProperty1 = 1,
            SourceProperty2 = 2,
            SourceProperty3 = "3",
            SourceProperty4 = "4"
        };
    var targetBaseClass = Mapper.Map<TargetBaseClass>(sourceClass);
    var targetClass1 = Mapper.Map<TargetClass1>(sourceClass);
    var targetClass2 = Mapper.Map<TargetClass2>(sourceClass);

    Console.WriteLine("TargetBaseClass: {0} {1}", targetBaseClass.TargetProperty1,
                      targetBaseClass.TargetProperty2); //1 2
    Console.WriteLine("TargetClass1: {0} {1} {2}", targetClass1.TargetProperty1, targetClass1.TargetProperty2,
                      targetClass1.TargetProperty3);//0 0 3 ???
    Console.WriteLine("TargetClass2: {0} {1} {2}", targetClass2.TargetProperty1, targetClass2.TargetProperty2,
                      targetClass2.TargetProperty4);//1 2 4
}

問題は、派生クラスにマッピングしようとすると、親クラスのプロパティが の場合はマッピングされませんが、 の場合はマッピングさTargetClass1れることですTargetClass2。何が間違っているのか、なぜこれら 2 つのマップの動作が異なるのか、誰かに説明してもらえますか? (私が重要な順序はありIncludeますか?)

編集:よく調べてみると、順序確かに重要です。ただし、なぜ秒だけIncludeがカウントされるのかはまだわかりません。

Edit2: @GruffBunnyのコメントに基づいて、拡張メソッドを使用してこれを「修正」できると思います。しかし、なぜ彼らがこのようにしたのか、私にはよくわかりません。のコードを見るとAutoMapper.TypeMap、次のことがはっきりとわかります。

public void IncludeDerivedTypes(Type derivedSourceType, Type derivedDestinationType)
{
    _includedDerivedTypes[derivedSourceType] = derivedDestinationType;
}

明らかにこれは、含まれるソース タイプごとに 1 つの宛先しか指定できないことを意味します。ただし、複数の宛先タイプをサポートすることを妨げるものは何もありません。

4

2 に答える 2

5

このカスタム拡張メソッドを見ることができます。ソースコードは Githubこちらにもあります。

拡張メソッドには、今考えられるツリーの欠点があります。1 つ目はMapper.AssertConfigurationIsValid()、ベース マップで定義されたプロパティ マッピングが見つからないため、 が失敗することです。これに対する解決策は、ベース マップで定義された提供されたメンバー マッピングを無視することです。

Mapper2 つ目は、拡張メソッドが静的クラスに依存していることです。それが AutoMapper の使用方法であれば、問題はありません。複数のマッピング エンジンを使用している場合や、AutoMapper のインターフェイスに対してコードを記述している場合は、この拡張メソッドを使用できません。両方のケースをサポートするには、2 つのオプション引数を追加する必要があります:IConfigurationProviderIMappingEngine.

静的Mapperクラスの使用を避け、必要な場所にIoCコンテナーを介してインターフェイスを挿入するようにしています。

3 つ目は、拡張メソッドが を返さずIMappingExpression<TSource, TDestination>、基本メンバー マッピングのオーバーライドを禁止することです。これを修正するには、 を返し、 IMappingExpression<TSource, TDestination>すべてのメンバーの条件を削除します。

これにより、次のコードが生成されます。

public enum WithBaseFor
{
    Source,
    Destination,
    Both
}

public static class AutoMapperExtensions
{
    public static IMappingExpression<TSource, TDestination> InheritMappingFromBaseType<TSource, TDestination>(
        this IMappingExpression<TSource, TDestination> mappingExpression,
        WithBaseFor baseFor = WithBaseFor.Both,
        IMappingEngine mappingEngine = null,
        IConfigurationProvider configurationProvider = null)
    {
        Type sourceType = typeof (TSource);
        Type destinationType = typeof (TDestination);

        Type sourceParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Source
            ? sourceType.BaseType
            : sourceType;

        Type destinationParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Destination
            ? destinationType.BaseType
            : destinationType;

        mappingExpression
            .BeforeMap((sourceObject, destObject) =>
            {
                if (mappingEngine != null)
                    mappingEngine.Map(sourceObject, destObject, sourceParentType, destinationParentType);
                else
                    Mapper.Map(sourceObject, destObject, sourceParentType, destinationParentType);
            });

        TypeMap baseTypeMap = configurationProvider != null
            ? configurationProvider.FindTypeMapFor(sourceParentType, destinationParentType)
            : Mapper.FindTypeMapFor(sourceParentType, destinationParentType);

        if (baseTypeMap == null)
        {
            throw new InvalidOperationException(
                string.Format("Missing map from {0} to {1}.", new object[]
                {
                    sourceParentType.Name,
                    destinationParentType.Name
                }));
        }

        foreach (PropertyMap propertyMap in baseTypeMap.GetPropertyMaps())
            mappingExpression.ForMember(propertyMap.DestinationProperty.Name, opt => opt.Ignore());

        return mappingExpression;
    }
}

使用法

CreateMap<SourceClass, TargetBaseClass>()
    .ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
    .ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));

CreateMap<SourceClass, TargetClass1>()
    .ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3))
    .InheritMappingFromBaseType(WithBaseFor.Destination)
    ;

CreateMap<SourceClass, TargetClass2>()
    .ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4))
    .InheritMappingFromBaseType(WithBaseFor.Destination)
    ;

カバーされていないシナリオがまだいくつかあるかもしれませんが、問題は確実に修正され、特定の拡張メソッドを記述する必要はありません。

于 2013-10-18T21:51:24.427 に答える