2012-02-03 65 views
14

我有一个ViewModel需要一些模型数据并略微改变它。如何处理AutoMapper中的自定义属性

我这样做的方式“起作用”,因为我只是将DomainModel传递给ViewModel的构造函数,但由于我在一些一对一ViewModels上使用AutoMapper,我以为我会尝试并学习如何在所有ViewModel中进行映射。

下面是一个ViewModel的例子,它做了一些额外的工作。

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

} 

这里就是我目前使用上述视图模型

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

} 

如何使用(或我应该使用)AutoMapper我的DomainModel映射到我的视图模型,而这样做,你在上面看到的定制处理?

回答

38

在创建Map的automapper上,您可以为目标类型的特定成员指定其他进程。

那么,你的默认的地图将

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

有一个流畅的语法来定义更为复杂的映射:

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

这里ForMember需要两个参数,第定义属性,您正在映射到。第二个提供了一种定义映射的方法。举个例子,我已经删除并显示了一个简单的映射。

如果您需要更困难的映射(例如CurrentUser映射),您可以创建一个实现IResolver接口的类,将映射逻辑合并到新的类中,然后将其添加到映射中。

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

当Mapper来做映射时,它会调用你的自定义解析器。

一旦您发现.ForMember方法的所有其他类型的插槽到位的语法。

13

自定义映射可以在global.ascx(在启动时)由以下代码来定义:

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

可以经由BeforeMap做一些初始化()方法。但是您可能需要在视图模型中进行一些更改。