web-dev-qa-db-fra.com

Comment configurer Automapper pour ignorer automatiquement les propriétés avec l'attribut ReadOnly?

Le contexte:

Disons que j'ai la classe "destination" suivante:

public class Destination
{
    public String WritableProperty { get; set; }

    public String ReadOnlyProperty { get; set; }
}

et une classe "source" avec l'attribut ReadOnly sur l'une de ses propriétés:

public class Source
{
    public String WritableProperty { get; set; }

    [ReadOnly(true)]
    public String ReadOnlyProperty { get; set; }
}

C'est évident, mais pour être clair: je vais mapper de la classe Source à la classe Destination de la manière suivante:

Mapper.Map(source, destination);

Problème:

Quels sont les moyens de configurer Automapper pour ignorer automatiquement la propriété avec l'attribut ReadOnly(true)?

Contraintes:

J'utilise les classes Profile d'Automapper pour la configuration. Je ne veux pas salir les classes avec des attributs spécifiques à Automapper. Je ne veux pas configurer Automapper pour chaque propriété en lecture seule et provoquer ainsi beaucoup de duplication.

Solutions possibles (mais non adaptées):

1) Ajoutez l'attribut IgnoreMap à la propriété:

    [ReadOnly(true)]
    [IgnoreMap]
    public String ReadOnlyProperty { get; set; }

Je ne veux pas salir les classes avec des attributs spécifiques aux automappeurs et les rendre dépendants. De plus, je ne veux pas ajouter d'attribut supplémentaire avec l'attribut ReadOnly.

2) Configurez Automapper pour ignorer la propriété:

CreateMap<Source, Destination>()
.ForSourceMember(src => src.ReadOnlyProperty, opt => opt.Ignore())

Ce n'est pas un moyen, car cela m'oblige à le faire pour chaque propriété partout et provoque également beaucoup de dédoublements.

21
Deilan

Écrivez méthode d'extension comme indiqué ci-dessous:

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

Pour appeler la méthode d'extension:

Mapper.CreateMap<ViewModel, DomainModel>().IgnoreReadOnly();

24
Vinkal

Maintenant, vous pouvez également utiliser ForAllPropertyMaps pour le désactiver globalement:

configure.ForAllPropertyMaps(map =>
    map.SourceMember.GetCustomAttributes().OfType<ReadOnlyAttribute>().Any(x => x.IsReadOnly),
    (map, configuration) =>
    {
        configuration.Ignore();
    });
8
Dresel

Si vous vouliez mapper uniquement les propriétés qui ont un certain attribut, dans mon cas l'attribut [DataMember], j'ai écrit une méthode basée sur l'excellente réponse ci-dessus pour gérer cela à la fois pour la source et la destination:

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

Elle sera appelée comme l’autre méthode:

Mapper.CreateMap<ViewModel,DomainModel>().IgnoreAllButMembersWithDataMemberAttribute();
2
matsemann