2011-03-06 7 views
22

Hola, tengo algunos problemas importantes con el mapeador automático y es lento. No estoy seguro de cómo acelerarlo.Necesito acelerar el automapper ... Tarda 32 segundos en hacer 113 objetos

estoy usando NHibernate, nhibernate fluidez y 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>(); 
     } 

    } 

Así como se puede ver Tengo algunas propiedades, algunas otras clases como en mi base de datos que tienen referencias entre ellos.

entonces hacer esto

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

// vista de modelo

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

// mapeo

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

// resolutores

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

Ignora los nombres extraños y cualquier error tipográfico. Mi objeto real funciona bien y tiene sentido.

por lo que utiliza el cronómetro y lo hizo

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. 

necesito optimizado esta muy mal.

+0

No sé mucho sobre automapper, pero ¿hay alguna forma de configurarlo para que genere clases de mapeo preconfiguradas y cableadas? Esas clases casi con seguridad serían órdenes de magnitud más rápidas. –

+1

Intentaré 2 cosas aquí: Ejecute esto con NHProf, y ejecútelo con dotTrace. Una vez que tenga algunos datos, eso podría apuntarme en la dirección correcta. –

Respuesta

25

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

Probar:

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

que es equivalente a la primera excepción de la llamada SetDateFormat que se podía hacer en su definición de asignación. También podría ser más rápido.

Si tiene una asignación definida entre Test2 => MyViewModel, AutoMapper proporciona automáticamente una para IEnumerable<Test2> => IEnumerable<MyViewModel>, por lo que no necesita realizar un ciclo.

También ha mencionado NHibernate en su pregunta. Asegúrese de que su objeto fuente junto con sus colecciones se carguen ansiosamente desde la base de datos antes de pasándolo a la capa de mapeo o no puede culpar a AutoMapper por ser lento porque cuando intenta mapear una de las colecciones de su objeto fuente golpea el base de datos porque NHibernate no obtuvo esta colección.

+1

Sería interesante saber qué tipo de aumento de velocidad obtuviste al usar este método ... – Tr1stan

+13

Solo corrí una prueba: mapear 350 objetos con 35 propiedades usando el método en la pregunta tomó '00: 01: 52.426584' - Mapeando el mismo los objetos que usan la solución tomaron '00: 00: 00.479615' – Tr1stan

+0

Tengo una colección con 6893 objetos con 23 propiedades (entorno de prueba, la producción debería tener mucho más). Con el bucle tomó '00: 02: 32.8118534' y con su solución tomó' 00: 02: 25.4527961'. No ayudó mucho. ¿Alguna sugerencia? – lpfx

1

No estoy seguro de si esto está causando problemas en su caso, pero tenga cuidado de serializar las propiedades implementadas automáticamente.

Cada vez que se compila su código, el compilador selecciona al azar el nombre de cada campo de respaldo (anónimo). Por lo tanto, es posible que vea algunas excepciones sorprendentes si serializa los datos con un programa que se compila al mismo tiempo y lo deserializa con un programa diferente.

3

Si sus subcolecciones son grandes, puede beneficiarse del uso de "Cualquiera()" en lugar de "Cuenta> 1". La función Any solo tendrá que iterar una vez, mientras que Count podría necesitar iterar la colección entmes (dependiendo de la implementación).

23

Otra cosa a tener en cuenta es el código de asignación que arroja excepciones. AutoMapper capturará estos silenciosamente, pero atrapar excepciones de esta manera afecta el rendimiento.

Así que si SomethingThatMightBeNull a menudo es nulo, esta asignación se realice bien debido a las NullreferenceExceptions:

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

que he encontrado haciendo un cambio como éste hará más de la mitad del tiempo el mapeo toma:

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

actualización: C# 6 sintaxis

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

fue capaz de mejorar el tiempo de inicio cuando se añade este

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

al final de cada

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

Esto casi HALVÓ el tiempo de mapeo en un objeto que mapeo GRACIAS. Esta es una solución más rápida para la respuesta de @ AaronS. –

0

He arreglado el mismo problema que los suyos. También me cuesta 32 para mapear solo un objeto. Así, utilizo opts.Ignore() para hacer frente a un objeto personalizado de la siguiente manera:

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

Después de eso, sólo cuestan unos pocos milisegundos.

Cuestiones relacionadas