2011-03-06 54 views
22

嗨我有一些自动映射器的主要问题,它很慢。我不知道如何加快速度。需要加速自动映射器...它需要32秒做113个对象

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

//解析器

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. 

我需要非常优化不好这一点。

+0

我对automapper了解不多,但有没有办法对它进行配置,以便输出预配置的硬连线映射类?这样的类别几乎肯定会快几个数量级。 – 2011-03-06 06:36:08

+1

我会在这里尝试2件事:用NHProf执行此操作,并使用dotTrace执行此操作。一旦你有了一些数据,这将能够指向我的正确方向。 – 2011-03-06 22:24:38

回答

25

相反的:

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没有获取这个集合。

+1

使用这种方法知道提高了什么样的速度会很有意思... – Tr1stan 2011-11-17 12:57:18

+13

刚刚运行一个测试:使用问题中的方法映射350个具有35个属性的对象使用了'00:01:52.426584' - 映射相同使用该解决方案的对象使用了'00:00:00.479615' – Tr1stan 2011-11-17 13:47:46

+0

我有一个具有23个属性(测试环境,生产应该有更多)的6893个对象的集合。循环使用了'00:02:32.8118534',而您的解决方案则使用了'00:02:25.4527961'。它没有那么多帮助。有什么建议么? – lpfx 2016-07-26 14:46:51

1

不确定这是否导致您的案件中的任何问题,但要小心序列化自动实施的属性。

每次编译代码时,编译器会随机挑选每个(匿名)后台字段的名称。因此,如果您使用一次编译的程序序列化数据并使用其他程序对其进行反序列化,您可能会看到一些令人惊讶的异常。

3

如果您的子集合较大,则可以使用“Any()”而不是“Count> 1”来获益。 Any函数只需迭代一次,而Count可能需要迭代entmes集合(取决于实现)。

23

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

所以,如果SomethingThatMightBeNull往往是null,则此映射将很差由于NullReferenceException异常进行:

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

我发现做这样的变化将超过一半的映射发生时间:

.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))) 
9

在每个

.CreateMap<..,..>() 
+0

这几乎使对象上的映射时间变长'重新映射。谢谢。这是@AaronLS答案的更快解决方案。 – 2015-09-23 10:08:55

0

末加入这个

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

当我固定的相同的问题你能提高启动时间。它也花费我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()); 

之后,它只是成本几毫秒。