releases news custom convertusing c# performance nhibernate asp.net-mvc-3 automapper

c# - news - convertusing automapper



Necesidad de acelerar automapper... Se necesitan 32 segundos para hacer 113 objetos (7)

Hola, tengo algunos problemas importantes con el mapeador automático y es lento. No estoy seguro de cómo acelerarlo.

Estoy usando nhibernate, fluent nhibernate y asp.net mvc 3.0

[Serializable()] public class Test { public virtual int Id { get; private set; } public virtual string Name { get; set; } public virtual string Description { get; set; } public virtual DateTimeDate { get; set; } public virtual IList<Reminder> Reminders { get; set; } public virtual IList<Reminder2> Reminders2 { get; set; } public virtual Test2 Test2 { get; set; } public Test() { Reminders = new List<Reminders>(); Reminders2 = new List<Reminders2>(); } }

Entonces, como pueden ver, tengo algunas propiedades, algunas otras clases como en mi base de datos tengo referencias entre ellas.

Entonces hago esto

var a = // get all items (returns a collection of Test2) var 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); }

// ver modelo

public class MyViewModel { public int Id { get; private set; } public string Name { get; set; } public string Description { get; set; } public DateTime DateTimeDate { get; set; } public string FormatedDueDate { get; set; } public string Test2Prefix { get; set; } public string Test2BackgroundColor { get; set; } public string SelectedDateFilter { get; set; } public bool DescState { get; set; } public bool AlertState { get; set; } /// <summary> /// Constructor /// </summary> public MyViewModel() { // Default values SelectedDateFilter = "All"; DescState = false; AlertState = false; } /// <summary> /// Sets the date formatter string used /// </summary> /// <param name="dateFormat"></param> public void SetDateFormat(DateTime dueDate, string dateFilter) { // simple if statement to format date. } }

// mapeo

Mapper.CreateMap<Test2,MyViewModel>().ForMember(dest => dest.DescState, opt => opt.ResolveUsing<DescStateResolver>()) .ForMember(dest => dest.AlertState, opt => opt.ResolveUsing<AlertStateResolver>());

// resolutores

public class AlertStateResolver : ValueResolver<Task, bool> { protected override bool ResolveCore(Task source) { if (source.Reminders.Count > 0 || source.Reminders2.Count > 0) { return true; } else { return false; } } } public class DescStateResolver : ValueResolver<Task,bool> { protected override bool ResolveCore(Task source) { if (String.IsNullOrEmpty(source.Description)) { return false; } else { return true; } } }

Ignore los nombres raros y cualquier error tipográfico, mi objeto real funciona bien y tiene sentido.

Así que usé el cronómetro e hice esto

Stopwatch a = new Stopwatch() foreach (Test2 t in a) { a.Start() MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t); a.Stop() vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString()); collection.Add(vm); } var b = a.Elapsed; // comes back with 32 seconds.

Necesito optimizar esto muy mal.


En lugar de:

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

Tratar:

var a = // get all items (returns a collection of Test2) List<MyViewModel> collection = Mapper.Map<IEnumerable<Test2>, IEnumerable<MyViewModel>>(a);

que es equivalente a la primera, excepto la llamada a SetDateFormat que podría hacer en su definición de mapeo. También podría ser más rápido.

Si tiene una asignación definida entre Test2 => MyViewModel AutoMapper proporciona automáticamente uno para IEnumerable<Test2> => IEnumerable<MyViewModel> para que no tenga que hacer un bucle.

También has mencionado NHibernate en tu pregunta. Asegúrese de que su objeto de origen junto con sus colecciones se cargue con entusiasmo de la base de datos antes de pasarlo a la capa de mapeo o no puede culpar a AutoMapper por ser lento porque cuando intenta asignar una de las colecciones de su objeto de origen, llega a la base de datos porque NHibernate no trajo esta colección.


Fue capaz de mejorar el tiempo de lanzamiento cuando se agregó esto

.ForAllMembers(options => options.Condition(prop => prop.SourceValue != null));

al final de cada

.CreateMap<..,..>()


He arreglado el mismo problema que el tuyo. También me cuesta 32s para mapear un solo objeto. Por lo tanto, uso opts.Ignore () para tratar algunos objetos personalizados como se muestra a continuación:

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

Después de eso, solo cuesta unos pocos milisegundos.


No estoy seguro de si esto está causando problemas en su caso, pero tenga cuidado de serializar las propiedades implementadas automáticamente.

Cada vez que se compila su código, el compilador elige al azar el nombre de cada campo de respaldo (anónimo). Por lo tanto, puede ver algunas excepciones sorprendentes si serializa datos con un programa que se compila de una vez y lo deserializa con un programa diferente.


Otra cosa a buscar es el código de mapeo que arroja excepciones. AutoMapper los detectará de manera silenciosa, pero detectar excepciones de esta manera afectará el rendimiento.

Entonces, si SomethingThatMightBeNull es a menudo nulo, entonces este mapeo tendrá un bajo rendimiento debido a las NullreferenceExceptions:

.ForMember(dest => dest.Blah, c.MapFrom(src=>src.SomethingThatMightBeNull.SomeProperty))

Descubrí que hacer un cambio como este llevará más de la mitad del tiempo que toma el mapeo :

.ForMember(dest => dest.Blah, c.MapFrom(src=> (src.SomethingThatMightBeNull == null ? null : src.SomethingThatMightBeNull.SomeProperty)))

Actualización: sintaxis C # 6

.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))


Si sus subcolecciones son grandes, podría beneficiarse de usar "Any ()" en lugar del "Count> 1". La función Any solo tendrá que iterar una vez, mientras que Count puede necesitar iterar la colección entmes (dependiendo de la implementación).


Un buen consejo es optimizar la configuración de AutoMapper, usar Ignore para las propiedades de los ViewModels y hacer que el método llame para validar las asignaciones "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();