Hi I have some major problems with auto mapper and it being slow. I am not sure how to speed it up.
I am using nhibernate,fluent nhibernate and asp.net mvc 3.0
If your subcollections are large you might benefit from using "Any()" instead of the "Count > 1". The Any function will only have to iterate once while the Count might need to iterate the entmes collection (depending on the implementation).
I fixed the same issue as yours. It also costs me 32s for mapping only one object. So, I use opts.Ignore() to deal with some customized object as below:
CreateMap<SiteConfiguration, Site>()
.ForMember(x => x.SubSystems, opts => opts.Ignore())
.ForMember(x => x.PointInformations, opts => opts.Ignore())
.ForMember(x => x.Schedules, opts => opts.Ignore())
.ForMember(x => x.EquipmentDefinitions, opts => opts.Ignore());
After that, it just cost a few milliseconds.
Instead of:
var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = new List<MyViewModel>();
foreach (Test2 t in a)
{
MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());
collection.Add(vm);
}
Try:
var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = Mapper.Map<IEnumerable<Test2>, IEnumerable<MyViewModel>>(a);
which is equivalent to the first except the SetDateFormat
call which you could do at your mapping definition. It might also be faster.
If you have a mapping defined between Test2 => MyViewModel
AutoMapper automatically provides one for IEnumerable<Test2> => IEnumerable<MyViewModel>
so that you don't need to loop.
Also you have mentioned NHibernate in your question. Make sure that your source object along with its collections is eagerly loaded from the database before passing it to the mapping layer or you cannot blame AutoMapper for being slow because when it tries to map one of the collections of your source object it hits the database because NHibernate didn't fetch this collection.
A good tip is to optimize the configuration of AutoMapper, use Ignore for the properties of the ViewModels, and make the method call to validate the mappings "Mapper.AssertConfigurationIsValid()".
Mapper.Initialize(cfg =>
{
cfg.ValidateInlineMaps = true;
cfg.AllowNullCollections = false;
cfg.AllowNullDestinationValues = true;
cfg.DisableConstructorMapping(); // <= In the case of my project, I do not use builders, I had a performance gain.
cfg.AddProfile<DomainToViewModelMappingProfile>();
cfg.AddProfile<ViewModelToDomainMappingProfile>();
});
Mapper.AssertConfigurationIsValid();
Was Able to improve Launch Time when added this
.ForAllMembers(options => options.Condition(prop => prop.SourceValue != null));
at end of Each
.CreateMap<..,..>()
Another thing to look for is mapping code that throws exceptions. AutoMapper will catch these silently, but catching exceptions in this way impacts performance.
So if SomethingThatMightBeNull is often null, then this mapping will perform poorly due to the NullreferenceExceptions :
.ForMember(dest => dest.Blah, c.MapFrom(src=>src.SomethingThatMightBeNull.SomeProperty))
I've found making a change like this will more than half the time the mapping takes:
.ForMember(dest => dest.Blah, c.MapFrom(src=> (src.SomethingThatMightBeNull == null
? null : src.SomethingThatMightBeNull.SomeProperty)))
Update: C# 6 syntax
.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))