¿Se puede "persuadir" a AutoMapper para que suspenda temporalmente asignaciones particulares?

¿Se puede "persuadir" a AutoMapper para que suspenda temporalmente asignaciones particulares?

Para ilustrar lo que estoy tratando de lograr, usaré una ilustración. Supongamos que tengo un repositorio, StudentRepository, que utiliza LINQ para interactuar con objetos de base de datos (tablas) como Estudiantes, Cursos, Actividades, Clubes, etc. En el lado de la aplicación, hay objetos de dominio coincidentes Estudiante, Curso, Actividad, Club. La clase Estudiante contiene miembros de matriz de tipo Curso, Actividad y Club como:

public class Student
{
    // ... more members
    public Course[] Courses { get; set; }
    public Activity[] Activities { get; set; }
    public Club[] Clubs { get; set; }
    // ... even more members
}

AutoMapper está configurado para asignar los objetos de la base de datos a los objetos del dominio donde las asignaciones se definen en un constructor estático de StudentRepository como:

public class StudentRepository : IStudentRepository
{
    static class StudentRepository
    {
        // ... other mappings

        Mapper.CreateMap<TableStudent, Student>()
            .ForMember(dest => dest.Courses, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Course>>(src.TableCourses)))
            .ForMember(dest => dest.Activities, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Activity>>(src.TableActivities)))
            .ForMember(dest => dest.Clubs, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Clubs>>(src.TableClubs)))
        // where TableStudents, TableCourses, TableActivities, TableClubs are database entities

        // ... yet more mappings

    }
}

¿Es posible "persuadir" a AutoMapper para que suspenda las asignaciones dentro de un bloque de funciones? Por ejemplo:

public Student[] GetStudents()
{
    DataContext dbContext = new StudentDBContext();

    var query = dbContext.Students;
    // => SUSPEND CONFIGURATION MAPPINGS for Subjects, Activities and Clubs WHILE STILL making use of others
    // => The idea here it to take personal charge of 'manually' setting the particular members (*for some specific reasons)

    var students = Mapper.Map<Student>(query); // => Still be able to use AutoMapper to map other members
}

public Student[] OtherStudentRepositoryMethods()
{
    // Other repository methods continue to make use of the mappings configured in the static constructor
}

NOTA "por algunas razones específicas": Una razón por la que uno puede querer quitarle el control a AutoMapper sería esta http://codebetter.com/davidhayden/2007/08/06/linq-to-sql-query-tuning-appears-to-break-down-in-more-advanced-scenarios/ sin que importe en el caso de asociaciones 1:n, LINQ to SQL solo admite unirse en una asociación 1:n por consulta. AutoMapper sería ineficiente aquí: hacer N llamadas para cargar Cursos para N estudiantes devueltos, N llamadas más para cargar Actividades para los mismos N estudiantes devueltos, y posiblemente N llamadas más para cargar Clubes para los mismos N estudiantes devueltos.

preguntado el 22 de mayo de 12 a las 15:05

No he probado esto, pero hay un BeforeMap Mapper.CreateMap<TableStudent, Student>().BeforeMap(x => { Mapper.CreateMap<TableStudent, Student>().ForMember... }) crear el mapa en el mapa anterior. Puede haber comprobaciones adicionales que podría hacer para no volver a crear el mapa varias veces. -

3 Respuestas

¿Es posible "persuadir" a AutoMapper para que suspenda las asignaciones dentro de un bloque de funciones?

Como sé, la mejor manera de hacerlo es usar Ignore() así

public class StudentRepository : IStudentRepository
{
    static class StudentRepository
    {
        // ... other mappings

        Mapper.CreateMap<TableStudent, Student>()
            .ForMember(dest => dest.Courses, opt => opt.Ignore())
            .ForMember(dest => dest.Activities, opt => opt.Ignore())
            .ForMember(dest => dest.Clubs, opt => opt.Ignore())
        // where TableStudents, TableCourses, TableActivities, TableClubs are database entities

        // ... yet more mappings

    }
}

Además, como se notó antes, te recomiendo que uses diferentes perfiles para cada objetivo que quieras lograr. Aquí hay un ejemplo.

public BaseService()
{
    AutoMapperRegistry.Configure();
}

public class AutoMapperRegistry
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<ServiceProfile1>();
            x.AddProfile<ServiceProfileReverseProfile1>();
        });
    }
}

public class ServiceProfile1 : Profile
{
    protected override string ProfileName
    {
        get
        {
            return "ServiceProfile1";
        }
    }

    protected override void Configure()
    {
        Mapper.CreateMap<DataContract_Sub, DTO_Sub>();
        Mapper.CreateMap<DataContract, DTO>()
            .ForMember(x => x.DataContract_Sub, opt => opt.MapFrom(y => y.DTO_Sub))
    .BeforeMap((s, d) => 
            {
                // your custom logic
            })
    .AfterMap((s, d) => 
            {
                // your custom logic
            });
    }
}

contestado el 28 de mayo de 12 a las 10:05

Una forma de lograr esto sería crear instancias de motor de mapeo separadas para cada escenario, de esa manera podría configurar diferentes mapas, como se sugiere en esta respuesta de Jimmy Bogard sobre querer mapear un solo tipo de diferentes maneras.

contestado el 23 de mayo de 17 a las 11:05

hmm... Gracias chicos por los comentarios. Me tomé el tiempo para considerar todas las respuestas y sugerencias. Ninguno en particular rinde exactamente bien, aunque dieron mucho que pensar. Pensé que debería inyectarme algo que probé. (Aviso legal: Mi opinión es que es un enfoque sucio, muchas cosas podrían salir mal, y las leyes de Murphy siguen siendo válidas). Puede aprovechar la funcionalidad Ignorar en la instancia particular para "suspender" la asignación. Por lo general, en un bloque de prueba y captura de la siguiente manera:

public Student[] GetStudents()
{
    try
    { // Suspend/Ignore the mappings
        // => SUSPEND CONFIGURATION MAPPINGS for Subjects, Activities and Clubs
        Mapper.CreateMap<TableStudent, Student>()
                    .ForMember(dest => dest.Courses, opt => opt.Ignore())
                    .ForMember(dest => dest.Activities, opt => opt.Ignore())
                    .ForMember(dest => dest.Clubs, opt => opt.Ignore())

        DataContext dbContext = new StudentDBContext();

        var query = dbContext.Students;

        // other logic ...

        var students = Mapper.Map<Student>(query); // => Still be able to use AutoMapper to map other members
        // set the properties you needed to do manually
    }
    finally // Restore back the mappings
    {
        Mapper.CreateMap<TableStudent, Student>()
                    .ForMember(dest => dest.Courses, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Course>>(src.TableCourses)))
                    .ForMember(dest => dest.Activities, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Activity>>(src.TableActivities)))
                    .ForMember(dest => dest.Clubs, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<Clubs>>(src.TableClubs)))
    }
}

Como mencioné, quizás esté sucio. No es el tipo de código que me gustaría escribir, especialmente porque no sé qué tipo de situaciones excepcionales pueden surgir si CreateMap() falla dentro del bloque final, pero en una aplicación heredada en la que no se puede revisar el enfoque: para posiblemente use diferentes perfiles como los sugeridos por @AndriyZakharko arriba, podría usarlo para recuperar el control temporalmente. Lo probé personalmente.

Respondido el 04 de junio de 12 a las 09:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.