AutoMapper - mapping integers to int array - c#

I'm trying to map integers from a database to an int array, but I'm getting the error: AutoMapper.AutoMapperConfigurationException: 'Custom configuration for members is only supported for top level individual members on a type.'
This is what I have:
Model:
public class Year
{
public int[] Months { get; set; } = new int[3];
}
Mapping:
CreateMap<DataRow, Year>()
.ForMember(dest => dest.Months[0], opt => opt.MapFrom(src => src["Jan"]))
.ForMember(dest => dest.Months[1], opt => opt.MapFrom(src => src["Feb"]))
.ForMember(dest => dest.Months[2], opt => opt.MapFrom(src => src["Mar"]))
Anyone know how to get this to work?

You will need to do something like this
.ForMember(dest => dest.Months, opt => opt.MapFrom(src => MapFromRow(src)))
where you have a method
int[] MapFromRow(DataRow src)
{
int months = new int[12];
months[0] = src["Jan"];
...
return months;
}
AutoMapper does not seem to support "dotting" into a property or indexing into an array.
If you want to go fully inline, you can do
.ForMember(dest => dest.Months, opt => opt.MapFrom(src => new int[]
{
(int)src["Jan"], (int)src["Feb"], ...
}))

Related

How to configure automapper so that it won't override the properties which are not defined in mapping

I am mapping one class property with two different other classes using auto mapper like below code:
var emp = map.Map<EmployeeAddressDto, Employee>(employeeAddressDto);
The above mapping gives me objects like the below:
Address1 = "some xyz address1"
Address2 = "some xyz address2"
Phone = "0000000000"
and then mapping the output object with EmployeeOtherAddressDto
map.Map<EmployeeOtherAddressDto, Employee>(emp);
The above mapping changes my out to this:
Address1 = "some other xyz address1"
Address2 = "some other xyz address2"
Phone = null
But I want it to be like the below:
Address1 = "some other xyz address1"
Address2 = "some other xyz address2"
Phone = "0000000000"
When the above mapping occurs it makes the phone no null but I want it to remain as it was defined on the previous mapping, although I want to override Address1 and Address2 fields.
Here is my mapping:
CreateMap<EmployeeAddressDto, Employee>()
.ForMember(dest => dest.Address1, options => options.MapFrom(src => src.Address1))
.ForMember(dest => dest.Address2, options => options.MapFrom(src => src.Address2))
.ForMember(dest => dest.Phone, options => options.MapFrom(src => src.Phone))
.ValidateMemberList(MemberList.None)
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
CreateMap<EmployeeOtherAddressDto, Employee>()
.ForMember(dest => dest.Address1, options => options.MapFrom(src => src.Address1))
.ForMember(dest => dest.Address2, options => options.MapFrom(src => src.Address2))
.ValidateMemberList(MemberList.None)
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
I want to do this in my mapping profile because I am using it in many places and do not want to update the phone no without using a mapper.
Please try this method for config your mapping.
AfterMap<>()
CreateMap<EmployeeOtherAddressDto, Employee>().AfterMap<EmployeeMappingAction>()
.ForMember(dest => dest.Address1, options => options.MapFrom(src => src.Address1))
.ForMember(dest => dest.Address2, options => options.MapFrom(src => src.Address2))
.ValidateMemberList(MemberList.None)
you can check your filed after mapping to check is it null or not.
public class EmployeeMappingAction: IMappingAction<EmployeeOtherAddressDto,Employee>
{
public void Process(EmployeeOtherAddressDto source, Employee destination, ResolutionContext context)
{
if (source.Phone == null)
{
destination.Phone = "";
}
}
}

Handle multiple mappings for same source and destination object

