Adrian Hristov
Adrian Hristov

Reputation: 2037

How to ignore all properties that are marked as virtual

I am using virtual keyword for some of my properties for EF lazy loading. I have a case in which all properties in my models that are marked as virtual should be ignored from AutoMapper when mapping source to destination.

Is there an automatic way I can achieve this or should I ignore each member manually?

Upvotes: 14

Views: 9462

Answers (5)

Joseph
Joseph

Reputation: 1536

in case you wanna do it without needing to configure a second instance of automapper :

public static class ClearNavigationExtension
{
    public static void ClearNavigation(this object entity)
    {
        var type = entity.GetType();

        foreach (var property in type.GetProperties().Where(p => p.GetGetMethod().IsVirtual))
        {
            property.SetValue(entity, null);
        }
    }

    public static T ClearNavigation<T>(this T entity)
    {
        var type = entity.GetType();
        var entityCopy = Mapper.Map<T, T>(entity);

        foreach (var property in type.GetProperties().Where(p => p.GetGetMethod().IsVirtual))
        {
            property.SetValue(entityCopy, null);
        }

        return entityCopy;
    }

}

Upvotes: 0

Martin Zikmund
Martin Zikmund

Reputation: 39092

As we were using some virtual properties, I had to rewrite the extension as follows:

private static readonly Type CollectionBaseType = typeof(ICollection<>);

public static IMappingExpression<TSource, TDestination> IgnoreNavigationProperties<TSource, TDestination>(
    this IMappingExpression<TSource, TDestination> expression)
{
    var desType = typeof(TDestination);
    foreach (var property in desType.GetProperties()
                        .Where(p => IsCollectionProperty(p) || HasForeignKeyAttribute(p)))
    {
        expression.ForMember(property.Name, opt => opt.Ignore());
    }

    return expression;
}

private static bool IsCollectionProperty(PropertyInfo property)
{
    var propertyType = property.PropertyType;
    return propertyType.IsGenericType && 
           propertyType.GetGenericTypeDefinition() == CollectionBaseType;
}

private static bool HasForeignKeyAttribute(PropertyInfo property) =>
    property.GetCustomAttribute<ForeignKeyAttribute>() != null;

In essence I check if the property either is of type ICollection<> or if it has the [ForeignKey] attribute.

Upvotes: 2

RCP161
RCP161

Reputation: 239

To correct the answer of @Alexei, don't use the ForSourceMember method, like it's answered here in this issu on github. It's just for validation.

An other way is to use ForAllPropertyMaps like in this answer.

Upvotes: 0

Alexei - check Codidact
Alexei - check Codidact

Reputation: 23088

inquisitive's answer works fine, but it can be augmented for real life usage, when some mappings are performed from data models to service models and virtual members from source type should be ignored.

Also, if the type implements some interface, those properties will appear as virtual, so !IsFinal condition must be added to remove these false positive virtual properties.

public static class AutoMapperExtensions
{
    public static IMappingExpression<TSource, TDestination> IgnoreAllDestinationVirtual<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        var desType = typeof(TDestination);
        foreach (var property in desType.GetProperties().Where(p => p.GetGetMethod().IsVirtual && !p.GetGetMethod().IsFinal))
        {
            expression.ForMember(property.Name, opt => opt.Ignore());
        }

        return expression;
    }

    public static IMappingExpression<TSource, TDestination> IgnoreAllSourceVirtual<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        var srcType = typeof(TSource);
        foreach (var property in srcType.GetProperties().Where(p => p.GetGetMethod().IsVirtual && !p.GetGetMethod().IsFinal))
        {
            expression.ForSourceMember(property.Name, opt => opt.Ignore());
        }

        return expression;
    }
}

Upvotes: 7

inquisitive
inquisitive

Reputation: 549

You can create a mapping extension and use it:

namespace MywebProject.Extensions.Mapping
{
    public static class IgnoreVirtualExtensions
    {
        public static IMappingExpression<TSource, TDestination>
               IgnoreAllVirtual<TSource, TDestination>(
                   this IMappingExpression<TSource, TDestination> expression)
        {
            var desType = typeof(TDestination);
            foreach (var property in desType.GetProperties().Where(p =>   
                                     p.GetGetMethod().IsVirtual))
            {
                expression.ForMember(property.Name, opt => opt.Ignore());
            }

            return expression;
        }
    }
}

Usage :

Mapper.CreateMap<Source,Destination>().IgnoreAllVirtual();

Upvotes: 35

Related Questions