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>.
Related
I have a structure with a single top level node RootSource with child objects Source needing to be mapped to a list of child objects List<Destination>.
public class RootSource
{
public List<Source> Sources { get; set; }
}
public class Source
{
public string SourceData { get; set; }
}
public class Destination
{
public string DestinationData { get; set; }
}
This is what I would like to achive:
[Test]
public void MapperTest()
{
var mapper = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Destination>()
.ForMember(x => x.DestinationData, opt => opt.MapFrom(x => x.SourceData));
cfg.CreateMap<RootSource, List<Destination>>()
???;
})
.CreateMapper();
var rootSource = new RootSource
{
Sources = new List<Source> {
new Source { SourceData = "A" },
new Source { SourceData = "B" },
}
};
var destinations = mapper.Map<List<Destination>>(rootSource);
Assert.AreEqual(2, destinations.Count);
Assert.AreEqual(rootSource.Sources[0].SourceData, destinations[0].DestinationData);
Assert.AreEqual(rootSource.Sources[1].SourceData, destinations[1].DestinationData);
}
I have found ONE solution, but it feels less than ideal, relaying on use of converter. Is there some less complex way of resolving this with AutoMapper?
public class RootSourceToListDestinationConverter : ITypeConverter<RootSource, List<Destination>>
{
public List<Destination> Convert(RootSource rootSource, List<Destination> destination, ResolutionContext context)
{
var result = new List<Destination>();
if (rootSource != null)
{
foreach (var source in rootSource.Sources)
{
result.Add(context.Mapper.Map<Source, Destination>(source));
}
}
return result;
}
}
with the addition of this mapper entry:
cfg.CreateMap<RootSource, List<Destination>>().ConvertUsing<RootSourceToListDestinationConverter>();
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
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; }
}
Using Automapper, I want to map a property that is a List of type Employee using string.Join() to product a comma-delimited string of the names of an employee's rights. Here are the classes I'm using:
public class MappedEmployee
{
public string Name { get; set; }
public string RightNames { get; set; }
}
public class Employee
{
public string Name { get; set; }
public List<Right> Rights { get; set; }
}
public class Right
{
public string Name { get; set; }
}
And here is the code I have:
Mapper.CreateMap<Employee, MappedEmployee>()
.ForMember(d => d.RightNames, o => o.MapFrom(s => s.Rights.SelectMany(r => string.Join(", ", r.Name))));
var employee = new Employee
{
Name = "Joe Schmoe",
Rights = new List<Right>
{
new Right { Name = "Admin" },
new Right { Name = "User" },
}
};
var mappedEmployee = Mapper.Map<Employee, MappedEmployee>(employee);
However, this it's producing the folowing:
System.Linq.Enumerable+<SelectManyIterator>d__14`2[Employee.Right,System.Char]
What can I do do get a comma-delimited string of the Employee's rights?
Try using ResolveUsing instead and putting string.Join before the selection:
Mapper.CreateMap<Employee, MappedEmployee>()
.ForMember(d => d.RightNames, o => o.ResolveUsing(s => string.Join(", ",s.Rights.Select(r => r.Name))));