このシナリオがあります:
public class Base { public string Name; }
public Class ClassA :Base { public int32 Number; }
public Class ClassB :Base { public string Description;}
public Class DTO {
public string Name;
public int32 Number;
public string Description;
}
を持っています IList<Base>
私の地図は:
AutoMapper.Mapper.CreateMap<IList<Base>, IList<DTO>>()
.ForMember(dest => dest.Number, opt => opt.Ignore())
.ForMember(dest => dest.Description, opt => opt.Ignore());
AutoMapper.Mapper.CreateMap<ClassA, DTo>()
.ForMember(dest => dest.Description, opt => opt.Ignore());
AutoMapper.Mapper.CreateMap<ClassB, DTO>()
.ForMember(dest => dest.Number, opt => opt.Ignore())
Mapper.AssertConfigurationIsValid(); //Is OK!
しかし、ClassAまたはClassBにあるプロパティは、これを実行するとマップされません。
IList<DTO>= AutoMapper.Mapper.Map<IList<Base>,IList<DTO>>(baseList);
ClasA
とClassB
で定義されているプロパティをマップするにはどうすればよいですか
次のように、ドメインクラスに一致するDTOクラスを作成する必要があります。
public class DTO
{
public string Name;
}
public class DTO_A : DTO
{
public int Number { get; set; }
}
public class DTO_B : DTO
{
public string Description { get; set; }
}
次に、マッピングを次のように変更する必要があります。
Mapper.CreateMap<Base, DTO>()
.Include<ClassA, DTO_A>()
.Include<ClassB, DTO_B>();
Mapper.CreateMap<ClassA, DTO_A>();
Mapper.CreateMap<ClassB, DTO_B>();
Mapper.AssertConfigurationIsValid();
これが完了すると、以下が機能します。
var baseList = new List<Base>
{
new Base {Name = "Base"},
new ClassA {Name = "ClassA", Number = 1},
new ClassB {Name = "ClassB", Description = "Desc"},
};
var test = Mapper.Map<IList<Base>,IList<DTO>>(baseList);
Console.WriteLine(test[0].Name);
Console.WriteLine(test[1].Name);
Console.WriteLine(((DTO_A)test[1]).Number);
Console.WriteLine(test[2].Name);
Console.WriteLine(((DTO_B)test[2]).Description);
Console.ReadLine();
残念ながら、これは不要なキャストがあることを意味しますが、それについてできることは多くないと思います。
少なくとも最近のAutomapperバージョン(> 2.0?)では、最初のCreateMap
ステートメントのIList<>
:sを削除すれば、コードは問題ありません。1。また、@ Simonが別の回答で提案しているように、特定のDTOクラスを作成する必要はありません(それが必要な場合を除きます)。
ただし、継承について具体的に説明し、基本クラスを拡張するときに重複するマッピング句を回避するには、.Include
メソッドを使用して継承を指定できます。したがって、次のようにマッピングを作成すると、
Mapper.CreateMap<Base, DTO>()
.Include<ClassA, DTO>()
.Include<ClassB, DTO>()
.ForMember(dest => dest.Description, opt => opt.Ignore())
.ForMember(dest => dest.Number, opt => opt.Ignore());
Mapper.CreateMap<ClassA, DTO>()
.ForMember(dest => dest.Description, opt => opt.Ignore());
Mapper.CreateMap<ClassB, DTO>()
.ForMember(dest => dest.Number, opt => opt.Ignore());
Mapper.AssertConfigurationIsValid(); //Is OK!
その後、これを行うことができます:
var baseList = new List<Base>
{
new Base {Name = "Base"},
new ClassA {Name = "ClassA", Number = 1},
new ClassB {Name = "ClassB", Description = "Desc"},
};
var test = Mapper.Map<IList<Base>, IList<DTO>>(baseList);
Console.WriteLine(test[0].Name);
Console.WriteLine(test[1].Name);
Console.WriteLine((test[1]).Number);
Console.WriteLine(test[2].Name);
Console.WriteLine((test[2]).Description);
Console.ReadLine();
(IListを具体的にマップする必要がないことに注意してください。オートマッパーがこれを処理します。)
この記事を参照.Include
について。
1実際にコードは質問に書かれたとおりにコンパイルされているのだろうか?
Eugene Gorbovoyの回答に続き、AutoMapperの構成にプロファイルを使用している場合は、TypeConverter
を使用する必要があります。
このように新しいTypeConverter
を作成します
public class NumberConverter : ITypeConverter<DTO, NumberBase>
{
public NumberBase Convert(DTO source, NumberBase destination, ResolutionContext context)
{
if (source.Id % 2 == 0)
{
return context.Mapper.Map<EvenNumber>(source);
}
else
{
return context.Mapper.Map<OddNumber>(source);
}
}
}
そして、彼の例のConvertUsing
行を
expression.CreateMap<DTO, NumberBase>()
.ConvertUsing(new NumberConverter());
問題を解決するためにこれを行いました
IList<DTO> list1 = AutoMapper.Mapper.Map<IList<ClassA>,IList<DTO>>(baseList.OfType<ClassA>().ToList());
IList<DTO> list2 = AutoMapper.Mapper.Map<IList<ClassB>,IList<DTO>>(baseList.OfType<ClassB>().ToList());
list = list1.Union(list2);
persons.OfType<T>().ToList()
これを行うにはより良い方法でなければなりません。
シナリオでは、IMappingExpression.ConvertUsingメソッドを使用する必要があります。これを使用することで、新しく作成されたオブジェクトに適切なタイプを提供できます。私の例を見てください(あなたのシナリオにかなりよく合います):
using System;
using System.Linq;
using AutoMapper;
namespace ConsoleApplication19
{
internal class Program
{
private static void Main(string[] args)
{
//mapping
Mapper.Initialize(expression =>
{
expression.CreateMap<DTO, NumberBase>()
.ForMember(@class => @class.IdOnlyInDestination,
configurationExpression => configurationExpression.MapFrom(dto => dto.Id))
.ConvertUsing(dto =>//here is the function that creates appropriate object
{
if (dto.Id%2 == 0) return Mapper.Map<EvenNumber>(dto);
return Mapper.Map<OddNumber>(dto);
});
expression.CreateMap<DTO, OddNumber>()
.IncludeBase<DTO, NumberBase>();
expression.CreateMap<DTO, EvenNumber>()
.IncludeBase<DTO, NumberBase>();
});
//initial data
var arrayDto = Enumerable.Range(0, 10).Select(i => new DTO {Id = i}).ToArray();
//converting
var arrayResult = Mapper.Map<NumberBase[]>(arrayDto);
//output
foreach (var resultElement in arrayResult)
{
Console.WriteLine($"{resultElement.IdOnlyInDestination} - {resultElement.GetType().Name}");
}
Console.ReadLine();
}
}
public class DTO
{
public int Id { get; set; }
public int EvenFactor => Id%2;
}
public abstract class NumberBase
{
public int Id { get; set; }
public int IdOnlyInDestination { get; set; }
}
public class OddNumber : NumberBase
{
public int EvenFactor { get; set; }
}
public class EvenNumber : NumberBase
{
public string EventFactor { get; set; }
}
}