My object mapping requires me to pass the source object and an additional object to be able to map the destination object appropriately. However, I am unable to determine a way to be able to get that done.
public class SourceDto
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string SourceValue3 { get; set; }
public string SourceValue4 { get; set; }
}
public class AnotherSourceDto
{
public string AnotherSourceValue1 { get; set; }
public string AnotherSourceValue2 { get; set; }
public string AnotherSourceValue3 { get; set; }
public string AnotherSourceValue4 { get; set; }
}
public class Destination
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string DestValue3 { get; set; }
}
public string ConvertToDestValue3(string sourceValue3, string anotherSourceValue2)
{
// Some logic goes here
return sourceValue3 + " " + anotherSourceValue2;
}
void Main()
{
var config = new MapperConfiguration(
cfg =>cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3,
opt => opt.MapFrom(
src => ConvertToDestValue3(src.SourceValue3, "" //Here I need to pass AnotherSourceDto.AnotherSourceValue2 ))));
}
I'm afraid the answer is to map that property outside AutoMapper.
Here's an example where you could do this using an extension method, so it still has the look and feel of being done with AutoMapper.
// put inside a static class
public static Destination CustomMap(
this IMapper mapper,
SourceDto source,
AnotherSourceDto anotherSource)
{
var destination = mapper.Map<Destination>(source);
destination.DestValue3 =
source.SourceValue3 + " " + anotherSource.AnotherSourceValue2;
return destination;
}
void Main()
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3, opt => opt.Ignore()));
// usage
var mapper = config.CreateMapper();
var source = new SourceDto { /* add properties */ };
var anotherSource = new AnotherSourceDto { /* add properties */ };
var destination = mapper.CustomMap(source, anotherSource);
}
Related
Automapper with complex nested mapping. I am trying to map mydestinationArrayField and
dest1Array, here source objectlist to be copied to dest1array.
here are my classes for source and destination.
namespace AutomapperDemo
{
class Program
{
static void Main(string[] args)
{
try
{
SourceObject request = new SourceObject()
{
sourceTypeField = "1",
SourceObj1Field = new SourceObj1
{
SourceObj1Id = "1",
SourceObjListss = new List<SourceInnerObjList>
{
new SourceInnerObjList
{
SourceObjListItem1Id = 1
},
new SourceInnerObjList
{
SourceObjListItem1Id = 2
}
}
}
};
var mapper = CreateMapper();
DestinationObject destination = new DestinationObject();
destination = mapper.Map<DestinationObject>(request);
}
catch (Exception ex)
{
throw ex;
}
}
public static IMapper CreateMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<SourceObject, DestinationObject>()
.ForMember(dest => dest.destinationTypeField, o => o.MapFrom(src => src.sourceTypeField))
.ForMember(dest => dest.destinationObjectArrayField, o => o.MapFrom(src => new destinationObjectArray()
{
mydestinationArrayField = src.SourceObj1Field.SourceObjListss.Select(x => x.SourceObjListItem1Id).FirstOrDefault().ToString(), //this gives error
//dest1Array = src.SourceObj1Field.SourceObjListss // here source objectlist to be copied to dest1array
}));
});
return config.CreateMapper();
}
}
}
namespace Automapper
{
public class SourceObject
{
public string sourceTypeField;
public SourceObj1 SourceObj1Field { get; set; }
}
public class SourceObj1
{
public string SourceObj1Id { get; set; }
public ICollection<SourceInnerObjList> SourceObjListss { get; set; }
}
public class SourceInnerObjList
{
public int SourceObjListItem1Id { get; set; }
public int SourceObjListItem2d { get; set; }
}
public class SourceInnerObj2List
{
public int? mycount { get; set; }
public int? yourcount { get; set; }
}
}
namespace Automapper
{
public class DestinationObject
{
public string destinationTypeField;
public destinationObjectArray[] destinationObjectArrayField;
}
public class destinationObjectArray
{
public string mydestinationArrayField;
public string myField1;
public destinationInnerObject1Array[] dest1Array;
public destinationInnerObject2Array[] dest2Array;
}
public class destinationInnerObject1Array
{
public string destinationInnerObjectItem11;
public string destinationInnerObjectItem21;
}
public class destinationInnerObject2Array
{
public string categoryTypeField;
public string valueField;
public string NumberField;
}
}
While executing the mapping i am getting "Missing type map configuration or unsupported mapping."
No matter how I configure ignores or custom mappings it seems to not like this nesting. Any Automapper experts out there who could tell me how a mapping with a complex object like this could be done.
It seems like your second ForMember doesn't work:
.ForMember(dest => dest.destinationObjectArrayField, o => o.MapFrom(src => new destinationObjectArray() //...
Because in you're defining your map to return destinationObjectArray()
and not destinationObjectArray[]!
So there is a map like:
destinationObjectArrayField -> destinationObjectArray()
and there is no map like:
destinationObjectArrayField -> destinationObjectArray[]
And Automapper is telling you that.
You should do something like this:
cfg.CreateMap<SourceObject, DestinationObject>()
.ForMember(dest => dest.destinationTypeField, o => o.MapFrom(src => src.sourceTypeField))
.ForMember(dest => dest.destinationObjectArrayField,
o => o.MapFrom(
src => src.SourceObj1Field
.SourceObjListss
.Select(x => new destinationObjectArray
{
myField1 = $"first_id: {x.SourceObjListItem2d} second_id: {x.SourceObjListItem1Id}"
})
.ToArray()
));
});
Also cleaning and formatting code is highly suggested, it's seems like you've simply got lost in it! IDE's can help with that.
Hey I'm trying to map my generic class to concrete class but using it's interface.
My service returns me data which type is
IPaggedResults<Customer>
and I want to be able to map this to
IPaggedResults<CustomerDto>
It works if I invoke mapping with:
_mapper.Map<PaggedResults<CustomerDto>>
but I want use following syntax:
_mapper.Map<IPaggedResults<CustomerDto>>
public class PaggedResults<T> : IPaggedResults<T>
{
public IEnumerable<T> Results { get; protected set; }
public int TotalResults { get; protected set; }
public int TotalPages { get; protected set; }
public int ResultsPerPage { get; protected set; }
public PaggedResults(IEnumerable<T> results, int totalResults, int resultsPerPage)
{
Results = results;
TotalResults = totalResults;
TotalPages = totalResults / resultsPerPage;
ResultsPerPage = resultsPerPage;
}
}
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
public string NIP { get; set; }
}
My mapper configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap<CustomerCompany, CustomerDto>();
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.CreateMap(typeof(IPaggedResults<>), typeof(IPaggedResults<>)).As(typeof(PaggedResults<>));
}).CreateMapper();
Im'using Automapper by Jimmy Bogard.
I could achieve it through the following code:
Create an extension for IMapperConfigurationExpression
public static class IMapperConfigurationExpressionExtensions
{
public static void MapPaggedResults<TSource, TDestination>(this IMapperConfigurationExpression exp){
exp.CreateMap(typeof(PaggedResults<TSource>), typeof(IPaggedResults<TDestination>))
.ConstructUsing((source, ctx) => { return ctx.Mapper.Map<PaggedResults<TDestination>>(source) as IPaggedResults<TDestination>; });
}
}
Then use this configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.MapPaggedResults<CustomerCompany, CustomerDto>();
}).CreateMapper();
Then both results can be obtained:
var _mapper = Initialize();
IPaggedResults<CustomerCompany> source = new PaggedResults<CustomerCompany>(
new List<CustomerCompany>() { new CustomerCompany() {Id =42, Name = "SomeName", NIP = "someNIP" } }, 1, 1);
var resut = _mapper.Map<PaggedResults<CustomerDto>>(source);
var resut2 = _mapper.Map<IPaggedResults<CustomerDto>>(source);
I have list of my class type which I need to copy to another class type. In following code I am trying to copy GenderEntity to Gender but getting no results
private void MapGender()
{
List<GenderEntity> _GenderEntity = _GenderServiceObject.GenderEntity();
List<Gender> _Gender = new List<Gender>();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<List<GenderEntity>, List<Gender>>();
});
IMapper mapper = config.CreateMapper();
_Gender = mapper.Map<List<GenderEntity>, List<Gender>>(_GenderEntity);
var x = "d";
}
GenderEntity - Source
public class GenderEntity
{
public GenderEntity() { }
public string Code { get; set; }
public string Description { get; set; }
}
Gender - Destination
[DataContract]
public class Gender
{
[DataMember]
public string Code { get; set; }
[DataMember]
public string Description { get; set; }
}
Gender entity is used for WCF SOAP services as data contract
Return List Class
public class GenderService
{
#region EBS Service return 'Gender'
public Gender GenderEBSEntity()
{
return GenderEBS.GetGender_EBS();
}
#endregion
#region Mapped Gender Entity
public List<GenderEntity> GenderEntity()
{
return GenderEBS.GetGenderEntity();
}
#endregion
}
You don't need to map Lists with AutoMapper, just create a map like:
cfg.CreateMap<GenderEntity, Gender>();
and then map it like this:
_Gender = mapper.Map<Gender[]>(_GenderEntity).ToList();
my answer...
private void MapGender()
{
List<Gender> MappedGender = new List<Contracts.Data.Gender>();
IList<GenderEntity> genderEntity = _GenderServiceObject.GenderEntity();
if(genderEntity!=null)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<GenderEntity, Gender>();
});
IMapper mapper = config.CreateMapper();
gender = mapper.Map<IList<GenderEntity>, IList<Gender>>(genderEntity).ToList();
}
}
I am having some difficulty projecting my POCO object to a DTO object
My POCO object
public class LogEntry
{
public LogEntry()
{
this.Username = Environment.UserName;
this.LogNote = String.Empty;
var now = DateTime.Now;
this.LoginTime = now;
this.LastSeenInput = now;
this.ServerName = Environment.MachineName;
}
public int LogEntryId { get; set; }
[Required(AllowEmptyStrings = false)]
[Column(TypeName = "nvarchar")]
[MaxLength(64)]
public string Username { get; set; }
[Required(AllowEmptyStrings = true)]
[Column(TypeName = "varchar")]
[MaxLength(20)]
public string ServerName { get; set; }
public DateTime LoginTime { get; set; }
public TimeSpan ConnectedFor { get; set; }
public TimeSpan IdleFor { get; set; }
public DateTime? LastSeenInput { get; set; }
[Required(AllowEmptyStrings = true)]
public string LogNote { get; set; }
}
My DTO object
public sealed class ClientEventDTO
{
public int LogEntryId { get; set; }
public string Username { get; set; }
public string ServerName { get; set; }
public DateTime LoginTime { get; set; }
public TimeSpan ConnectedFor { get; set; }
public string LogNote { get; set; }
public TimeSpan IdleForWithLast { get; set; }
}
What I am trying to do is I want IdleForWithLast to represent
public TimeSpan IdleForWithLast
{
get
{
var tmp = IdleFor;
if (LastSeenInput.HasValue)
tmp = tmp.Add(DateTime.Now - LastSeenInput.Value);
return tmp;
}
}
However when I do this
static void Main(string[] args)
{
Mapper.CreateMap<LogEntry, ClientEventDTO>()
.ForMember(dto => dto.IdleFor,
a => a.MapFrom(ent => ent.LastSeenInput == null ?
ent.IdleFor :
ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value)
)
);
Mapper.AssertConfigurationIsValid();
using (var ctx = new LoginHistoryContext())
{
var username = "Scott";
var query = ctx.LogEntries.Where(entry => entry.Username == username).Project().To<ClientEventDTO>();
var result = query.ToList();
Debugger.Break();
}
}
I get the following exception
System.ArgumentException was unhandled
HResult=-2147024809
Message=DbArithmeticExpression arguments must have a numeric common type.
Source=System.Data.Entity
Changing ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value) to ent.IdleFor.Add(DateTime.Now - ent.LastSeenInput.Value) just gives me a different exception
System.NotSupportedException was unhandled
HResult=-2146233067
Message=LINQ to Entities does not recognize the method 'System.TimeSpan Add(System.TimeSpan)' method, and this method cannot be translated into a store expression.
Source=System.Data.Entity
What is the correct way to do this kind of operation using while maintaining compatibility QueryableExtensions?
If you don't care about compatibility it can be easily done this way
static void Main(string[] args)
{
Mapper.CreateMap<LogEntry, ClientEventDTO>()
.ForMember(dto => dto.IdleFor, opt=>opt.ResolveUsing<CustomResolver>());
Mapper.AssertConfigurationIsValid();
using (var ctx = new LoginHistoryContext())
{
var username = "Scott";
var query = ctx.LogEntries.Where(entry => entry.Username == username);
var result = Mapper.Map<List<ClientEventDTO>>(query.ToList());
Debugger.Break();
}
}
public class CustomResolver : ValueResolver<LogEntry, TimeSpan>
{
protected override TimeSpan ResolveCore(LogEntry source)
{
var tmp = source.IdleFor;
if (source.LastSeenInput.HasValue)
tmp = tmp.Add(DateTime.Now - source.LastSeenInput.Value);
return tmp;
}
}
and that is the work around I will likely use for this situation, but I would like to know how to handle this in the future in case I am in a situation where I must use QueryableExtensions and I need to do something similar.
You can ignore this member, and map it manually after mapping:
Mapper.CreateMap<LogEntry, ClientEventDTO>()
.ForMember(dto => dto.IdleForWithLast, m => m.Ignore())
.AfterMap((ent, dto) =>
dto.IdleForWithLast = ent.LastSeenInput.HasValue ?
ent.IdleFor + (DateTime.Now - ent.LastSeenInput.Value) : ent.IdleFor);
Or change default mapping in after map:
Mapper.CreateMap<LogEntry, ClientEventDTO>()
.ForMember(dto => dto.IdleForWithLast, m => m.MapFrom(ent => ent.IdleFor))
.AfterMap((ent, dto) => {
if (ent.LastSeenInput.HasValue)
dto.IdleForWithLast += DateTime.Now - ent.LastSeenInput.Value;
});
Is it possible to configure AutoMapper to set all properties to default value in case if the source object is null for specified classes? I know that I should use Mapper.AllowNullDestinationValues = false; to do what I want for all classes in application.
Here the sampled code that I use for tests, but it doesn't work
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
//Mapper.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}
public class B
{
public string Name { get; set; }
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
}
Using of this code:
var b = new B() {Foo = 1, Foo1 = 3.3, Foo2 = true, Name = "123"};
var a = new A {Name = "aName"};
a.Init(b); // All ok: Name=aName, Foo=1, Foo1=3,3, Foo2=True
a.Init(null); // Should be Name=aName, Foo=null, Foo1=null, Foo2=False,
// but a has the same values as on a previous line
It must be related to "a" being already mapped.
var a = new A {Name = "aName"};
a.Init(b);
a.Init(null);
All mappings are cached, so if you'll attempt to re-map same instance the automapper would just keep the original result.
In order to test it, try:
var c = new A {Name = "x"};
c.Init(null);
Here is a link to similar question.
Looks like it will replace with null if you set Mapper.Configuration.AllowNullDestinationValues = false:
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
Mapper.Configuration.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}