Necesidad de acelerar automapper...It toma 32 segundos hacer 113 objetos


Hola Tengo algunos problemas importantes con auto mapper y es lento. No estoy seguro de cómo acelerarlo.

Estoy usando nhibernate, nhibernate fluido 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>();
        }

    }

Así que como puedes ver tengo algunas propiedades, Algunas otras clases ya que 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>());

/ / solucionadores

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

Ignorar los nombres extraños 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.

Author: chobo2, 2011-03-06

7 answers

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

Intenta:

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 SetDateFormat que podría hacer en su definición de asignación. También podría ser más rápido.

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

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

 25
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-27 07:34:52

Otra cosa a buscar es el código de asignación que arroja excepciones. AutoMapper capturará estos silenciosamente, pero la captura de excepciones de esta manera afecta el rendimiento.

Así que si SomethingThatMightBeNull es a menudo null, entonces esta asignación funcionará mal debido a las excepciones NullReferenceExceptions:

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

He encontrado que hacer un cambio como este será más de la mitad del tiempo que tarda la asignación :

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

Actualización: sintaxis de C# 6

.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))
 26
Author: AaronLS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 14:41:04

Fue capaz de mejorar el Tiempo de lanzamiento cuando se agregó este

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

Al final de cada

.CreateMap<..,..>()
 10
Author: a.boussema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-25 22:48:49

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

 3
Author: TheNameless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-07 07:13:37

No estoy seguro de si esto está causando algún problema en su caso, pero tenga cuidado de serializar las propiedades auto-implementadas.

Cada vez que se compila su código, el nombre de cada campo de respaldo (anónimo) es elegido al azar por el compilador. Así que puede ver algunas excepciones sorprendentes si serializa los datos con un progran que se compila a la vez y de-serializarlo con un programa diferente.

 1
Author: Chris Bednarski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-06 07:04:38

Solucioné el mismo problema que el tuyo. También me cuesta 32s para mapear solo un objeto. Por lo tanto, uso opts.Ignore () para tratar con algún objeto personalizado 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.

 0
Author: capcom923,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 03:35:26

Un buen consejo es optimizar la configuración de AutoMapper, usar Ignore para las propiedades de los ViewModels, y hacer la llamada al método 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();
 0
Author: Jean Gatto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-03 17:20:14