I have Source class and Destination class to map. Destination class is a generic class such as Person which will be used as a field in one of my class (MainClass) in various fields e.g Father,Mother,Brother etc. How can I map data coming from source to destination which is present for Father,Mother etc.
I can create
CreateMap<Source, MainClass>()
.ForMember(dest => dest.Mother, m => m.MapFrom(source => source))
.ForMember(dest => dest.Father, m => m.MapFrom(source => source))
.ForMember(dest => dest.Brother, m => m.MapFrom(source => source));
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, m => m.MapFrom(source => source.motherName))
.ForMember(dest => dest.ID, m => m.MapFrom(source => source.motherId))
.ForMember(dest => dest.Address, m => m.MapFrom(source => source.motherAddress));
but how can I handle mapping for father,brother etc. to achieve
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, m => m.MapFrom(source => source.FatherName))
.ForMember(dest => dest.ID, m => m.MapFrom(source => source.FatherId))
.ForMember(dest => dest.Address, m => m.MapFrom(source => source.FatherAddress));
Ok so this is an untested Code from an notepad editor :D
you can try this and change it in the way you need it. This will not work from the very beginning!
opt.MapFrom(source => SetName(source, "Mother")))
private object SetName(Person y, string personState)
{
Person person = new Person();
var properties = DictionaryFromType(y);
foreach(var property in properties)
{
if(property.Key.ToLower().Contains(personState.ToLower()))
{
// you should make the real mapping to id here. This is just example code on how it could work
PropertyInfo propertyInfo = person.GetType().GetProperty(property.Key);
propertyInfo.SetValue(person, Convert.ChangeType(property.Value, propertyInfo.PropertyType), null);
}
}
return person;
}
public static Dictionary<string, object> DictionaryFromType(object atype)
{
if (atype == null) return new Dictionary<string, object>();
Type t = atype.GetType();
PropertyInfo[] props = t.GetProperties();
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (PropertyInfo prp in props)
{
object value = prp.GetValue(atype, new object[] { });
dict.Add(prp.Name, value);
}
return dict;
}
may be you have to debug a little bit to make it work but somehow like this you can make it.
It is possible that there are better solutions with automapper, but i only had this in my mind for the moment.
Hope this can help you even if this is not a finished answer! (Sorry my time is low atm)

customize a field in automapper

