c# - net - ¿Cómo configurar Automapper para ignorar automáticamente las propiedades con el atributo ReadOnly?
imapper c# (3)
Contexto:
Digamos que tengo la siguiente clase de "destino":
public class Destination
{
public String WritableProperty { get; set; }
public String ReadOnlyProperty { get; set; }
}
y una clase "fuente" con el atributo ReadOnly
en una de sus propiedades:
public class Source
{
public String WritableProperty { get; set; }
[ReadOnly(true)]
public String ReadOnlyProperty { get; set; }
}
Es obvio, pero para ser claro: voy a asignar de la clase de Source
clase de Destination
de la siguiente manera:
Mapper.Map(source, destination);
Problema:
¿Cuáles son las formas de configurar Automapper para ignorar automáticamente la propiedad con el atributo ReadOnly(true)
?
Restricciones
Yo uso las clases de Profile
de Automapper para la configuración. No quiero ensuciar las clases con atributos específicos de Automapper. No quiero configurar Automapper para cada propiedad de solo lectura y causar mucha duplicación de esta manera.
Soluciones posibles (pero no adecuadas):
1) Agregue el atributo IgnoreMap
a la propiedad:
[ReadOnly(true)]
[IgnoreMap]
public String ReadOnlyProperty { get; set; }
No quiero ensuciar las clases con atributos específicos de automapper y hacer que dependa de ellas. Tampoco quiero agregar un atributo adicional junto con el atributo ReadOnly
.
2) Configure Automapper para ignorar la propiedad:
CreateMap<Source, Destination>()
.ForSourceMember(src => src.ReadOnlyProperty, opt => opt.Ignore())
No es una forma porque me obliga a hacer eso por cada propiedad en cualquier parte y también causa mucha duplicación.
Ahora también puede usar ForAllPropertyMaps
para deshabilitarlo globalmente:
configure.ForAllPropertyMaps(map =>
map.SourceMember.GetCustomAttributes().OfType<ReadOnlyAttribute>().Any(x => x.IsReadOnly),
(map, configuration) =>
{
configuration.Ignore();
});
Escriba el método de extensión como se muestra a continuación:
public static class IgnoreReadOnlyExtensions
{
public static IMappingExpression<TSource, TDestination> IgnoreReadOnly<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
foreach (var property in sourceType.GetProperties())
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(sourceType)[property.Name];
ReadOnlyAttribute attribute = (ReadOnlyAttribute) descriptor.Attributes[typeof(ReadOnlyAttribute)];
if(attribute.IsReadOnly == true)
expression.ForMember(property.Name, opt => opt.Ignore());
}
return expression;
}
}
Para llamar al método de extensión:
Mapper.CreateMap<ViewModel, DomainModel>().IgnoreReadOnly();
Si solo quería mapear propiedades que tienen un determinado atributo, en mi caso el atributo [DataMember] , escribí un método basado en la excelente respuesta anterior para manejar esto tanto en el origen como en el destino:
public static class ClaimMappingExtensions
{
public static IMappingExpression<TSource, TDestination> IgnoreAllButMembersWithDataMemberAttribute<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
foreach (var property in sourceType.GetProperties())
{
var descriptor = TypeDescriptor.GetProperties(sourceType)[property.Name];
var hasDataMemberAttribute = descriptor.Attributes.OfType<DataMemberAttribute>().Any();
if (!hasDataMemberAttribute)
expression.ForSourceMember(property.Name, opt => opt.Ignore());
}
foreach (var property in destinationType.GetProperties())
{
var descriptor = TypeDescriptor.GetProperties(destinationType)[property.Name];
var hasDataMemberAttribute = descriptor.Attributes.OfType<DataMemberAttribute>().Any();
if (!hasDataMemberAttribute)
expression.ForMember(property.Name, opt => opt.Ignore());
}
return expression;
}
}
Se llamará como lo hizo el otro método:
Mapper.CreateMap<ViewModel,DomainModel>().IgnoreAllButMembersWithDataMemberAttribute();