13

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?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Adrian Hristov
  • 1,957
  • 2
  • 16
  • 22
  • You cannot do this in automapper out-of-the-box, but may be you can create maps in code using this rule. – Kirill Bestemyanov Jul 06 '14 at 15:01
  • Curious, why do you want to ignore these? Why would they be on your destination type if you want to ignore them? – Jimmy Bogard Jul 07 '14 at 17:00
  • These are navigation properties to other entities in EF Code First Models. When creating new entries I need to map my view model with data from a form to properties in the domain model (Ef Code First model). Typically not mapping the properties would not result in any errors or exception but I am using AutoMapper's `Mapper.AssertConfigurationIsValid()` in a unit test and if I have not mapped all properties of the destination model the assertion throws an exception – Adrian Hristov Jul 07 '14 at 17:33

5 Answers5

35

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();
jgillich
  • 71,459
  • 6
  • 57
  • 85
inquisitive
  • 549
  • 4
  • 7
  • Thanks Scott Chamberlain for adding the missing brackets – inquisitive Jul 06 '14 at 16:27
  • It was a good solution and that looked like the only error in the code, so I fixed it. – Scott Chamberlain Jul 06 '14 at 21:26
  • @ScottChamberlain Huh, the only error you say? The method name that you added brackets to wasn't quite right either. ;) – jgillich Mar 18 '15 at 16:12
  • 7
    Note that properties defined in an interface are always virtual. One solution could be to add `!p.GetGetMethod().IsFinal` to the `Where` predicate. http://stackoverflow.com/a/4793253/941764 – jgillich Mar 24 '15 at 14:00
  • Thanks! If you dont mind, would you edit your answer with the suggestion @jgillich made? – nilsK May 15 '18 at 10:44
6

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;
    }
}
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • 1
    Thank you. I was finding various properties that were being ignored due to these false positives. This should be the correct, accepted answers. – crichavin Oct 30 '18 at 22:53
2

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.

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91
0

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.

RCP161
  • 199
  • 1
  • 13
0

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

}
Joseph
  • 1,458
  • 15
  • 20