I have an entity and a corresponding DTO
public class PersonEntity {
public int personId;
public List<Contact> contacts;
}
public class PersonDto {
public int personId;
public List<int> contacts;
}
Using the following map with AutoMapper
Mapper.Map<PersonDto, Person>();
I'm using AutoMapper to get the DTO, which isn't a problem.
I'm parsing the DTO back to the Entity, to update fields in the Entity for a save operation and I'm not really interested in the list of contacts anymore. Automapper throws an exception with this as it doesn't like mapping the list of int's to a list of objects.
any suggestions or better ways to do this please.
Edit
solution used is
Mapper.CreateMap<PersonDto, Person>()
.ForMember(x => x.contacts, y => y.Ignore());
Can you use the ignore method in configuration?
http://automapper.codeplex.com/wikipage?title=Configuration%20Validation
opt => opt.Ignore()
But. Do you really need to update the entity just to save? Why don't you send a command which contains the changed data.
AutoMapper as you may know, looks for properties in objects with the same name & type, therefore you will most likely need to alter your DTO or Entity so they match.
Related
I have an entity
Topics having these properties
1. public string Name {get; set;}
2. public string Location {get; set;}
3. public string Identity {get; set;}
4. public TopicsMapped TopicsMapped {get; set;}
Also, I have one DTO that is TopicsDTO and it has properties
1. public string Name {get; set;}
2. public string Location {get; set;}
3. public string Identity {get; set;}
4. public TopicsMapped TopicsMapped {get; set;}
so in Automapper.config I have created a mapping of these 2.
roughly,
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Topics, TopicsDTO>();
});
now in webapi method, I am using linq to sql and I have written something like this
var query= (from t in topics select t).tolist();
Topics t= new topics();
var mapper = new Mapper(config);
var topicsDTO = mapper.Map<List<TopicsDTO>>(query);
return OK(topicsDTO) //since the webapi method returning IHttpActionResult.
But my problem is that I have to assign a list of TopicsMapped type and return in OK as a part of TOPICs DTO. I tried taking join of both types and assign to query but it threw error that mapping configuration is not properly configured.
How do I do this?
Defining a DTO for your web interface is good, but the purpose of implementing a DTO is to provide a model specific to what the consumer needs and to avoid passing more data that is needed, or data/schema information that the consumer has no right to see.
Assuming TopicsMapped is a class containing some fields, the relationship between Topic & TopicsMapped is a one-to-one. Typically in a one-to-one relationship you might want to provide some relevant fields from the related entity. A general rule to follow when mapping DTOs or ViewModels is don't mix DTOs (view models) with entities. (data models) This can lead to problems because if TopicsMapped has references back to Topic or other entities/tables, putting that entity into your DTO could see lazy load calls or exceptions when serializing your DTO. Either create a DTO graph for related entities where needed, or flatten the DTO model to represent the data you care about.
For example, if the TopicsMapped has a field called "Name" that we want to include for our web API, we don't need the entire TopicsMapped model, just the field. So we can flatten that down in the DTO:
[Serializable]
public class TopicDTO
{
public int TopicId { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Identity { get; set; }
public string MappedName { get; set; } // <- Will map to TopicsMapped.Name
}
Then with the automapper configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Topics, TopicsDTO>()
.ForMember(x => x.MappedName, opt => opt.MapFrom(src => src.TopicsMapped.Name));
};
By default Automapper can use convention to work out mappings for destination fields from the source object, and if you follow those conventions in your DTO it is possible to avoid an explicit mapping, but I honestly prefer explicit mapping to avoid the guesswork and bugs if property names get changed. It also helps ensure usage counts for properties are properly accounted for. Linq2SQL should provide access to IQueryable, so to properly leverage projection /w Automapper you will want to use ProjectTo rather than Map:
var topicDTOs = Context.Topics
.ProjectTo<TopicDTO>(config)
.ToList();
This will involve adding a using clause for "Automapper.QueryableExtensions". The difference between ProjectTo and Map is that with Map, the SQL generation happens with your ToList call. Map would have to resort to lazy loading the data from the TopicMapping class or you would have to set it up to eager-load that data. In both cases this would essentially include all fields from TopicMapping even though we only want 1 field in this example. ProjectTo on the other hand lets Automapper in on the SQL generation. The resulting query would only return the fields that the DTO needs from the Topic and TopicMapping tables, nothing more. No need to eager load related entities, and it produces much more streamlined queries.
Disclaimer: From what I have read, IQueryable and Automapper's ProjectTo should be supported with Linq2SQL. If you encounter issues I would strongly recommend considering using Entity Framework rather than Linq2SQL as it has full support for projection, eager, and lazy loading.
I am trying to use AutoMapper for the usual scenario Dto -> Entity. But one of the Dto's properties should map to my Entity's child.
My Employee DTO has a property of type int, JobTitleId.
My Employee Entity has a child Entity JobTitle, which has an property of id. This is where i want to map to. So summarized it will be something like:
EmployeeDto.JobTitleId ---> Employee.JobTitle.Id
The only thing that worked for me was adding a Custom Automapper Resolver, but it gets too nasty when I need to do that for many different but similar scenarios.
class EmployeeDto
{
int JobTitleId;
}
Class Employee
{
JobTitle jobTitle;
}
Class JobTitle
{
int Id;
}
I would like something similar to this:
public class EmployeeMapperProfiles : Profile
{
public EmployeeMapperProfiles()
{
CreateMap<EmployeeDto, Employee>()
.ForMember(dest => dest.JobTitle.Id,
opt => opt.MapFrom(src => src.JobTitleId));
}
}
However, my result with this approach is:
System.ArgumentException: 'Expression 'dest => dest.JobTitle.Id' must resolve to top-level member and not any child object's properties. You can use ForPath, a custom resolver on the child type or the AfterMap option instead. (Parameter 'lambdaExpression')'.
But AfterMap and custom resolver approaches are not what I want.
So it must be ForPath, can't get it to work though.
Explanation
Exception message is telling you that you cannot specify custom mapping rules for child object properties, like this:
CreateMap<EmployeeDto, Employee>()
.ForMember(
employee => employee.JobTitle.Id,
options => options.MapFrom(employeeDto => employeeDto.JobTitleId));
That's because only top-level members are supported, and for deeper mapping you should use tools like value resolvers, type converters, explicit path configurations or additional mappings between types.
Solution
To specify source property for Id of Employee you should use path configuration like you already figured it out. It's pretty straightforward, like this:
CreateMap<EmployeeDto, Employee>()
.ForPath(
employee => employee.JobTitle.Id,
options => options.MapFrom(employeeDto => employeeDto.JobTitleId));
I try to move my project to AutoMapper 9.x but I don't find a solution to update an object read from EF.
The situation is like as describe on this old post Using Automapper to update an existing Entity POCO
The big issue is that:
AutoMapper 9.x has removed the static Mapper.Map and you can add the [AutoMap(typeof(x))] attribute but it solve the mapping from EF Class to DTO class.
class MyPoco{
public int Id {get;set;}
}
[AutoMap(typeof(MyPoco))]
public class Customer {
public int Id { get;set; }
}
MYPOCO pocoDesc= dbContext.DD_POCO.SingleOrDefault(m => m.Id == 123);
//Row below not supported
AutoMapper.Mapper.Map<Customer, MYPOCO>(customerSrc, pocoDesc);
dbContext.Save();
At the moment the only way to solve is remove AutoMapper and manually bind one by one the property.
UPDATE
After some test I try to compare to call
ObjectMapper.Map(input.Article, article);
Mapper.Instance.Map(input.Article, article);
ObjectMapper came from DI. In startup module I register via IMapperConfigurationExpression
mapper.CreateMap<Customer,MyPoco>().ConvertUsing((src,dto) =>{
// remove for brevity
});
And I see that static API not pass throw my custom map opposite to ObjectMapper that use my custom map. At the moment I don't understand why but I try to investigate more
Automap the dto with subDto
class productsDTO
{
public int id;
public AddressDTO DeliveryAddress;
}
Class productsViewModel
{
public int id;
public AddressViewModel DeliveryAddress;
}
Here, I have one dto class. I just want to auto map the dTo class into view model. In DTO class has AddressDTo that has to be automatically mapped AddressViewModel in productsViewModel.
If anyone have solution just post
var products = [some objects];
products.ForEach(a =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ProductsDTO, ProductsViewModel>()
.ForMember(dest => dest.DeliveryAddress, opts =>opts.Ignore())
.AfterMap((src, dest) => {
dest.DestinationAddress =
Mapper.Map(src.DeliveryAddress,dest.DeliveryAddress);
});
});
IMapper iMapper = config.CreateMapper();
var productList = iMapper.Map<ProductsDTO, ProductsViewModel>(a);
products.add(productList)
});
This code produce an error like this : "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."
You need to add a second mapping, between AddressDTO and AddressViewModel. When when you map between productsDTO and productsViewModel it will automatically map those properties as well, and AutoMapper will know how the mapping should work.
In the same way you mapped productsDTO and productsViewModel, you can map these classes too:
cfg.CreateMap<AddressDTO, AddressViewModel>();
Also, it should not be necessary to define your mappings multiple times inside a loop, as you are doing now. The mapping is simply a definition, to be used later. It only needs to be specified once. When you want to actually carry out a mapping of two objects, then use Mapper.Map - use this as many times as you have objects. It will use the mapping definition you created beforehand. But cfg.CreateMap only needs to be called once for each combination of classes. I'm also not sure you need the whole "Ignore" business in your code - it seems redundant to ignore a property and then map it again on the next line. You can also convert all the objects in the products list in a single Map operation.
Something like this should work better, I would think:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ProductsDTO, ProductsViewModel>();
cfg.CreateMap<AddressDTO, AddressViewModel>();
});
IMapper iMapper = config.CreateMapper();
var products = [some objects];
List<ProductViewModel> productVMList = iMapper.Map<List<ProductViewModel>>(products);
Firstly, You map AddressDTO and AddressViewModel properties.If you give same property name AutoMapper mapped automatically. If you wanna to map properties with different name, You define which property equals with other...
Docs ;
http://docs.automapper.org/en/stable/Projection.html
After You will mapped productsDTO and productsViewModel and Everythings work fine.. :)
I am facing issue for mapping for nested objects using Auto Mapper method
Can any one give suggestion to map Source and Target class using auto mapper
For example this is sample code.
Source:
Main Class:
public class EMP {
public int empid {get;set;}
public int Name {get;set;}
public EMPDetails Employeedata {get;set;}//Here is inner object in main class
public EmpContact EmpContactdetails {get;set;}//Here is other inner object in main class
}
public Class EMPDetails {
public String Name {get;set;}
public EmpAdd EmpAddress {get;set;}//Here is inner Object in one class
}
public class EmpAdd {
public int id {get;set;}
public int Street {get;set;}
}
public class EmpContact {
public int id {get;set;}
public int Mobile {get;set;}
}
Target:
Main Class:
public class EMPInfo {
public int id {get;set;}
public int EmpName {get;set;}
public EMPPerdata Employeedata {get;set;}//Here is inner object in main class
public EmpContact EmpContactdetails {get;set;}//Here is other inner object in main class
}
public class EMPPerdata {
public String FullName {get;set;}
public EmpAdressInfo EmpAddress {get;set;}//Here is inner Object in one class
}
public class EmpAdressInfo {
public int id {get;set;}
public int Street {get;set;}
}
public class EmpContact {
public int id {get;set;}
public int Mobile {get;set;}
}
Original Answer
You'll need to create maps for both the outer and inner classes.
Ex:
Mapper.CreateMap<Emp,EmpInfo>();
Mapper.CreateMap<EmpDetails,EMPPerdata>();
etc...
Then:
EmpInfo Dest = Mapper.Map<Emp, EmpInfo>(srcObj);
That should work.
Edit (because that's not a great answer)
So, just wanted to add on this edit to show a few AutoMapper best practices because if you follow those best practices it's a lot easier to use.
AutoMapper Profiles
When using Automapper, unless you are mapping really really simple objects, the best way to set up your mapping is through using profiles. To use those profiles make certain to add a using AutoMapper; to your program and then you can create a mapping profile like so:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<EMP, EMPInfo>();
}
}
Now, in any of the classes you want to use this profile you can use it like so:
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
EMPInfo empInfo = Mapper.Map<EMP, EMPInfo>(emp);
Now... why would you ever go to all of the trouble to do this? You could have just as easily said Mapper.CreateMap<EMP, EMPInfo>(); Mapper.Map<EMP, EMPInfo(src); and it would be doing the same thing.
The benefits to doing it the way I stated above are three-fold.
Centralization of your mapping expressions. In my case, I usually have a single MappingProfiles class that holds all the Mapping profiles that I could use. This practice helps more to organize your code than anything else.
Reusability. Wherever in your program you need to use that mapping profile which will likely be more than just a single CreateMap() line you can just initialize that profile and then use it.
Unit Testing. Loading in profiles makes unit testing for AutoMapper really easy. And, unit testing will help you figure out exactly how to map nested objects.
Unit Testing AutoMapper
In your unit testing project (add one if you don't have it, it's a default project template in visual studio) you should have a class that holds the following unit test:
[TestMethod]
public void AutoMapperConfigIsValid()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MappingProfile>();
// add all profiles you'll use in your project here
});
Mapper.AssertConfigurationIsValid();
}
Now, if I were to run that unit test using the classes you laid out above and the mapping profile that I put above it will give me the error:
Unmapped Properties:
id
EmpName
This isn't the full error, just the most important part. What's happening is... AutoMapper cannot figure out where the id and EmpName fields on the EMPInfo Type should be mapped from.
To resolve this error we need to add mapping for particular members to our profile. Our profile becomes:
CreateMap<Source.EMP, Target.EMPInfo>()
.ForMember(dest => dest.id, opt => opt.MapFrom(src => src.empid))
.ForMember(dest => dest.EmpName, opt => opt.MapFrom(src => src.Name));
We add that in... run the test again... get a new error.
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type TestStuff.Target.EmpContact.
Context:
Mapping from type TestStuff.Source.EmpContact to TestStuff.Target.EmpContact
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
This tells us that we need to add in a map for the nested members of the objects we are mapping.
To resolve this, we add the following map to the mapping profile:
CreateMap<Source.EmpContact, Target.EmpContact>();
Now what you need to do here, and this is sort of an arduous, but necessary, process, is keep running that configuration test and fixing the issues that it gives you until the test does not fail.
When all the issues are fixed, the mapping profile looks like this:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source.EMP, Target.EMPInfo>()
.ForMember(dest => dest.id, opt => opt.MapFrom(src => src.empid))
.ForMember(dest => dest.EmpName, opt => opt.MapFrom(src => src.Name));
CreateMap<Source.EmpContact, Target.EmpContact>();
CreateMap<Source.EMPDetails, Target.EMPPerdata>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
CreateMap<Source.EmpAdd, Target.EmpAdressInfo>();
}
}
This mapping profile works great and will map your two objects fine.
Now, this map seem to be a lot of work for a little thing, but the benefits of doing it this way come with scale. If you are ever using AutoMapper in a large 'Adapter' type project, code structured this way is going to be easier to navigate, read and test.
Hopefully this helps a bit. Sorry if I went way too far at any point here but hopefully this answer can help anyone trying to use AutoMapper.
You can find documentation on some of the stuff I mentioned above in the AutoMapper documentation as well: https://github.com/AutoMapper/AutoMapper/wiki/Configuration
you will need to create maps for all your models
Mapper.CreateMap<EMP,EmpInfo>();
Mapper.CreateMap<EmpDetails,EMPPerdata>();
Mapper.CreateMap<EmpContact,EmpContact>();
Mapper.CreateMap<EmpAdd,EmpAdressInfo>();
then:
Mapper.Map<Emp, EmpInfo>(someEmp);