3

Need automapper to map a domain type's properties back to an existing entity from the context (basically just updating the fields that have changed). I need it to ignore Navigation properties and only map the scalar properties.

I can get it to work if I say ForMember(o => o.MyNavProperty, opt => opt.Ignore) but I'd rather have a generic method for all of my mappings to tell it to only map scalar and not nav properties.

Trying to follow Mauricio's solution:

ASP.net MVC - Should I use AutoMapper from ViewModel to Entity Framework entities?

but I can't get it to successfully ignore my navigation properties.

Here's my updated version:

      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 Answers 2

5

I was working on it recently.
You can use the following solution :

Define an Attribute for navigation properties :

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

Mark all navigation properties in View-Models with above 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; }
}

Writing an extension method for AutoMapper to ignore all properties with NavigationProperty attribute.

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

At last you can use it as follows :

Mapper.CreateMap<TagModel, Tag>()
        .ForMember(m => m.Id, opt => opt.Condition(m => m.Id > 0))
        .IgnoreNavigationProperties();
1

I use an explicit approach by adding an interface to the entity and mapping to/from the interface. So rather than exclude I'm being explicit on what to include. The interface is added by declaring a partial class.

The interface is free for me as I use the interface for decoupling, test stubs, mocking etc.

Perhaps just me, but I do not like to see any ignores in an AutoMapper configuration. Can't justify that but it just feels wrong to me.

5
  • 1
    No ignores? then how do you deal with columns the user isn't allowed to update?
    – Betty
    Commented Sep 30, 2012 at 6:52
  • +1 I agree. This kind of automated mapping can bite you one day and it is hard to read from the code what happens. Mapping from interfaces to entity classes is the best way to stay in control. If the user should not update columns don't put them in the interface, but it's also something for a view model/validation to deal with. Commented Sep 30, 2012 at 7:21
  • This is more of a curiosity thing to see if it's possible to do this, some have posted solutions like the one I posted above but I couldn't get them to work. I know I can explicitly set the mappings, will do that until I can get this to work.
    – Mark W
    Commented Sep 30, 2012 at 16:17
  • Hi Mark W ... FYI ... it works as I have done it. Let AutoMapper do what it's name implies. Define the contracts in the code on the objects your mapping. My opinion is that the AutoMapper file should be just map this to this. Otherwise u end up with partially mapped objects.
    – Rob Smyth
    Commented Oct 1, 2012 at 9:17
  • Betty - u don't have to. That is the nice thing, you map by code contract. If it is not on the interface then it is not part of the service. Use the interface type, not the concrete type, to define what the object "is".
    – Rob Smyth
    Commented Oct 1, 2012 at 9:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.