AutoMapper with complex types
I'm having a problem trying to use complex types with AutoMapper.
I have two objects, a domain and a ViewModel that utlize in my View;
ViewModel
public class RegisterCoupleModel
{
[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "Primeiro nome", Order = 0, GroupName = "Noivo")]
[StringLength(60)]
public string FistNameGroom
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "Último nome", Order = 1, GroupName = "Noivo")]
[StringLength(60)]
public string LastNameGroom
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[RegularExpression(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", ErrorMessage = "Formato de email inválido.")]
[DataType(DataType.EmailAddress)]
[Remote("IsEmailAvailable", "Validation", "", ErrorMessage = "este email já está sendo usado por outro usuário.")]
[Display(Name = "Email", Order = 2, GroupName = "Noivo")]
[StringLength(180)]
public string EmailGroom
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "Primeiro nome", Order = 0, GroupName = "Noiva")]
[StringLength(60)]
public string FistNameBride
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "Último nome", Order = 1, GroupName = "Noiva")]
[StringLength(60)]
public string LastNameBride
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[RegularExpression(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", ErrorMessage = "Formato de email inválido.")]
[DataType(DataType.EmailAddress)]
[Remote("IsEmailAvailable", "Validation", "", ErrorMessage = "este email já está sendo usado por outro usuário.")]
[Display(Name = "Email", Order = 2, GroupName = "Noiva")]
[StringLength(180)]
public string EmailBride
{
get;
set;
}
[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "Url")]
[RegularExpression(@"^[a-zA-Z0-9_\-\.\+]{5,22}$", ErrorMessage = "Chave inválida")]
[StringLength(22, MinimumLength = 5, ErrorMessage = "A chave deve ter entre {0} e {1} caracteres.")]
[Remote("IsKeywordAvaliable", "Validation")]
public string UrlKeyword
{
get;
set;
}
[Display(Name = "Voce é humano?")]
[UIHint("ReCaptcha")]
public string ReCaptcha
{
get;
set;
}
}
Domain Object
public class Couple
{
[Key]
public Guid Id { get; set; }
public string UrlKeyword { get; set; }
public virtual Partner Groom { get; set; }
public virtual Partner Bride { get; set; }
public DateTime Marriage { get; set; }
public DateTime Dating { get; set; }
public DateTime Engagement { get; set; }
public virtual ICollection<User> Users { get; set; }
}
If you look you will see that my object Couple
domain has the properties has Bride
and Groom
which are actually the same type.
How can I map my domain object Couple
with the RegisterCoupleModel
?
Here I made it as far as:
In settings automapper tried to do something like:
CreateMap<RegisterCoupleModel, Couple>()
.ForMember(dest => dest.Bride.User.FirstName, opt => opt.MapFrom(source => source.FistNameBride))
.ForMember(dest => dest.Bride.User.LastName, opt => opt.MapFrom(source => source.LastNameBride))
.ForMember(dest => dest.Bride.User.Email, opt => opt.MapFrom(source => source.EmailBride))
.ForMember(dest => dest.Groom.User.FirstName, opt => opt.MapFrom(source => source.FistNameGroom))
.ForMember(dest => dest.Groom.User.LastName, opt => opt.MapFrom(source => source.LastNameGroom))
.ForMember(dest => dest.Groom.User.Email, opt => opt.MapFrom(source => source.EmailGroom));
But the error below is displayed:
Expression 'dest => dest.Bride.User.FirstName' must resolve to top-level member. Parameter name: lambdaExpression
I know this takes the act of trying to use to map properties with nested types.开发者_C百科
But how can I map RegisterCoupleModel
to Couple
and the two properties Bride
and Groom
are the same type?
I found a question here on StackOverflow that looks like this, but it helped me.
What i would do is encapsulate the properties for FirstNameBride, LastNameBride, EmailBride, FirstNameGroom, LastNameGroom, EmailGroom into a nested viewmodel type, let's say PersonDetails
:
public class PersonDetails
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
And update your parent VM accordingly:
public class RegisterCoupleModel
{
public PersonDetails GroomDetails { get; set; }
public PersonDetails BrideDetails { get; set; }
}
Then you can provide the mapping from PersonDetails
to User
(or whatever type Groom.User is):
Mapper.CreateMap<PersonDetails,User>();
Note how i've got so explict property mapping -> because the fields have the same name in the source and destination, so no explicit mapping is required. Always try and do this where possible. Less code -> better code.
Then in addition to the above mapping, simply do this:
Mapper.CreateMap<RegisterCoupleModel, Couple>();
And AutoMapper will see that RegisterCoupleModel
has two PersonDetails
objects, see it already has a mapping definition, then automatically use that.
That should work (i've done it before).
You shouldn't always use flat viewmodels, nest them where necessary (such as reusing fields).
精彩评论