customize a field in automapper - c#

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)));
}
}

Related

AutoMapper - mapping integers to int array

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"], ...
}))

AutoMapper 4.2 not ignoring properties in profile

In my Web API controller method, before I map the UpdatePlaceDTO to PlaceMaster, I make a database call to populate the properties that are not covered by the Map but for some reason AutoMapper makes those properties null.
var mappedPlaceMaster = _mapper.Map<PlaceMaster>(placeMasterDTO);
// mappedPlaceMaster.EntityId is null
I've tried many of the solutions to IgnoreExistingMembers but none of them work.
This is what I have
public class PlaceMapperProfile : Profile
{
protected override void Configure()
{
// TO DO: This Mapping doesnt work. Need to ignore other properties
//CreateMap<UpdatePlaceDto, PlaceMaster>()
// .ForMember(d => d.Name, o => o.MapFrom(s => s.Name))
// .ForMember(d => d.Description, o => o.MapFrom(s => s.Description))
// .ForMember(d => d.ParentPlaceId, o => o.MapFrom(s => s.ParentPlaceId))
// .ForMember(d => d.LeftBower, o => o.MapFrom(s => s.LeftBower))
// .ForMember(d => d.RightBower, o => o.MapFrom(s => s.RightBower)).IgnoreAllNonExisting();
}
}
This is the extension
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
I've used Modules to inject the mapper to my depedencies
protected override void Load(ContainerBuilder builder)
{
//register all profile classes in the calling assembly
var profiles =
from t in typeof(Navigator.ItemManagement.Data.MappingProfiles.PlaceMapperProfile).Assembly.GetTypes()
where typeof(Profile).IsAssignableFrom(t)
select (Profile)Activator.CreateInstance(t);
builder.Register(context => new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve))
.As<IMapper>()
.SingleInstance();
}
I've seen in some thread that _mapper.Map actually creates a new object so how we make it to sort of "add-on" to the existing property values?
Ok I found the solution. It was right in front of me but I didn't see it!
I just had to use an overload of the Map function which doesn't create a new instance of PlaceMaster rather it assigns the properties which are available in the map.
mappedPlaceMaster = _mapper.Map(placeMasterDTO, placeMasterFromDatabase);

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));
}
}

AutoMapper Map If Not Null, Otherwise Custom Convert

Here's my code:
Mapper.CreateMap<Foo, Foo2>()
.ForMember(dest => dest.Bar, opt => opt.MapFrom(src => src.Bar == null ? new BarViewModel() : src.Bar))
Basically, "BarViewModel" has a parameterless ctor which sets up properties in the class.
So i'm trying to say to AutoMapper:
If the value is null, then use the ctor for the class. otherwise use the mapping you have in place
The above is giving me a C# compiler error. And i'm guessing a cast wouldn't work either.
So is there a AutoMapper trick to do this?
Worst case i could remove that mapping for that property, and just do:
var mapped = Mapper.Map<Foo,Foo2>(src);
if (mapped.Bar == null) mapped.Bar = new BarViewModel();
But that's a tad ugly.
Ideas?
You can use custom value resolver. The following should work:
Mapper.CreateMap<Foo, Foo2>()
.ForMember(dest => dest.Bar, opt => opt.ResolveUsing(src => src.Bar == null ? new Bar() : Mapper.Map<Bar,Bar2>(src.Bar)))
Now you can use .NullSubstitute() to replace NULL value to some custom value in Automapper, e.g.:
CreateMap<SMModel, VM_SMModel>()
.ForMember(d => d.myDate, o => o.NullSubstitute(new DateTime(2017,12,12)));
As of Automapper 8, ResolveUsing is no longer an option but inline Func's, IValueResolver and IMemberValueResolver are 😊.
Inline Func Example
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Foo, FooViewModel>()
.ForMember(dest => dest.BarViewModel,
opt => opt.MapFrom((src, dest) =>
{
if (src.Bar == null)
return new BarViewModel ();
return Mapper.Map<Bar, BarViewModel>(src.Bar);
}));
cfg.CreateMap<Bar, BarViewModel>();
});
IMemberValueResolver Example
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Foo, FooViewModel>()
.ForMember(dest => dest.BarViewModel,
opt => opt.MapFrom<NullBarResolver, Bar>(src => src.Bar));
cfg.CreateMap<Bar, BarViewModel>();
});
public class NullBarResolver : IMemberValueResolver<object, object, Bar, BarViewModel>
{
public BarViewModel Resolve(object source, object destination, Bar sourceMember,
BarViewModel destMember, ResolutionContext context)
{
if (sourceMember == null)
return new BarViewModel();
return Mapper.Map(sourceMember, destMember);
}
}
There's some good documentation on Custom Value Resolvers here.
Working demo
I don't get a compiler error for the following:
public class Foo
{
public Bar Bar { get; set; }
}
public class Foo2
{
public Bar Bar { get; set; }
}
public class Bar
{
public int Id { get; set; }
public Bar()
{
Id = 3;
}
}
CreateMap<Foo, Foo2>()
.ForMember(
dest => dest.Bar,
opt => opt.MapFrom(src => src.Bar == null ? new Bar() : src.Bar));
...so I'm wondering if the problem is not actually with your mapping?
This can be done with the PreCondition() method. Here's an extension method I wrote to do this more easily:
public static IMappingExpression<TSource, TDestination> MapIf<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> map, Expression<Func<TDestination, object>> selector,
Func<TSource, bool> mapIfCondition, Expression<Func<TSource, object>> mapping)
{
map.ForMember(selector, c =>
{
c.MapFrom(mapping);
c.PreCondition(mapIfCondition);
});
return map;
}
Usage Example:
//if settings are null coming from the sender, then ignore them and keep the current settings
CreateMap<PresentationView, Presentation>()
.MapIf(x => x.Settings, x => x.Settings is not null, v => v.Settings!)

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