一部のモデルデータを受け取り、少し変更するViewModelがあります。
DomainModel
をViewModel
のコンストラクターに渡すだけなので、「機能」しますが、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にマップするにはどうすればよいですか?
マップを作成するオートマッパーで、宛先タイプの特定のメンバーに追加のプロセスを指定できます。
したがって、デフォルトのマップは
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メソッドの構文を見つけたら、他のすべての種類のスロットを配置します。
カスタムマッピングは、以下のコードで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()メソッドを介して初期化を行うことができます。ただし、ビューモデルでいくつかの変更を行う必要がある場合があります。
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))));