Нужно ускорить автомаппер ... Требуется 32 секунды, чтобы сделать 113 объектов - PullRequest
25 голосов
/ 06 марта 2011

Привет У меня есть некоторые серьезные проблемы с автоматическим картографированием, и он работает медленно.Я не уверен, как это ускорить.

Я использую nhibernate, беглый nhibernate и 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>();
        }

    }

Так что, как вы можете видеть, у меня есть некоторые свойства, Некоторые другиеклассы, как в моей базе данных, у меня есть ссылки между ними.

Затем я делаю это

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

// просмотр модели

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

// отображение

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

// resolvers

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

Игнорировать странные имена и любые опечатки, мой реальный объект работает просто отлично и имеет смысл.

Поэтому я использовал секундомер и сделал это

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.

Мне нужно очень сильно оптимизировать это.

Ответы [ 7 ]

28 голосов
/ 10 апреля 2012

Еще одна вещь, которую нужно искать, это отображение кода, который выдает исключения.AutoMapper будет перехватывать их тихо, но перехват исключений таким образом влияет на производительность.

Так что, если SomethingThatMightBeNull часто имеет значение null, это отображение будет работать плохо из-за исключений NullreferenceExceptions:

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

Iмы обнаружили, что внесение таких изменений будет более чем вдвое меньше, чем отображение занимает :

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

Обновление: синтаксис C # 6

.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))
26 голосов
/ 06 марта 2011

Вместо:

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

Попробуйте:

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

, что эквивалентно первому, за исключением вызова SetDateFormat, который вы могли бы сделать в своем определении отображения. Это также может быть быстрее.

Если у вас есть отображение, определенное между Test2 => MyViewModel AutoMapper автоматически предоставляет его для IEnumerable<Test2> => IEnumerable<MyViewModel>, чтобы вам не пришлось зацикливаться.

Также вы упомянули NHibernate в своем вопросе. Убедитесь, что ваш исходный объект вместе с его коллекциями загружен из базы данных до того, как передаст его на слой отображения, или вы не можете обвинить AutoMapper в том, что он медленный, потому что когда он пытается отобразить одну из коллекций вашего источника объект попадает в базу данных, потому что NHibernate не получил эту коллекцию.

12 голосов
/ 26 июня 2015

Был в состоянии улучшить время запуска при добавлении этого

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

в конце каждого

.CreateMap<..,..>()
3 голосов
/ 07 марта 2011

Если ваши подколлекции велики, вы можете воспользоваться «Any ()» вместо «Count> 1». Любая функция должна будет выполнить итерацию только один раз, в то время как счетчику может понадобиться выполнить итерацию коллекции entmes (в зависимости от реализации).

1 голос
/ 06 марта 2011

Не уверен, вызывает ли это какие-либо проблемы в вашем случае, но остерегайтесь сериализации автоматически реализуемых свойств.

Каждый раз, когда ваш код компилируется, имя каждого (анонимного) вспомогательного поля выбирается случайным образом компилятором. Так что вы можете увидеть некоторые неожиданные исключения, если сериализовать данные с помощью програна, скомпилированного за один раз, и десериализовать его с помощью другой программы.

0 голосов
/ 03 сентября 2018

Хорошим советом является оптимизация конфигурации AutoMapper, использование Ignore для свойств ViewModels и вызов метода для проверки соответствия "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 голосов
/ 02 ноября 2016

Я исправил ту же проблему, что и у вас. Это также стоит мне 32 секунды за отображение только одного объекта. Итак, я использую opts.Ignore () для работы с некоторыми настроенными объектами, как показано ниже:

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

После этого стоит всего несколько миллисекунд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...