如何忽略所有标记为虚拟的属性

本文关键字:虚拟 属性 记为 何忽略 | 更新日期: 2023-09-27 18:11:34

我使用virtual关键字的一些属性EF延迟加载。我有一种情况,在我的模型中标记为virtual的所有属性在映射源到目标时都应该从AutoMapper忽略。

是否有一种自动的方法可以实现这一点,或者我应该手动忽略每个成员?

如何忽略所有标记为虚拟的属性

您可以创建一个映射扩展并使用它:

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

用法:

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

inquisitive的答案工作得很好,但是在实际使用中,当从数据模型到服务模型执行一些映射时,可以对它进行扩展,并且源类型的虚拟成员应该被忽略。

另外,如果类型实现了某些接口,则这些属性将显示为虚的,因此必须添加!IsFinal条件来消除这些假阳性的虚属性。

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

当我们使用一些虚拟属性时,我不得不重写扩展如下:

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;

本质上,我检查属性是否为ICollection<>类型或是否具有[ForeignKey]属性。

要纠正@Alexei的答案,不要使用ForSourceMember方法,就像在github上这个问题中回答的那样。这只是为了验证。

另一种方法是在这个答案中使用ForAllPropertyMaps

如果你想在不需要配置第二个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;
    }
}