web-dev-qa-db-fra.com

Comment gérer les propriétés personnalisées dans AutoMapper

J'ai un ViewModel qui prend des données de modèle et les modifie légèrement.

La façon dont je le fais "fonctionne" puisque je passe simplement le DomainModel au constructeur pour le ViewModel, mais puisque j'utilise AutoMapper sur certains de mes ViewModels one-to-one , J'ai pensé essayer d'apprendre à faire le mappage sur tous les ViewModels.

Voici un exemple de ViewModel qui fait un petit plus.

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

}

Et voici comment j'utilise actuellement le ViewModel ci-dessus

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

}

Comment puis-je utiliser (ou devrais-je utiliser) AutoMapper pour mapper mon DomainModel à mon ViewModel tout en effectuant le traitement personnalisé que vous voyez ci-dessus?

21
Chase Florell

Sur l'automapper où vous créez la carte, vous pouvez spécifier des processus supplémentaires pour des membres spécifiques du type de destination.

Alors, où serait votre carte par défaut

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

il existe une syntaxe fluide pour définir les mappages les plus compliqués:

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

Ici, le ForMember prend deux arguments, le premier définit la propriété à laquelle vous mappez. La seconde permet de définir la cartographie. Pour un exemple, j'ai coupé et montré l'un des mappages faciles.

Si vous avez besoin d'un mappage plus difficile (tel que votre mappage CurrentUser), vous pouvez créer une classe qui implémente l'interface IResolver, incorporer votre logique de mappage dans ces nouvelles classes, puis l'ajouter dans le mappage.

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

lorsque Mapper viendra faire le mappage, il invoquera votre résolveur personnalisé.

Une fois que vous avez découvert la syntaxe de la méthode .ForMember, tous les autres types d'emplacements se mettent en place.

50
Bobbles

Le mappage personnalisé peut être défini dans global.ascx (au démarrage) à l'aide des codes suivants:

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

vous pouvez effectuer une initialisation via la méthode BeforeMap (). Mais vous devrez peut-être apporter des modifications à votre modèle de vue.

19
Mahmoud Moravej

Je pense que la syntaxe a légèrement changé en 2019 (ASP.NET Core 2.2), cette méthode est maintenant gérée avec MapperConfiguration et les méthodes statiques ne sont plus disponibles.

Mais je suis d'accord avec @KJSR, ce post est toujours très utile :-)

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