I have these two view models:
public class SettingsViewModel
{
public string UserId { get; set; }
public PersonalViewModel Personal { get; set; }
}
public class PersonalViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
After implementing these view models I created a map with automapper:
Mapper.CreateMap<User, PersonalViewModel>()
.ForMember(vm => vm.Birthday, m => m.MapFrom(
u => (u.Birthday.HasValue) ? u.Birthday.Value.ToString("dd.MM.yyyy") : DateTime.Now.ToString("dd.MM.yyyy")));
Mapper.CreateMap<User, SettingsViewModel>()
.ForMember(vm => vm.UserId, m => m.MapFrom(
u => u.Id));
var viewModel = Mapper.Map<User, SettingsViewModel>(user);
Now I have the problem, that my property Personal in my SettingsViewModel is null. How can I combine my two mappings? How can I fill my Personal property with the data from my user object? In my user object, there are properties for FirstName und LastName.
You could set up your mapping this way:
Mapper.CreateMap<User, SettingsViewModel>()
.ForMember(vm => vm.UserId, m => m.MapFrom(u => u.Id))
.ForMember(vm => vm.Personal, opt => opt.MapFrom(u => u));
Here, we're saying map the Personal property from the User object itself. When you specify this, the mapping from User → PersonalViewModel will automatically be used.
Example: https://dotnetfiddle.net/YJnPDq
Related
I want map Origin.CityId and Origin.StateId properties of
Itinerary class to OriginCityId and OriginStateId properties
of ItineraryModel class.
Ex: Itinerary itinerary = Mapper.Map<Itinerary>(ItineraryModel);
My ViewModel
public class ItineraryModel : BaseModel
{
public int OriginCityId { get; set; }
public int OriginStateId { get; set; }
public bool Published { get; set; }
}
My Entity
public class Itinerary : BaseEntity
{
public City Origin { get; set; }
public bool Published { get; set; }
}
My mapping that tried do
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<ItineraryModel, Itinerary>()
.ForPath(x => x.Origin.CityId, opt => opt.MapFrom(src => src.OriginCityId))
.ForPath(x => x.Origin.StateId, opt => opt.MapFrom(src => src.OriginStateId))
.ReverseMap();
}
}
I would like to too .ReverseMap() but can't find right syntax.
You need to add two mappings for mapping to Itenerary
CreateMap<ItineraryModel, City>()
.ForMember(city => city.CityId, expression => expression.MapFrom(itineraryModel => itineraryModel.OriginCityId))
.ForMember(city => city.StateId, expression => expression.MapFrom(itineraryModel => itineraryModel.OriginStateId));
CreateMap<ItineraryModel, Itinerary>()
.ForMember(itinerary => itinerary.Origin, expression => expression.MapFrom(itineraryModel => itineraryModel));
Similarly you can define reverse mappings manually if needed.
BTW ReverseMap() is not recommended by author
https://jimmybogard.com/automapper-usage-guidelines/
I'm not too familiar with automapper, but it's been thrown on me by our Software Architect for this project.
The concept is complete encapsulation of volatility. Here's a poorly made Diagram
API/Presentation Layer |
Request and Response suffixed objects. (ie, ApplicationCreateRequest)
Business Layer |
Home of Domain transfer objects, suffixed DTO. (ie, ApplicationCreateDTO)
Database Layer |
Home of Resource Access Objects and entities suffixed RAO and Entity (ie, ApplicationEntity, ApplicationCreateRAO)
I need to convert ApplicationCreateRequests to ApplicationCreateDTO's, as well as convert the Requests Nested objects to DTO's as well.
For example:
public class ApplicationCreateRequest
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ContactCreateRequest Contact { get; set; }
public DemographicCreateRequest Demographic { get; set; }
public EducationCreateRequest Education { get; set; }
public WorkCreateRequest Work { get; set; }
}
becomes
public class ApplicationCreateDTO
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ContactCreateDTO Contact { get; set; }
public DemographicCreateDTO Demographic { get; set; }
public EducationCreateDTO Education { get; set; }
public WorkCreateDTO Work { get; set; }
}
The DTO's and Requests have for the most part the same properties.
I've only gotten to work with very basic mappings such as:
CreateMap<ObjectOne, ObjectTwo>();
A easy way to map complex model is to declare, and map them from easiest (with native types eg : strings, int,...) to complex ones.
So, you should create simple mappings for ContactCreateRequest to ContactCreateDTO with CreateMap (and others simplest). Then you will have to create something like :
MapFrom allows you to specify the property (a reason beeing if named differently) to map from. It also allows you to specify a result from a pre-defined mapping, you simply tell it the member you want to Map from...
Mapper.CreateMap<ApplicationCreateRequest, ApplicationCreateDTO>()
.ForMember(g => g.FirstName, opt => opt.MapFrom(src => src.FirstName));
.ForMember(g => g.LastName, opt => opt.MapFrom(src => src.LastName));
.ForMember(g => g.Contact, opt => opt.MapFrom(src => Mapper.Map<ContactCreateRequest,ContactCreateDTO>(g.Contact)));
.ForMember(g => g.Demographic, opt => opt.MapFrom(src => Mapper.Map<DemographicCreateRequest,DemographicCreateDTO>(g.Demographic)));
.ForMember(g => g.Education, opt => opt.MapFrom(src => Mapper.Map<EducationCreateRequest,EducationCreateDTO>(g.Education)));
.ForMember(g => g.Work, opt => opt.MapFrom(src => Mapper.Map<WorkCreateRequest,WorkCreateDTO>(g.Work)));
You can build up by using
.ForMember(g => g.Property, opt => opt.Ignore()); // to ignore the mapping of a property
Handy note, define base mapping before complex ones, otherwise you will have trouble!
I'm using Automapper to define a mapping configuration between some objects. I need to retrieve single mapped values for the destination based on the configuration.
Let's say I have a domain object
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
and a corresponding DTO
class PersonDTO
{
public string FullName { get; set; }
}
and a mapping
Mapper.CreateMap<Person, PersonDTO>()
.ForMember(
dest => dest.FullName,
opts => opts.MapFrom(src => src.FirstName + " " + src.LastName));
I'd like a method like Mapper.MapField<PersonDTO>(person, x => x.FullName) that returns just one mapped value (that would get assigned to the specified field in a full mapping). Is there any way I can do this without mapping the whole object and extracting the value from the result?
First create a resolver and then use the resolve using method in your mapper call.
public class PersonNameResolver : ValueResolver<Person, PersonDTO>
{
protected override PersonDTO ResolveCore(Person source)
{
return new PersonDTO
{
FirstName = source.FirstName,
LastName = source.LastName
};
}
}
Once this is created, use the following in your mapper call.
Mapper.CreateMap<Person, PersonDTO>()
.ForMember(
dest => dest.Name,
opts => opts.ResolveUsing<PersonNameResolver>().FromMember(o => o))
I have a db entity which stores the Order Addresses like this...
And I have the BLL classes like this...
public class DeliveryAddress
{
public string Id { get; set; }
public string PersonyName { get; set; }
public string CompanyName { get; set; }
public List<string> AddressLines { get; set; }
public string Zip { get; set; }
public string City { get; set; }
public string CountryCode { get; set; }
}
and another class like this...
public class InvoiceAddress
{
public string Id { get; set; }
public string PersonyName { get; set; }
public string CompanyName { get; set; }
public List<string> AddressLines { get; set; }
public string Zip { get; set; }
public string City { get; set; }
public string CountryCode { get; set; }
}
and I want to map the EF entity to the above classes on the basis of AddressType column. Can anybody explain me how to do that ?
UPDATE
I want to map to OR.DeliveryAddress if the addressType is "Delivery" and to OR.InvoiceAddress if the addressType is "Invoice"
So far, I have been able to do this, but I don't know how to apply condition on the entity mapping level...
Mapper.CreateMap<OrderAddress, OR.DeliveryAddress>()
.ForMember(d => d.City, o => o.MapFrom(s => s.city))
.ForMember(d => d.CompanyName, o => o.UseValue(string.Empty))
.ForMember(d => d.CountryCode, o => o.MapFrom(s => s.countryCode))
.ForMember(d => d.Id, o => o.MapFrom(s => s.id))
.ForMember(d => d.PersonyName, o => o.MapFrom(s => s.name))
.ForMember(d => d.Zip, o => o.MapFrom(s => s.zip));
UPDATE 2
After discussion with #Yuliam Here is the Fiddle that I could come up with for my problem...
You can create a customer mapper to object. And also you don't have to specify each property using ForMember because if the difference is only upper case / lower case (unless for PersonName), by default AutoMapper is case insensitive when mapping the property name.
Create a custom mapper to object.
public class AddressConverter : ITypeConverter<OrderAddress, object>
{
public object Convert(ResolutionContext context)
{
var o = context.SourceValue as OrderAddress;
if (o == null) return null;
if (o.addressType == "Delivery") return Mapper.Map<OR.DeliveryAddress>(o);
if (o.addressType == "Invoice") return Mapper.Map<OR.InvoiceAddress>(o);
return null;
}
}
Then define the mapper.
Mapper.CreateMap<OrderAddress, OR.DeliveryAddress>()
.ForMember(d => d.PersonyName, o => o.MapFrom(s => s.name));
Mapper.CreateMap<OrderAddress, OR.InvoiceAddress>()
.ForMember(d => d.PersonyName, o => o.MapFrom(s => s.name));
Mapper.CreateMap<OrderAddress, object>().ConvertUsing<AddressConverter>();
Usage.
var orderAddressDto = Mapper.Map<object>(orderAddress);
The actual orderAddressDto type will be based on the addressType. If you have an interface or base class for OR.DeliveryAddress and OR.InvoiceAddress that would be more strongly type. Then replace the object type with the interface / base class.
You may want to try taking a look at ResolveUsing
Semi-pseudo code, as I don't know what your entire Domain Model looks like:
Mapper.CreateMap<OrderObject, OrderDto>()
.ForMember(x => x.Address, opt => opt.ResolveUsing(oo => oo.Type == Invoice ? oo.InvoiceAddress : oo.DeliveryAddress));
I'm assuming here that you have an actual Order entity, which you're trying to make to an 'OrderDto' which only contains one address field.
I have two classes that looks as follows:
public class Rule
{
public int Id { get; set; }
public RuleGroup RuleGroup { get; set; }
}
public class RuleGroup
{
public int Id { get; set; }
public List<Rule> RuleList { get; set; }
}
A RuleGroup has a list of rules. My AutoMapper settings are as follows:
Mapper.CreateMap<RuleRecord, FirstSolar.Mes.Core.Entities.Recipe.Rule>()
.ForMember(destination => destination.RuleGroup, source => source.Ignore())
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleId));
Mapper.CreateMap<IList<RuleRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.Rule>>();
Mapper.CreateMap<RuleGroupRecord, FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>()
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleGroupId));
Mapper.CreateMap<IList<RuleGroupRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>>();
When I attempt to map a RuleGroupRecord (LinqToSQL object) to RuleGroup (DTO), AutoMapper says I need to add a mapping for RuleGroup.RuleList. I'm wondering why because I defined how to map a single RuleRecord and a List.
If I have to, how would I do it?
Simply add (I hope I got the syntax right, but you should see what I'm hinting at):
.ForMember(destination => destination.RuleList, source => source.MapFrom(item => item.Rules));
to the second mapping. While you handled the general mapping for RuleRecord to Rule in the first mapping, you didn't tell automapper to map the specific property RuleGroup.RuleList.