I am trying to map a source list to a destination array using AutoMapper.
Source classes
public class ReservationSource
{
public Travel TravelSource { get; set; }
public string SeqNo { get; set; }
}
public class Travel
{
public string TravelId { get; set; }
public ICollection<Trip> Trips { get; set; }
}
public class Trip
{
public string TrainNumber { get; set; }
public string Arrival { get; set; }
}
Destination classes
public class ReservationDestination
{
public Route[] TravelDest { get; set; }
public string SeqNumber { get; set; }
}
public class Route
{
public string SequNumber { get; set; }
public string RouteId { get; set; }
}
private static route[] GetRoutes(ICollection<Trip> trips)
{
List<route> routeList = new List<route>();
foreach (var trip trips)
{
var route = new route
{
SequNumber = trip.trainNumber
};
routeList.Add(route);
}
return routeList.ToArray();
}
Map configuration
cfg.CreateMap<ReservationSource, ReservationDestination>()
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<ReservationSource, ReservationDestination>()
.ForMember(dest => dest.SeqNumber, o => o.MapFrom(src => SeqNo))
.ForPath(dest => dest.TravelDest, o => o.MapFrom(src => GetRoutes(src)));
});
This is what I have tried, here I would like to eliminate the GetRoutes method where I will do a manual map using a foreach loop. Is it possible to use any other way without a loop?
Add mapping for Trip and Route classes.
cfg.CreateMap<Trip, Route>()
.ForMember(dest => dest.SequNumber, o => o.MapFrom(src => src.TrainNumber));
Complete Mapping Configuration
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Trip, Route>()
.ForMember(dest => dest.SequNumber, o => o.MapFrom(src => src.TrainNumber));
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<ReservationSource, ReservationDestination>()
.ForMember(dest => dest.SeqNumber, o => o.MapFrom(src => src.SeqNo))
.ForPath(dest => dest.TravelDest, o => o.MapFrom(src => src.TravelSource.Trips));
});
IMapper mapper = config.CreateMapper();
var source = new ReservationSource
{
SeqNo = "Seq001",
TravelSource = new Travel
{
TravelId = "1",
Trips = new List<Trip>
{
new Trip { TrainNumber = "A0001" },
new Trip { TrainNumber = "B0001" }
}
}
};
var destination = mapper.Map<ReservationSource, ReservationDestination>(source);
Console.WriteLine(JsonConvert.SerializeObject(destination));
Sample Program
Output
{"TravelDest":[{"SequNumber":"A0001","RouteId":null},{"SequNumber":"B0001","RouteId":null}],"SeqNumber":"Seq001"}
References
Lists and Arrays - AutoMapper documentation
Related
I'm trying to map a flat object to a list. The Id gets mapped properly, but the list is always empty. I have used reverse mapping for the ListItem in destination.
What is wrong with this code or missing please?
using AutoMapper;
MapperConfiguration config = new MapperConfiguration(x =>
{
x.CreateMap<Origin, Destination>();
x.CreateMap<Destination, NonListItem>()
.ReverseMap()
.ForPath(d => d.ListItem, o => o.MapFrom(s => new List<ListItem> {
new ListItem
{
ListInt = s.NonListInt,
ListStr = s.NonListStr
}
}));
});
var origin = new Origin();
origin.Id = 12345;
origin.NonListItem.NonListInt = 54321;
origin.NonListItem.NonListStr = "This is a test";
IMapper mapper = new Mapper(config);
var destination = mapper.Map<Destination>(origin);
Console.ReadKey();
//###############################################################+
public class Origin
{
public NonListItem NonListItem { get; set; } = new NonListItem();
public int Id { get; set; }
}
public class NonListItem
{
public int NonListInt { get; set; }
public string? NonListStr { get; set; }
}
public class Destination
{
public int Id { get; set; }
public List<ListItem> ListItem { get; set; } = new List<ListItem>();
}
public class ListItem
{
public int ListInt { get; set; }
public string? ListStr { get; set; }
}
Mapping from Destination to NonListItem is not needed. Just configure the Origin to Destination mapping.
MapperConfiguration config = new MapperConfiguration(x =>
{
x.CreateMap<Origin, Destination>()
.ForMember(dest => dest.ListItem, opt => opt.MapFrom(src => new List<ListItem>
{
new ListItem
{
ListInt = src.NonListItem.NonListInt,
ListStr = src.NonListItem.NonListStr
}
}));
});
I have:
Class A
{
public string FirstName { get; set; }
}
Class B
{
public string FirstName { get; set; }
public Guid RequestId { get; set; }
}
I want to map from A to B, since "A" doesn't have RequestId I want to set it to Guid.NewGuid()
I tried this code:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<A, B>().ForMember(m => m.RequestId, o => Guid.NewGuid());
});
_mapper = config.CreateMapper();
But I'm still getting empty Guid in RequestId.
That mapping should look like this:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<A, B>().ForMember(m => m.RequestId, o => o.MapFrom(s => Guid.NewGuid()));
});
Change mapping confugration:
cfg.CreateMapper<A,B>().ForMember(x => x.RequestId, o => o.NullSubstitute(Guid.NewGuid());
I'm trying to map a class (TrackingKeyStatic<T>) using c# and automapper.
TrackingKeyStatic<T> has the interface IBatchProcessing is inherited from Trackingkey<T> which has the interface ITrackingKey.
So by definition TrackingKeyStatic<T> is IBatchProcessing and ITrackingKey.
Automapper working fine with only one interface (IBatchProcessing)
But can't be mapped/be detect with interface ITrackingKey
I've created a fiddle to demonstrate https://dotnetfiddle.net/TO21PI
So the question is how can I map source with two interface to a concrete type<T>?
I've tried with this config, and it didn't work (which is the problem)
cfg.CreateMap<ITrackingKey, MyEntitiesDbFirstModel>()
I've tried to change the automapper config for
cfg.CreateMap<TrackingKeyStatic<NotReleventClassForThisExample>, MyEntitiesDbFirstModel>()
As demonstrate in Method TestWitTrackingKeyStaticAsSource_WORKING() its working just fine. But I can't really make a mapping for each subclass
I've tried to use method like .Include or .IncludeAllDerived, it didn't work, but I'm not quite sure if I need to use them here? Maybe I did it wrong?
Here's the unit tests I wrote for this question
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace StackOverflow
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestWithItrackingAsSource_NOTWORKING()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<ITrackingKey, MyEntitiesDbFirstModel>()
.ForMember(d => d.TrackingKey, o => o.MapFrom(s => s.NewTrackingKey));
cfg.CreateMap<IBatchProcessing, MyEntitiesDbFirstModel>()
.ForMember(d => d.Skip, o => o.MapFrom(s => s.Skip))
.ForMember(d => d.Take, o => o.MapFrom(s => s.Take))
.ForMember(d => d.Total, o => o.MapFrom(s => s.Total));
});
var mapper = config.CreateMapper();
var source = new TrackingKeyStatic<NotReleventClassForThisExample>()
{
Skip = 10,
Take = 50,
Total = 123456,
NewTrackingKey = 987654
};
var actual = mapper.Map<MyEntitiesDbFirstModel>(source);
Assert.AreEqual(10, actual.Skip);//ok
Assert.AreEqual(50, actual.Take);//ok
Assert.AreEqual(123456, actual.Total);//ok
Assert.AreEqual(987654, actual.TrackingKey);//failed
}
[TestMethod]
public void TestWitTrackingKeyStaticAsSource_WORKING()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<TrackingKeyStatic<NotReleventClassForThisExample>, MyEntitiesDbFirstModel>()
.ForMember(d => d.TrackingKey, o => o.MapFrom(s => s.NewTrackingKey));
cfg.CreateMap<IBatchProcessing, MyEntitiesDbFirstModel>()
.ForMember(d => d.Skip, o => o.MapFrom(s => s.Skip))
.ForMember(d => d.Take, o => o.MapFrom(s => s.Take))
.ForMember(d => d.Total, o => o.MapFrom(s => s.Total));
});
var mapper = config.CreateMapper();
var source = new TrackingKeyStatic<NotReleventClassForThisExample>()
{
Skip = 10,
Take = 50,
Total = 123456,
NewTrackingKey = 987654
};
var actual = mapper.Map<MyEntitiesDbFirstModel>(source);
Assert.AreEqual(10, actual.Skip);//ok
Assert.AreEqual(50, actual.Take);//ok
Assert.AreEqual(123456, actual.Total);//ok
Assert.AreEqual(987654, actual.TrackingKey);//work fine
}
}
public interface ITrackingKey
{
int NewTrackingKey { get; set; }
List<object> Records { get; set; }
}
public interface IBatchProcessing
{
int Skip { get; set; }
int Take { get; set; }
int Total { get; set; }
}
public class TrackingKey<T> : ITrackingKey
{
private List<object> _records;
public int NewTrackingKey { get; set; }
public List<T> Records //not relevant for question, it just for implementing interface
{
get { return _records?.Cast<T>()?.ToList(); }
set { _records = value?.Cast<object>()?.ToList(); }
}
List<object> ITrackingKey.Records //not relevant for question, it just for implementing interface
{
get { return _records; }
set { _records = value; }
}
}
public class TrackingKeyStatic<T> : TrackingKey<T>, IBatchProcessing
{
public int Skip { get; set; }
public int Take { get; set; }
public int Total { get; set; }
}
public class MyEntitiesDbFirstModel
{
public int Skip { get; set; }
public int Take { get; set; }
public int Total { get; set; }
public int TrackingKey { get; set; }
}
public class NotReleventClassForThisExample { public int MyProperty { get; set; }}
}
I was able to get it working with a small "hacky" wrapper method:
public static MyEntitiesDbFirstModel MapToMyDbModel<T>(TrackingKeyStatic<T> trackingKey, IMapper mapper)
{
var interimTypeObject = new TrackingKey<T>()
{
NewTrackingKey = trackingKey.NewTrackingKey
};
var actual = mapper.Map<MyEntitiesDbFirstModel>(trackingKey);
mapper.Map<ITrackingKey, MyEntitiesDbFirstModel>(interimTypeObject, actual);
return actual;
}
Here's the fiddle for it - https://dotnetfiddle.net/XAjQB4
You may be able get rid of uglyness further - it seems that AutoMapper is not able to choose the correct map here when you use TrackingKeyStatic<T> but has no problems doing TrackingKey<T>.
Below is the mapper snippet with classes :
Mapper.Initialize(cfg =>
{
cfg.CreateMap<STest, ETest>().ForMember(d => d.customFields[0].stringValue, o => o.MapFrom(s => s.val));
}
);
var result = Mapper.Map<ETest>(JsonDeseriazedSource);
var serialized = JsonConvert.SerializeObject(result, Formatting.Indented);
And these are the source and destination classes to be mapped:
//source class
public class STest
{
public string Id { get; set; }
public string val { get; set; }
}
// destination class
public class ETest
{
public string Id { get; set; }
public Customfield[] customFields { get; set; }
}
public class Customfield
{
public string id { get; set; }
public string fieldName { get; set; }
public string stringValue { get; set; }
}
I need to map stringValue in Customfield object(destination) from val in Stest(source).
Thanks in advance!!
If I understand your question correctly, you can try this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<STest, Customfield>()
.ForMember(d => d.stringValue, o => o.MapFrom(s => s.val));
cfg.CreateMap<STest[], ETest>()
.ForMember(d => d.customFields, o => o.MapFrom(s => s.Select(Mapper.Map<Customfield>)));
});
var sTests= new[] { new STest { Id = "1", val = "val1" }, new STest { Id = "2", val = "val2" } };
var result = Mapper.Map<ETest>(sTests);
public class SourceExamModel
{
public int ExamId { get; set; }
public List<SectionModel> Sections { get; set; }
}
public class DesiationExamModel
{
public in ExamId {get;set;}
public System.Collections.Generic.IEnumerable<SectionModel> DestSections
{
get
{
}
set
{
}
}
What I tried:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<CrmMapper.SourceExamModel, CrmMapper.DestiationExamModel>()
.ForMember(v => v.Id, opts => opts.MapFrom(src => src.Id))
.ForMember(v => v.DestSections, opts => opts.MapFrom(src => src.SourceSections));
});
IMapper mapper = config.CreateMapper();
var source = new ExamModel();
var dest = mapper.Map<SourceExamModel, CrmMapper.DestiationExamModel>(source);
Can ayone help me how to map list of comple objects o los of complex objects
Assuming half of your example code is simple spelling mistakes, you pretty much have it working.
If the properties on the source and destination are named the same, you don't have to explicitly map them.
Your source in your example doesn't make sense, it needs the correct object and data.
Here's my attempt at a working example you can copy past into a console application.
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<SourceExamModel, DestinationExamModel>()
.ForMember(dest => dest.DestSections, c => c.MapFrom(src => src.Sections))
);
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var source = new SourceExamModel
{
ExamId = 1,
Sections = new List<SectionModel> { new SectionModel { SectionId = 1 }, new SectionModel { SectionId = 2 } }
};
var destination = mapper.Map<SourceExamModel, DestinationExamModel>(source);
}
}
public class SourceExamModel
{
public int ExamId { get; set; }
public List<SectionModel> Sections { get; set; }
}
public class DestinationExamModel
{
public int ExamId { get; set; }
public List<SectionModel> DestSections { get; set; }
}
public class SectionModel
{
public int SectionId { get; set; }
}