public string AdjustTimezoneForDisplay(DateTime date)
{
//.......
}
automapper config
Mapper.Initialize(config =>
{
config.CreateMap<EquipmentOther, KMEquipmentOthers>()
.ForMember(x=>x.Status,opt=>opt.Ignore())
.ForMember(x => x.CreatedOn, opt => opt.MapFrom(src =>src.CreatedOn));
}
what am trying to achieve is to adjust time zone for CreatedOn field like the below code
ObjKMEquipmentOthers.CreatedOn = this._Utility.AdjustTimezoneForDisplay(EquipmentOtherDetails.CreatedOn);
would anybody please help me to achieve this
ps:AdjustTimezoneForDisplay is function to adjust time zone and return with local time
this worked for me ,hope this will help anybody in future
.ForMember(x => x.CreatedOn, opt => opt.MapFrom(x => AdjustTimezoneForDisplay(x.CreatedOn)));
and ps : AdjustTimezoneForDisplay must be static one .(initialy it was non static this is my problem ),again mapping initilizer also be static
public static string AdjustTimezoneForDisplay(DateTime date)
{
//.......
}
public static void RegisterMaps()
{
Mapper.Initialize(config =>
{
config.CreateMap<EquipmentOther, KMEquipmentOthers>()
.ForMember(x=>x.Status,opt=>opt.Ignore())
.ForMember(x => x.CreatedOn, opt => opt.MapFrom(x => AdjustTimezoneForDisplay(x.CreatedOn)));
}
}

Why this AutoMapper conversion fails?

In the following automapper example I've to deal with some different kind of problem:
property of the target class (P) which are themselves classes (i.e. PContact, Bank)
lists (new List<Bank>())
wrapper class (Wrapper wraps IDataRecord)
What's wrong?
I would like to convert from an instance of a IDataRecord to an instance of class P.
AutoMapper.Mapper.CreateMap<IDataRecord, P>()
.ConvertUsing(x => AutoMapper.Mapper.Map<Wrapper, P>(new Wrapper(x)));
AutoMapper.Mapper.CreateMap<Wrapper, P>()
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.GetString("Nome")))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.GetString("Cognome")))
.ForMember(dest => dest.Banks,
opt => opt.ResolveUsing(src => AutoMapper.Mapper.Map<Wrapper, List<Bank>>(src)));
AutoMapper.Mapper.CreateMap<Wrapper, PContact>()
.ForMember(dest => dest.StreetAddress, opt => opt.MapFrom(src => src.GetString("Indirizzo")))
.ForMember(dest => dest.Town, opt => opt.MapFrom(src => src.GetString("Città")))
.ForMember(dest => dest.Phone, opt => opt.MapFrom(src => src.GetString("Telefono")))
AutoMapper.Mapper.CreateMap<Wrapper, List<Bank>>()
.ConvertUsing(x => new List<Bank>() { AutoMapper.Mapper.Map<Wrapper, Bank>(x) });
AutoMapper.Mapper.CreateMap<Wrapper, Bank>()
.ForMember(dest => dest.AccountNumber, opt => opt.MapFrom(src => src.GetString("NumCC")))
.ForMember(dest => dest.IBAN, opt => opt.MapFrom(src => src.GetString("IBAN")));
The problem was related to the fact that AutoMapper has its own Mapper for the IDataReader so it's needed to do the following
public class AutomapperTypeAdapterFactory
:ITypeAdapterFactory
{
public AutomapperTypeAdapterFactory()
{
//scan all assemblies finding Automapper Profile
var profiles = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.BaseType == typeof(Profile));
Mapper.Initialize(cfg =>
{
foreach (var item in profiles)
{
if (item.FullName != "AutoMapper.SelfProfiler`2")
cfg.AddProfile(Activator.CreateInstance(item) as Profile);
}
});
//===>>
MapperRegistry.Mappers.Remove(MapperRegistry.Mappers.FirstOrDefault(x => x is DataReaderMapper));
}
}

Using Automapper to make full copy of an object with IList properties

So I need to make copies of objects. I have a model here "place" that has a IList HasAndBelongsToMany Property that is being a pain. I need to take the field property and copy it too but it copies the reference only. Here is what i have
public class place : ActiveRecordBase<place>
{
public place() { }
private int place_id;
[PrimaryKey("place_id")]
virtual public int id
{
get { return place_id; }
set { place_id = value; }
}
private IList<fields> Fields;
[HasAndBelongsToMany(typeof(fields), Lazy = true, Table = "place_to_fields", ColumnKey = "place_id", ColumnRef = "field_id", NotFoundBehaviour = NotFoundBehaviour.Ignore, Cascade = ManyRelationCascadeEnum.AllDeleteOrphan)]
virtual public IList<fields> field
{
get { return Fields; }
set { Fields = value; }
}
}
And use automapper like this
place org = ActiveRecordBase<place>.Find(id);
Mapper.Reset();
Mapper.CreateMap<place, place>().ForMember(dest => dest.id, o => o.Ignore())
.ForMember(dest => dest.field, o => o.Ignore())
;
place copy = new place();
Mapper.Map(org, copy);
copy.SaveAndFlush();
Which works because i'm skiping the field. What i was hoping for was something more like:
Mapper.CreateMap<place, place>().ForMember(dest => dest.id, o => o.Ignore())
.ForMember(dest => dest.field.id, o => o.Ignore())
;
See the first line with .ForMember(dest => dest.id, o => o.Ignore()) is so that I don't copy the reference id for the place object. I need to do the same for the place property field. I need to ignore the id's and make new entries with the same values on the rest of its properties
You need to create mapping for field type and add "Ignore" option for field's id like you've alredy done for place type.
Mapper.CreateMap<fields, fields>().ForMember(dest => dest.id, o => o.Ignore());
Mapper.CreateMap<place, place>().ForMember(dest => dest.id, o => o.Ignore());

Categories

Resources