让Automapper忽略EF中的非标量和导航属性

3
需要使用自动映射器将一个域类型的属性映射回上下文中的现有实体(基本上只更新已更改的字段)。我需要它忽略导航属性,只映射标量属性。
如果我说 ForMember(o => o.MyNavProperty, opt => opt.Ignore),我可以让它工作,但我宁愿为所有映射使用通用方法,告诉它只映射标量而不是 nav 属性。
试图遵循 Mauricio 的解决方案: ASP.net MVC - Should I use AutoMapper from ViewModel to Entity Framework entities? 但我无法成功地忽略我的导航属性。
这是我的更新版本:
      private static void CreateMapForEF<TDto, TEntity>()
      {
         Mapper.CreateMap<TDto, TEntity>()
    .ForAllMembers(o => o.Condition(ctx =>
                                       {

                                          var members = ctx.Parent.SourceType.GetMember(ctx.MemberName); // get the MemberInfo that we are mapping

                                          if (!members.Any())
                                             return false;

                                          if (members.First().GetCustomAttributes(
                                                typeof (EdmRelationshipNavigationPropertyAttribute), false).Any())
                                             return false;

                                          return members.First().GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Any(); // determine if the Member has the EdmScalar attribute set

                                       }));
      }
2个回答

5

我最近在处理相关内容。

你可以使用以下解决方案:

为导航属性定义一个属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NavigationPropertyAttribute : Attribute
{
} 

在视图模型中,使用上述属性标记所有导航属性。

public class TagModel
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public string Description { get; set; }

    [Display(Name = "Image")]
    public string ImagePath { get; set; }

    [NavigationProperty]
    public List<ContentModel> Contents { get; set; }
}

编写一个 AutoMapper 的扩展方法,让它忽略所有带有 NavigationProperty 属性的属性。
public static IMappingExpression<TSource, TDestination> IgnoreNavigationProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var sourceType = typeof(TSource);

    foreach (PropertyInfo property in sourceType.GetProperties())
    {
        var isNavProp = property.GetCustomAttributes(typeof(NavigationPropertyAttribute), false).Count() == 1;
        if (isNavProp)
                expression.ForMember(property.Name, opt => opt.Ignore());
    }
    return expression;
}

最后,您可以按照以下步骤使用它:
Mapper.CreateMap<TagModel, Tag>()
        .ForMember(m => m.Id, opt => opt.Condition(m => m.Id > 0))
        .IgnoreNavigationProperties();

1

我使用显式方法,通过为实体添加接口并进行接口的映射来实现。因此,我不是排除某些内容,而是明确指定要包含哪些内容。通过声明部分类来添加接口。

对我来说,这个接口是免费的,因为我可以将其用于解耦、测试存根、模拟等方面。

也许只有我这样想,但我不喜欢在AutoMapper配置中看到任何忽略项。无法证明,但这种做法对我来说感觉不对。


1
没有忽略吗?那么如何处理用户无权更新的列呢? - Betty
+1 我同意。这种自动映射可能会在某一天给你带来麻烦,而且很难从代码中读出发生了什么。从接口到实体类的映射是保持控制的最佳方式。如果用户不应该更新列,请不要将它们放入接口中,但这也是视图模型/验证要处理的内容。 - Gert Arnold
这更像是一种好奇心,想看看是否有可能做到这一点,有些人发布了像我上面发布的解决方案,但我无法使它们工作。我知道我可以明确设置映射,直到我能够让它起作用。 - Mark W
嗨,马克W...FYI...它可以正常工作,因为我已经这样做了。让AutoMapper做它的名字所暗示的事情。在映射的对象上在代码中定义合同。我的意见是AutoMapper文件应该只是将这个映射到这个。否则,您最终会得到部分映射的对象。 - Rob Smyth
贝蒂 - 你不必这样做。这是一个好的事情,你可以通过代码契约进行映射。如果它不在接口上,那么它就不是服务的一部分。使用接口类型而不是具体类型来定义对象的“是什么”。 - Rob Smyth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接