Подтвердить что ты не робот

Необходимо ускорить автоперенос... Требуется 32 секунды, чтобы сделать 113 объектов

Привет, у меня есть некоторые серьезные проблемы с auto mapper, и он медленный. Я не уверен, как ускорить его.

Я использую 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.

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

4b9b3361

Ответ 1

Вместо:

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

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

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

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

Ответ 2

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

Итак, если SomethingThatMightBeNull часто имеет значение null, то это отображение будет плохо выполняться из-за NullreferenceExceptions:

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

Я нашел, что внесение такого изменения будет больше, чем в два раза меньше, чем требуется для отображения.:

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

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

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

Ответ 3

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

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

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

.CreateMap<..,..>()

Ответ 4

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

Ответ 5

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

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

Ответ 6

Я исправил ту же проблему, что и ваша. Это также стоит мне 32s для отображения только одного объекта. Таким образом, я использую 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());

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