I expect that the Destination will be constructed using the ConstructUsing and then the concrete mappings will happen. I am not seeing my values mapped on the concrete mappings. Is there something wrong with the mappings?
Profile
CreateMap<TaskCustomProperty,TaskCustomPropertyDTO> ()
.ConstructUsing (t => {
switch (t.Type) {
case "string":
return new TaskCustomPropertyString ();
case "numeric":
return new TaskCustomPropertyNumeric ();
case "choices":
return new TaskCustomPropertyChoices ();
default:
throw new ArgumentOutOfRangeException ();
}
})
.ForMember (property => property.Task, expression => expression.Ignore ())
.Include<TaskCustomProperty, TaskCustomPropertyChoices> ()
.Include<TaskCustomProperty, TaskCustomPropertyNumeric> ()
.Include<TaskCustomProperty, TaskCustomPropertyString> ();
CreateMap<TaskCustomProperty, TaskCustomPropertyChoices> ()
.ForMember (property => property.Values, expression => expression.MapFrom (property => property.Choices != null ? string.Join ("|", property.Choices) : null))
.ForMember (property => property.StringValue, expression => expression.Ignore ());
CreateMap<TaskCustomProperty, TaskCustomPropertyNumeric> ()
.ForMember (property => property.Task, expression => expression.Ignore ())
.ForMember (property => property.NumericValue, expression => expression.Ignore ());
CreateMap<TaskCustomProperty, TaskCustomPropertyString> ()
.ForMember (property => property.Task, expression => expression.Ignore ())
.ForMember (property => property.StringValue, expression => expression.Ignore ());
Classes
public class TaskCustomProperty {
public Guid? Id { get; set; }
public Guid TaskId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool IsRequired { get; set; }
public object Value { get; set; }
public string [] Choices { get; set; }
}
public abstract class TaskCustomPropertyDTO {
public Guid Id { get; set; }
public Guid TaskId { get; set; }
public string Name { get; set; }
public bool IsRequired { get; set; }
public abstract object Value { get; set; }
public abstract bool IsEmpty ();
}
public class TaskCustomPropertyString : TaskCustomPropertyDTO {
public string StringValue { get; set; }
public override object Value {
get {
return StringValue;
}
set {
StringValue = (string)value;
}
}
public override bool IsEmpty () {
return string.IsNullOrWhiteSpace (StringValue);
}
}
public class TaskCustomPropertyChoices : TaskCustomPropertyString {
public string Values { get; set; }
public string [] GetValues () {
if (string.IsNullOrEmpty (Values)) {
return new string [0];
}
return Values.Split ('|');
}
}
ConstructUsing does not work like a factory for the object.
See: https://github.com/AutoMapper/AutoMapper/issues/376
Related
Having an issue with version 6.1.1. In the below, the result of the reverse map still has the Company object populated. Per this post, which shows what I am doing below, except they are ignoring a property, and I'm ignoring a complex object.
What am I missing?
CreateMap<Item, ItemViewModel>(MemberList.Destination)
.ReverseMap()
.ForMember(x => x.Company, x => x.Ignore())
;
With AutoMapper 6.1 you could use ForPath instead ForMember to ignore complex objects.
See How to ignore property with ReverseMap for further information.
I see not what is wrong, but here is a running sample:
namespace AutomapperTest2
{
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantEducation, ApplicantEducationVM>();
cfg.CreateMap<Applicant, ApplicantVM>().ReverseMap()
.ForMember(x => x.Education, x => x.Ignore());
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
Applicant ap = new Applicant
{
Name = "its me",
Education =
new ApplicantEducation
{
SomeInt = 10,
SomeString = "sampleString"
}
};
// Map
ApplicantVM apVm = Mapper.Map<Applicant, ApplicantVM>(ap);
Applicant apBack = Mapper.Map<ApplicantVM, Applicant>(apVm);
}
#endregion
}
/// Your source classes
public class Applicant
{
public ApplicantEducation Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducation
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
// Your VM classes
public class ApplicantVM
{
public string Description { get; set; }
public ApplicantEducationVM Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducationVM
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
}
}
When you try to create a one-to-many relationship from the use a composite key, I get the error "the sequence contains more than one matching element"
Help me please!
modelBuilder.Entity<PracticePilotScoringInfo>()
.HasKey(info => new { info.DriverName, info.Control,info.VehicleClass, info.ScoringInfoId });
modelBuilder.Entity<PracticeScoringInfo>()
.HasKey(info => info.Id)
.HasMany(info => info.PracticePilotScoringInfos)
.WithRequired(info => info.ScoringInfo)
.HasForeignKey(info => new { info.DriverName, info.Control, info.VehicleClass, info.ScoringInfoId });
public class PracticeScoringInfo : ScoringInfo
{
public int GrandPrixId { get; set; }
public GrandPrix GrandPrix { get; set; }
public virtual ICollection<PracticePilotScoringInfo> PracticePilotScoringInfos { get; set; }
}
public class PracticePilotScoringInfo : PilotScoringInfo
{
public string DriverName { get; set; }
public ControlType Control { get; set; }
public string VehicleClass { get; set; }
public Guid ScoringInfoId { get; set; }
public virtual PracticeScoringInfo ScoringInfo { get; set; }
}
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
Looks like I'm not identified in Fluent API mapping.
declaration a below is working fine
modelBuilder.Entity<ScoringInfo>()
.HasKey(info => info.Id);
modelBuilder.Entity<PracticeScoringInfo>()
.HasKey(info => info.Id);
modelBuilder.Entity<PilotScoringInfo>()
.HasKey(info => new { info.DriverName, info.Control, info.VehicleClass, info.ScoringInfoId });
modelBuilder.Entity<PracticePilotScoringInfo>()
.HasKey(info => new { info.DriverName, info.Control, info.VehicleClass, info.ScoringInfoId });
I am trying to map two objects that are mostly similar with AutoMapper but one member (AudioSummary) raises the following exception :
The following property on EchoNestModel.AudioSummary cannot be mapped: AudioSummary
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type EchoNestModel.AudioSummary.
Context:
- Mapping to property AudioSummary from EchoNest.Api.AudioSummary to EchoNestModel.AudioSummary
- Mapping from type EchoNest.Api.TrackProfile to EchoNestModel.Profile
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
Mapping definition
var map = Mapper.CreateMap<TrackProfile, Profile>();
map.ForMember(dest => dest.ForeignIds, opt => opt.ResolveUsing<ForeignIdResolver>());
map.ForMember(dest => dest.ForeignReleaseIds, opt => opt.ResolveUsing<ForeignReleaseIdResolver>());
map.ForMember(s => s.Media, t => t.Ignore());
map.ForMember(s => s.ProfileId, t => t.Ignore());
map.ForMember(s => s.AudioSummary, t => t.MapFrom(s => s.AudioSummary));
I've added the following two lines but a totally different error occurs :
map.ForMember(s => s.AudioSummary.Profile, t => t.Ignore());
map.ForMember(s => s.AudioSummary.AudioSummaryId, t => t.Ignore());
Expression 's => s.AudioSummary.Profile' must resolve to top-level member and not any child object's properties.
Use a custom resolver on the child type or the AfterMap option instead.
Parameter name: lambdaExpression
How can I successfully map AudioSummary ?
Source object
Target object
EDIT: In general, try AutoMapper.Mapper.AssertConfigurationIsValid();, this will show you all possible problems in your mapper setup.
From the information you provided, it looks like you need to define map for the AudioSummary classes (dest and source) as well:
[TestFixture]
public class MappingTest
{
public class SourceAudioSummary
{
public int Id { get; set; }
public string OtherData { get; set; }
}
public class TrackProfile
{
public string Whatever { get; set; }
public SourceAudioSummary AudioSummary { get; set; }
}
public class DestAudioSummary
{
public int Id { get; set; }
public string OtherData { get; set; }
}
public class Profile
{
public string Whatever { get; set; }
public DestAudioSummary AudioSummary { get; set; }
}
[Test]
public void Mapping()
{
Mapper.CreateMap<SourceAudioSummary, DestAudioSummary>();
Mapper.CreateMap<TrackProfile, Profile>();
var trackProfile = new TrackProfile
{
Whatever = "something",
AudioSummary = new SourceAudioSummary
{
Id = 1,
OtherData = "other"
}
};
var profile = Mapper.Map<TrackProfile, Profile>(trackProfile);
Assert.That(profile.Whatever == "something");
Assert.That(profile.AudioSummary.Id == 1);
Assert.That(profile.AudioSummary.OtherData == "other");
}
}
i want register custo generic type like default generic type that register in autoMaper (like List, Array) in AutoMappper.
i have one generic type in project this Code :
class PagedResult<T>
{
public List<T> PageOfResults { get; set; }
public int TotalItems { get; set; }
}
and Dto Class is:
class StudentDto
{
public int ID { get; set; }
public string Name { get; set; }
}
and VM Model is :
class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and service class:
class MyServie
{
public PagedResult<StudentDto> Swap()
{
var test2 = new PagedResult<StudentDto>();
test2.PageOfResults = new List<StudentDto>();
test2.PageOfResults.Add(new StudentDto() { ID = 10, Name = "Ten" });
test2.TotalItems = 10;
return test2;
}
}
i want use from AutoMapper Object Manager for register PagedResult<> in automapper but i can not do this
var allMappers = MapperRegistry.AllMappers();
MapperRegistry.AllMappers = () => new IObjectMapper[]{
new IdentifiableMapper(),
}.Concat(allMappers);
var service = new MyServie();
PagedResult<StudentDto> pageableStudentDto = service.Swap();
Mapper.CreateMap<StudentDto, StudentVM>();;
PagedResult<StudentVM> vm = Mapper.Map<PagedResult<StudentDto>, PagedResult<StudentVM>>(pageableStudentDto);
and implement of
public class PageOfMapper : IObjectMapper
{
public bool IsMatch(ResolutionContext context)
{
return typeof(PagedResult<>).IsAssignableFrom(context.SourceType.GetGenericTypeDefinition()) &&
typeof(PagedResult<>).IsAssignableFrom(context.DestinationType.GetGenericTypeDefinition());
//return true;
}
public object Map(ResolutionContext context, IMappingEngineRunner mapper)
{
// please help me in this code for map ******************
return null;
}
}
I am trying to map between two lists of objects. The source type has a complex property of type A; the destination type is a flattened subset of type A plus an additional scalar property that is in the source type.
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
}
If it's not clear, I'd like Source.MyA.Name to map to Destination.Name and Source.SomeOtherValue to map to Destination.SomeOtherValue.
In reality, type A has a dozen or so properties, about which 80% map over to properties of the same name in Destination. I can get things to work if I explicitly spell out the mappings in CreateMap like so:
CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.MyA.Name));
The downside here is I want to avoid having to add a ForMember line for each of A's properties that need to get copied over to Destination. I was hoping I could do something like:
CreateMap<Source, Destination>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.MyA));
But if I try the above I get a runtime error when the mapping is registered: "Custom configuration for members is only supported for top-level individual members on a type."
Thanks
create mappings between A and Destination, and Source and Destination, and then use AfterMap() to use first mapping in second
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.AfterMap((s, d) => Mapper.Map<A, Destination>(s.MyA, d));
then use it like this:
var res = Mapper.Map<Source, Destination>(new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } });
As a workaround you can use custom type converter with additional property in the destination type to avoid recursion.
[TestFixture]
public class MapComplexType
{
[Test]
public void Map()
{
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>().ConvertUsing(new TypeConvertor());
var source = new Source
{
MyA = new A
{
Name = "Name"
},
SomeOtherValue = 5
};
var dest = new Destination();
Mapper.Map(source, dest);
Assert.AreEqual(dest.Name, "Name");
}
}
public class TypeConvertor : ITypeConverter<Source, Destination>
{
public Destination Convert(ResolutionContext context)
{
var destination = (Destination) context.DestinationValue;
if (!((Destination)context.DestinationValue).IsMapped || destination == null)
{
destination = destination ?? new Destination();
destination.IsMapped = true; // To avoid recursion
Mapper.Map((Source)context.SourceValue, destination);
destination.IsMapped = false; // If you want to map the same object few times
}
Mapper.Map(((Source)context.SourceValue).MyA, destination);
return (Destination)context.DestinationValue;
}
}
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
// Used only for mapping purposes
internal bool IsMapped { get; set; }
}
Try this,
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.ForMember(destination => destination.Name, options => options.MapFrom(source => Mapper.Map<A, Destination>(source.MyA).Name));
var objSource = new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } };
var result = Mapper.Map<Source, Destination>(objSource);