web-dev-qa-db-ja.com

AutoMapperでカスタムプロパティを処理する方法

一部のモデルデータを受け取り、少し変更するViewModelがあります。

DomainModelViewModelのコンストラクターに渡すだけなので、「機能」しますが、1対1のViewModelの一部で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;
    }

}

そして、これは私が現在上記の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();
    }

}

AutoMapperを使用して(または使用する必要があります)、上記のカスタム処理を実行しながらDomainModelをViewModelにマップするにはどうすればよいですか?

21
Chase Florell

マップを作成するオートマッパーで、宛先タイプの特定のメンバーに追加のプロセスを指定できます。

したがって、デフォルトのマップは

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は2つの引数を取ります。最初の引数は、マッピングするプロパティを定義します。 2番目は、マッピングを定義する手段を提供します。例として、簡単なマッピングの1つを抜粋して示しました。

CurrentUserマッピングなど、より難しいマッピングが必要な場合は、IResolverインターフェイスを実装するクラスを作成し、マッピングロジックをその新しいクラスに組み込んでから、マッピングに追加できます。

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

mapperがマッピングを行うと、カスタムリゾルバーが呼び出されます。

.ForMemberメソッドの構文を見つけたら、他のすべての種類のスロットを配置します。

50
Bobbles

カスタムマッピングは、以下のコードで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()メソッドを介して初期化を行うことができます。ただし、ビューモデルでいくつかの変更を行う必要がある場合があります。

19
Mahmoud Moravej

2019(ASP.NET Core 2.2)では構文がわずかに変更されたと思います。このメソッドはMapperConfigurationで処理されるようになり、静的メソッドは使用できなくなりました。

しかし、私は@KJSRに同意します、この投稿はまだ本当に役に立ちます:-)

 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