web-dev-qa-db-fra.com

Besoin d'accélérer l'application automatique ... Il faut 32 secondes pour faire 113 objets

Bonjour, j'ai quelques problèmes majeurs avec Auto Mapper et c'est lent. Je ne sais pas comment accélérer les choses.

J'utilise nhibernate, nhibernate et asp.net mvc 3.0 couramment

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

    }

Donc, comme vous pouvez le voir, j'ai quelques propriétés, d'autres classes comme dans ma base de données, j'ai des références entre elles.

Je fais alors ça

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

// voir le modèle

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

// mappage

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

// résolveurs

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

Ignorer les noms étranges et les fautes de frappe, mon objet réel fonctionne parfaitement et a un sens.

J'ai donc utilisé le chronomètre et l'ai fait

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.

J'ai besoin d'optimiser cela très mal.

24
chobo2

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

Essayer:

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

ce qui équivaut au premier, à l'exception de l'appel SetDateFormat, que vous pouvez utiliser dans votre définition de mappage. Cela pourrait aussi être plus rapide.

Si vous avez un mappage défini entre Test2 => MyViewModel, AutoMapper en fournit automatiquement un pour IEnumerable<Test2> => IEnumerable<MyViewModel> afin que vous n'ayez pas besoin de boucler.

Vous avez également mentionné NHibernate dans votre question. Assurez-vous que votre objet source et ses collections sont chargés avec impatience à partir de la base de données avant en le transmettant à la couche de mappage, sinon vous ne pourrez pas reprocher à AutoMapper d'être lent, car il tente de mapper l'une des collections de votre objet source. frappe la base de données parce que NHibernate n'a pas récupéré cette collection.

26
Darin Dimitrov

Une autre chose à rechercher est le code de mappage qui lève des exceptions. AutoMapper les détectera en silence, mais intercepter les exceptions de cette manière aura un impact sur les performances.

Donc, si SomethingThatMightBeNull est souvent null, alors ce mappage fonctionnera mal en raison des NullreferenceExceptions:

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

J'ai trouvé que faire un changement comme celui-ci prend plus de la moitié du temps que prend la cartographie :

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

Mise à jour: syntaxe C # 6

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

Était capable d'améliorer le temps de lancement quand ajouté ceci

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

à la fin de chaque

.CreateMap<..,..>()
11
a.boussema

Si vos sous-collections sont volumineuses, vous pouvez utiliser "Any ()" au lieu de "Count> 1". La fonction N'importe laquelle n'aura à itérer qu'une seule fois, alors que le Compte pourrait devoir itérer la collection entmes (en fonction de l'implémentation).

3
TheNameless

Vous ne savez pas si cela pose problème dans votre cas, mais méfiez-vous de la sérialisation des propriétés implémentées automatiquement.

Chaque fois que votre code est compilé, le nom de chaque champ de support (anonyme) est choisi au hasard par le compilateur. Vous pouvez donc rencontrer des exceptions surprenantes si vous sérialisez des données avec un programme compilé à la fois et que vous le désérialisez avec un programme différent.

1
Chris Bednarski

J'ai résolu le même problème que le vôtre. Cela me coûte également 32 s pour mapper un seul objet. Donc, j'utilise opts.Ignore () pour traiter un objet personnalisé comme ci-dessous:

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

Après cela, il ne vous a fallu que quelques millisecondes.

0
capcom923

Un bon conseil est d’optimiser la configuration d’AutoMapper, d’utiliser Ignore pour les propriétés de ViewModels et d’appeler la méthode pour valider les mappages "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
Jean Gatto