需要加速自动化...需要32秒才能完成113个对象

cho*_*bo2 26 c# nhibernate performance automapper asp.net-mvc-3

嗨我有一些自动映射器的主要问题,它很慢.我不知道如何加快速度.

我正在使用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>();
        }

    }
Run Code Online (Sandbox Code Playgroud)

所以你可以看到我得到了一些属性,在我的数据库中有一些其他类我在它们之间有引用.

然后我这样做

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);
            }
Run Code Online (Sandbox Code Playgroud)

//查看模型

    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.
            }
        }
Run Code Online (Sandbox Code Playgroud)

//映射

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

解析器

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;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

忽略奇怪的名字和任何错别字我的真实对象工作得很好并且有意义.

所以我用秒表做了这个

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.
Run Code Online (Sandbox Code Playgroud)

我需要非常好地优化它.

Dar*_*rov 28

代替:

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);
}
Run Code Online (Sandbox Code Playgroud)

尝试:

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

除了SetDateFormat您可以在映射定义中进行的调用之外,它等同于第一个.它也可能更快.

如果您在Test2 => MyViewModelAutoMapper 之间定义了映射,则自动提供一个映射,IEnumerable<Test2> => IEnumerable<MyViewModel>以便您不需要循环.

你也在你的问题中提到过NHibernate.确保源对象及其集合在将数据传递到映射层之前急切地从数据库加载,或者您不能责怪AutoMapper因为速度缓慢,因为当它尝试映射源对象的其中一个集合时它会访问数据库,因为NHibernate没有获取此集合.

  • 刚运行测试:使用问题中的方法映射350个具有35个属性的对象:00:01:52.426584` - 使用解决方案映射相同的对象时采用了00:00:00.479615 (16认同)
  • 这是使用ORM时的关键:**在将源对象及其集合传递到映射层之前,请确保它们已从数据库中急切地加载源对象及其集合** (2认同)

Aar*_*nLS 28

要寻找的另一件事是映射抛出异常的代码.AutoMapper将以静默方式捕获这些内容,但以这种方式捕获异常会影响性能.

因此,如果SomethingThatMightBeNull通常为null,则由于NullreferenceExceptions,此映射将执行得很差:

.ForMember(dest => dest.Blah, c.MapFrom(src=>src.SomethingThatMightBeNull.SomeProperty))
Run Code Online (Sandbox Code Playgroud)

我发现这样的更改将超过映射所需的一半时间:

.ForMember(dest => dest.Blah, c.MapFrom(src=> (src.SomethingThatMightBeNull == null
    ? null : src.SomethingThatMightBeNull.SomeProperty)))
Run Code Online (Sandbox Code Playgroud)

更新:C#6语法

.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))
Run Code Online (Sandbox Code Playgroud)


a.b*_*ema 12

添加此功能后,能够改善启动时间

 .ForAllMembers(options => options.Condition(prop => prop.SourceValue != null));
Run Code Online (Sandbox Code Playgroud)

在每个结尾

.CreateMap<..,..>()
Run Code Online (Sandbox Code Playgroud)