AutoMapper issue- Automap the DTO,Viewmodel if DTO contains another DTo - c#

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.. :)

Related

While returning a DTO which is mapped using automapper, how do I assign it a list of it's child class type?

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.

Mapping from primtive type property to child object

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));

AutoMapper.Mapper.CreateMap<TSource,TDestination>()' is obsolete

I have to classes Like
class A
{
public int id {get; set;}
}
class B
{
public C c {get; set;}
}
class C
{
public int id {get; set;}
public string Name {get; set;}
}
My requirement is to map id of class A to id of class C.
Now what I was doing till now was:
Mapper.CreateMap().ForMember(des => des.C.Id, src => src.MapFrom(x => x.id));
and it was working fine.
Now seems like Auto mapper has changed their implementation. and I am getting warning as below:
AutoMapper.Mapper.CreateMap()' is obsolete: 'Dynamically creating maps will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed, or Mapper.Initialize. Use CreateMapper to create a mapper instance.
I need to map some properties of classes which has different name and structure. Any help on this.
Previously
Mapper.CreateMap<Src, Dest>()
.ForMember(d => d.UserName, opt => opt.MapFrom(/* ????? */));
The problem here is mapping definitions are static, defined once and reused throughout the lifetime of the application. Before 3.3, you would need to re-define the mapping on every request, with the hard-coded value. And since the mapping configuration is created in a separate location than our mapping execution, we need some way to introduce a runtime parameter in our configuration, then supply it during execution.
This is accomplished in two parts: the mapping definition where we create a runtime parameter, then at execution time when we supply it. To create the mapping definition with a runtime parameter, we “fake” a closure that includes a named local variable:
Mapper.Initialize(cfg => {
string userName = null;
cfg.CreateMap<Source, Dest>()
.ForMember(d => d.UserName,
opt => opt.MapFrom(src => userName)
);
});
For more information see this
For one or more classes
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<Source, Dest>()
.ForMember(d => d.UserName,
opt => opt.MapFrom(src => userName)
);
cfg.CreateMap<AbcEditViewModel, Abc>();
cfg.CreateMap<Abc, AbcEditViewModel>();
});
In mapping class
IMapper mapper = config.CreateMapper();
var source = new AbcEditViewModel();
var dest = mapper.Map<AbcEditViewModel, Abct>(source);
Another way that seems a bit cleaner is to make a MappingProfile class which inherits from the Profile class of AutoMapper
public class MappingProfile:Profile
{
public MappingProfile()
{
CreateMap<Source1, Destination1>();
CreateMap<Source2, Destination2>();
...
}
}
Then you initialize the mapping with Mapper.Initialize(c => c.AddProfile<MappingProfile>()); in your startup code
That will allow you to use the mapping anywhere by calling
destination1Collection = source1Collection.Select(Mapper.Map<Source1, Destination1>);
Finally I found the resolution. I was doing: Mapper.Initialize{ Mapping field from source to destination }
in the App_start and adding this file to the global.asax--> Application_Start() --> GlobalConfiguration.
I need to add one more line inside my Mapper.Initialize which is cfg.CreateMissingTypeMaps = true;
Now this code will work for explicit mapping where two classes don't have the same structure and names of properties.
Apart from this, if we need to map properties of two class with the same structure the code Mapper.map(source, destination) will also work, which was not working earlier.
Let me know if someone is having difficulty with the solution. Thanks all for the above reply.

Can you tell AutoMapper to globally ignore missing properties when mapping?

I have quite a bit of entities and so far, I've been doing stuff like
Mapper.CreateMap<Employee, EmployeeDetailsDTO>()
.ForSourceMember(mem => mem.NewsPosts, opt => opt.Ignore());
I want to tell AutoMapper to simply ignore missing properties in the destination object without having to specify each of them. So far, I haven't found a way to do so with my multiple SO and Google searches. Anyone has a solution? I'm ready to do some sort of loop or anything, as long as it can be written once and that it will scale with model / dto changes or added properties.
When are you getting the error? Is it when you call AssertConfigurationIsValid ?
If yes, then simply dont call this method
You dont have to call this method, consider the following mapping which works:
public class Foo1
{
public string Field1 { get; set; }
}
public class Foo2
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Mapper.CreateMap<Foo1, Foo2>();
var foo1 = new Foo1() {Field1 = "field1"};
var foo2 = new Foo2();
Mapper.Map(foo1, foo2);//maps correctly, no Exception
You may want to call AssertConfigurationIsValid for other mappings to ensure they are correct so instead what you need to do is organize your mappings into Profiles:
public class MyMappedClassesProfile: Profile
{
protected override void Configure()
{
CreateMap<Foo1, Foo2>();
//nb, make sure you call this.CreateMap and NOT Mapper.CreateMap
//I made this mistake when migrating 'static' mappings to a Profile.
}
}
Mapper.AddProfile<MyMappedClassesProfile>();
and then if you decide you want to check the validity of the mapping (case by case basis in your situation) then call
Mapper.AssertConfigurationIsValid(typeof(MyMappedClassesProfile).FullName);
important in your case and/or any case where you dont call AssertConfigurationIsValid you should use something like AutoFixture and a Unit Test to ensure your mapping is working. (which is the intent of AssertConfigurationIsValid)
Suggested in wal's answer "don't call AssertConfigurationIsValid()" is not safe, as it will hide potential errors in mappings.
It's better to explicitly ignore mapping between classes, for which you are sure that all needed properties already mapped correctly. You can use extensions created in AutoMapper: "Ignore the rest"? answer:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Src, Dest>();
cfg.IgnoreUnmapped<Src, Dest>(); // Ignores unmapped properties on specific map
});
The overload without parameters cfg.IgnoreUnmapped(this IProfileExpression profile)
ignores unmapped properties on all maps and not recommended, because it also hides any potential problems for all classes.
If I have many classes with many properties to ignore, I don't want to have exception when call AssertConfigurationIsValid(), but prefer to report it in a log and just review are all unmapped properties missed intentionally.
Because method to do validation is not exposed by AutoMapper, I catch AssertConfigurationIsValid and return error message as string.
public string ValidateUnmappedConfiguration(IMapper mapper)
{
try
{
mapper.ConfigurationProvider.AssertConfigurationIsValid();
}
catch (AutoMapperConfigurationException e)
{
return e.Message;
}
return "";
}
I am calling the ValidateUnmappedConfiguration method from unit test
[TestMethod]
public void LogUmmappedConfiguration()
{
var mapper = new MapperConfiguration((cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
})).CreateMapper();
var msg=ValidateUnmappedConfiguration(mapper) ;
if (!msg.IsNullOrBlank())
{
TestContext.WriteString("Please review the list of unmapped fields and check that it is intentional: \n"+msg);
}
}

AutoMapper configuration of List

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.

Categories