How to handle custom Properties in AutoMapper

46,617

Solution 1

On automapper where you create the Map you can specify additional processes for specific members of the destination type.

So where your default map would be

Mapper.Map<Domain.User, UsersDetailsViewModel>();

there is a fluent syntax to define the more complicated mappings:

Mapper.Map<Domain.User, UsersDetailsViewModel>()
      .ForMember(vm=>vm.UserName, m=>m.MapFrom(u=>(u.UserName != null) 
                                               ? u.UserName 
                                               : "User" + u.ID.ToString()));

Here the ForMember takes two Arguments the first defines the property that you are mapping to. The second provides a means of defining the mapping. For an example I have copped out and shown one of the easy mappings.

If you require a more difficult mapping, (such as your CurrentUser mapping) you can create a class that implements the IResolver interface, incorporate your mapping logic in that new clases and then add that into the mapping.

Mapper.Map<Domain.User, UsersDetailsViewModel>()
  .ForMember(vm=>vm.IsUserMatch, m=>m.ResolveUsing<MatchingUserResolver>()));

when Mapper comes to do the mapping it will invoke your custom resolver.

Once you discover the syntax of the .ForMember method everything else kind of slots into place.

Solution 2

Custom mapping can be defined in global.ascx (at startup) by following codes :

      AutoMapper.Mapper.CreateMap<Domain.User, UsersDetailsViewModel>()
          .ForMember(o => o.Email, b => b.MapFrom(z => z.Email))
          .ForMember(o => o.UserName , b => b.MapFrom(user => (user.UserName != null) ? user.UserName : "User" + user.ID.ToString));

you can do some initialization via BeforeMap () method. But you may need to do some changes in your viewmodel.

Solution 3

I think the syntax has slightly changed in 2019 (ASP.NET Core 2.2), this method is now handled with the MapperConfiguration and the static methods are no more available.

But I agree with @KJSR, this post is still really useful :-)

 private Mapper UserMapper= new Mapper(new MapperConfiguration(cfg => (cfg.CreateMap<Domain.User, UsersDetailsViewModel>())
            .ForMember(x=>x.Email, y=>y.MapFrom(z=>z.Email))
            .ForMember(x => x.UserName , y => y.MapFrom(user => (user.UserName != null) ? user.UserName : "User" + user.ID.ToString))));
Share:
46,617

Related videos on Youtube

Chase Florell
Author by

Chase Florell

I'm a developer in BC Canada and one of the owners of Flo Media Group Inc. I work primarily in C# .NET, Xamarin, HTML5 and Javascript, and I'm also very passionate about DevOps, and have been known to sling my fair share of PowerShell. When I'm not coding, I'm enjoying time with my wonderful wife and children, riding my motorcycle, camping in the summer months, snowboarding in the winter, or maybe just a round at the Golf Course. I Blog Here, and I'm also on Linkedin Contact Me

Updated on September 27, 2020

Comments

  • Chase Florell
    Chase Florell over 3 years

    I've got a ViewModel that takes some Model data and slightly alters it.

    The way I'm doing it "works" since I just pass the DomainModel to the constructor for the ViewModel, but since I'm using AutoMapper on some of my one-to-one ViewModels, I thought I'd try and learn how to do the mapping across all ViewModels.

    Here's an example of a ViewModel that does a little extra.

    public class UsersDetailsViewModel
    {
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Website { get; set; }
        public int ID { get; set; }
        public List<OpenID> OpenIds { get; set; }
        public string UserAge { get; set; }
        public string About { get; set; }
        public string Slug { get; set; }
        public System.DateTime LastSeen { get; set; }
        public string Region { get; set; }
        public string MemberSince { get; set; }
        public string Reputation { get; set; }
        public bool IsUserMatch { get; set; }
    
        private readonly MarkdownDeep.Markdown _markdown;
    
    
        public UsersDetailsViewModel(Domain.User user)
        {
            AuthUserData currentuser = AuthenticationHelper.RetrieveAuthUser;
            _markdown.NoFollowLinks = true;
            _markdown.SafeMode = true;
            _markdown.ExtraMode = false;
            _markdown.MarkdownInHtml = true;
    
            // We want to ensure that the user has a username, even if they
            // haven't set one yet. What this does is check to see if the
            // user.UserName field is blank, and if it is, it will set the
            // username to "UserNNNN" where NNNN is the user ID number.
            _UserName = (user.UserName != null) ? user.UserName : "User" + user.ID.ToString;
    
            // Nothing fancy going on here, we're just re-passing the object from
            // the database to the View. No data manipulation!
            _Email = user.Email;
            _Website = user.WebSite;
            _ID = user.ID;
    
            // Get's a list of all of the user's OpenID's
            _OpenIds = user.OpenIDs.ToList;
    
            // Converts the users birthdate to an age representation
            _UserAge = user.BirthDate.ToAge;
            //IE: 29
    
            // Because some people can be real ass holes and try to submit bad
            // data (scripts and shitè) we have to modify the "About" content in
            // order to sanitize it.  At the same time, we transform the Markdown
            // into valid HTML. The raw input is stored without sanitization in
            // the database.  this could mean Javascript injection, etc, so the
            // output ALWAYS needs to be sanitized.
    
            // This method below was used in conjunction with MarkDownSharp
            // _About = Trim(Utilities.HtmlSanitizer.Sanitize(Markdown.Transform(user.About)))
            _About = _markdown.Transform(user.About);
    
            // Removes spaces from Usernames in order to properly display the
            // username in the address bar
            _Slug = Strings.Replace(user.UserName, " ", "-");
    
            // Returns a boolean result if the current logged in user matches the
            // details view of tBhe user in question.  This is done so that we can
            // show the edit button to logged in users.
            _IsUserMatch = (currentuser.ID == user.ID);
    
    
            // Grabs the users registration data and formats it to a <time> tag
            // for use with the timeago jQuery plugin
            _MemberSince = user.MemberSince;
    
            // Grabs the users last activity and formats it to a <time> tag
            // for use with the timeago jQuery plugin
            _LastSeen = user.ActivityLogs.Reverse.FirstOrDefault.ActivityDate;
    
            // Formats the users reputation to a comma Deliminated number 
            //    IE: 19,000 or 123k
            _Reputation = user.Reputation.ToShortHandNumber;
    
    
            // Get the name of the users Default Region.
            _Region = user.Region.Name.FirstOrDefault;
        }
    
    }
    

    And here's how I currently utilize the above ViewModel

    public ActionResult Details(int id)
    {
        User user = _userService.GetUserByID(id);
    
        if (user != null) {
            Domain.ViewModels.UsersDetailsViewModel userviewmodel = new Domain.ViewModels.UsersDetailsViewModel(user);
            return View(userviewmodel);
        } else {
            // Because of RESTful URL's, some people will want to "hunt around"
            // for other users by entering numbers into the address.  We need to
            // gracefully redirect them to a not found page if the user doesn't
            // exist.
            throw new ResourceNotFoundException();
        }
    
    }
    

    How can I use (or should I use) AutoMapper to map my DomainModel to my ViewModel while doing the custom processing you see above?

  • Akmal Salikhov
    Akmal Salikhov over 5 years
    I wonder if it's best good practise to have such transformations on automapper side?
  • KJSR
    KJSR almost 5 years
    Still very useful in 2019 :) Helped me solve my problem