I have a source and destination object like so:
Source:
public class Team{
public string TeamName{get; set;}
public string List<Person> {get; set;}
}
public class Person{
public string FullName {get; set;}
}
Destination:
public class MyDTOClass{
public string TeamName;
public string PersonName;
}
I basically want to flatten a one-to-many relationship, duplicating the Name property, so the result would be:
MyDtoClass.TeamName= "X";
MyDtoClass.PersonName= "Y";
MyDtoClass.TeamName= "X";
MyDtoClass.PersonName= "Z";
Is there a way to do this with automapper?
I don't think AutoMapper can automatically go from a single Team to an array/collection of MyDTOObjects. However, it should be pretty easy to do what you want with LINQ:
var flattened = from p in team.Persons
select new MyDTOClass { TeamName = team.Name, PersonName = p.FullName}
I just started using Automapper, but here is solution I was able to come with:
Func<Team[], IEnumerable<MyDTOClass>> getPersonsFromTeams = (teams) => new IEnumerable<MyDTOClass>
{
teams.SelectMany(s => s.Persons, (t, person) => new MyDTOClass(team.TeamName, person))
};
mapper.CreateMap<Company, CompanyDTOs>()
.ForMember(d => d.Persons, o => o.ResolveUsing(s => s.getPersonsFromTeams(s.Teams)));
Related
I am having employee table:
public class Employee
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int SupervisorId {get;set;}
}
SupervisorId is a foreign key from the same table (Employee) that points to other employee.
Then I have something like "EmployeeSupervisorDto"
public class EmployeeSupervisorDto
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public string FullNameSupervisor {get;set;}
}
What I want to achievie is to use automapper to set FullNameSupervisor automaticly to combination of FirstName and LastName of supervisor.
Tried so far to do something like this:
cfg.CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, m => m.MapFrom(p => $"{p.LastName} {p.FirstName}"));
But I have no idea how to do reference to Id that points out to employee id that is supervisor of given employee.
To use the below solution, you will need to inject your data context to the auto mapper profile class (via constructor parameter), and also, in the ConfigureServices, add the DI of the automapper profile as shown in https://stackoverflow.com/a/49198279/9907597.
Create a method in the AutoMapper profile class:
public string GetEmployerFullName(int supervisorEmpId)
{
var supervisor = db.Employees.Find(supervisorEmpId);
return supervisor.FirstName + " " + supervisor.LastName;
}
Then create the mapping in the automapper profile class constructor as:
CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, m => m.MapFrom(p => GetEmployerFullName(p.SupervisorId)));
You can use either ValueResolver or something like this code if you want to use it once and not generally:
Mapper.CreateMap<Employee, EmployeeSupervisorDto>()
.ForMember(e => e.FullNameSupervisor, o => o.ResolveUsing(m => { return m.LastName + m.FirstName}));
May help you,
i try with linq approach :
public class Employee
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int SupervisorId {get;set;}
}
it give you the list of employe with sup FullNameSupervisor
var Employeelist = new List<Employee>() {...};
var EmployeeWithSup = from ep in Employeelist
join epsup in Employeelist on ep.SupervisorId equals epsup.Id
select new { Id = ep.Id,FirstName = ep.FirstName,LastName = ep.LastName,
SupervisorId = ep.SupervisorId,FullNameSupervisor = epsup.FirstName + " " + epsup.LastName };
If you want to do joins use automapper ,try the follow link :
AutoMapper to join records from 2 tables into single IEnumerable viewmodel
I have 3 classes:
public class CountryModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class CountryDTO
{
public int Id { get; set; }
public string Title { get; set; }
}
public class BaseCountryDTO
{
public CountryDTO Country {get; set};
}
I need to map CountryDTO to CountryModel, but through BaseCountryDTO class.
I know that I can do it like this:
CreateMap<BaseCountryDTO, CountryModel>()
.ForMember(model => model.Id, o => o.MapFrom(dto => dto.Country.Id))
.ForMember(model => model.Title, o => o.MapFrom(dto => dto.Country.Title));
But I want to do it clear, something like this:
// This is not working code, just my imagination :)
CreateMap<BaseCountryDTO, CountryModel>()
.ForMember(model => model, dto => dto.Country));
Because in model can be more than 2 properties. Is there way to do it?
#LucianBargaoanu Helped me with the link https://docs.automapper.org/en/latest/Flattening.html#includemembers
It solved my problem.
The solution looks like:
CreateMap<BaseCountryDTO, CountryModel>().IncludeMembers(s => s.Country);
CreateMap<CountryDTO, CountryModel>();
So the thing is we have to create a map of base class to our model with include of what we are really want to map. Then we should create a map of class which we are really need to map.
If the properties in CountryModel and CountryDTO have same names/types, then you can configure the mapping simply as -
CreateMap<CountryDTO, CountryModel>();
You can test the mapping as -
CountryDTO dto = new CountryDTO { Id = 4, Title = "Something" };
CountryModel model = Mapper.Map<CountryModel>(dto);
It will automatically map the properties from CountryDTO to CountryModel, no matter how many they are. You don't have to manually configure mapping for any property, or go through another class like BaseCountryDTO.
I have this three model in my databse:
public class Parent{
public Child B {get; set;}
// ....more properties....
}
public class Child{
public City City {get; set;}
// ....more properties....
}
public class City {
public string Name{get; set;}
}
now I want to get all parent including its children and also city of children using this code:
using (var ctx = new DataBaseCtx())
{
var result = ctx.Parents.Include(x => x.B.City)
.Select(x => new ParentDTO
{
B= x.B,
// .... other properties ....
}).ToList();
}
but when i try to access to result.B.City this is null...
where is wrong in my code or approach?
You have to include your child class before calling city
var result = ctx.Parents.Include(x => x.Child).Include(y=>y.City).ToList();
I would use AutoMapper to create an expression to use with LINQ (e.g. LINQ To SQL):
void Main()
{
Mapper.CreateMap<Person, PersonDto>();
Mapper.Engine.CreateMapExpression<Person, PersonDto>().ToString().Dump();
}
public class Person
{
public string Name { get; set; }
public IEnumerable<Person> Children { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public IEnumerable<PersonDto> Children { get; set; }
}
But a StackOverflowException occurs mapping the "Children" property (I had already written to Jimmy in the past).
The author told that the problem is that the root class has a self reference, but it's not supported by many LINQ providers.
If the root class has not self-references the problem is another with AutoMapper 3,
because the produced LINQ expression is:
x => new PersonDto() { Name = x.Name, Children = x.Children.Select(y => new AddressDto { Name = y.Name })}
Instead of:
x => new PersonDto() { Name = x.Name, Children = IIF((x.Children == null), null, x.Children.Select(y => new AddressDto { Name = y.Name }))}
So if the child property of the root class is null, the projection will fail.
At the moment is not possible to use AutoMapper for this purpose.
I have something like this
public class ProductViewModel
{
public int SelectedProductId { get; set; }
public string ProductName {get; set;}
public int Qty {get; set;}
public List<SelectListItem> Products { get; set};
}
I have a domain like this
public class Product
{
public int ProductId {get; set;}
public string ProductName {get; set;}
public int Qty {get; set;}
}
public class Store
{
public Product() {get; set;}
}
Now I need to do the mapping.
// in my controller
var result = Mapper.Map<ProductViewModel, Store>(Product);
this won't bind anything since it can't figure out how to put the ProductId in since it
is
Store.Product.ProductId;
My map is like this
Mapper.CreateMap<ProductViewModel, Store>().ForMember(dest => dest.Product.ProductId, opt => opt.MapFrom(src => src.SelectedProductId));
I get this error
Expression 'dest =>
Convert(dest.Product.SelectedProductId' must
resolve to top-level member. Parameter
name: lambdaExpression
I am unsure how to do this.
To Map nested structures, you just need to create a new object in the MapFrom argument.
Example
Mapping:
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.MyNestedType, o => o.MapFrom(t => new NestedType { Id = t.Id }));
Mapper.AssertConfigurationIsValid();
Test Code:
var source = new Source { Id = 5 };
var destination = Mapper.Map<Source, Destination>(source);
Classes:
public class Source
{
public int Id { get; set; }
}
public class Destination
{
public NestedType MyNestedType { get; set; }
}
public class NestedType
{
public int Id { get; set; }
}
You can use Resolver.
Create a resolver class like that :
class StoreResolver : ValueResolver<Store, int>
{
protected override int ResolveCore(Store store)
{
return store.Product.ProductId;
}
}
And use it like that :
Mapper.CreateMap<ProductViewModel, Store>()
.ForMember(dest => dest.SelectedProductId, opt => opt.ResolveUsing<StoreResolver >());
Hope it will help ...
The error your getting is because you cannot declare mapping declarations more than one level deep in your object graph.
Because you've only posted one property its hard for me to give you the codes that will make this work. One option is to change your viewmodel property to MyTestTestId and the conventions will automatically pick up on that.
The correct answer given by allrameest on this question should help: AutoMapper - Deep level mapping
This is what you need:
Mapper.CreateMap<ProductViewModel, Store>()
.ForMember(dest => dest.Product, opt => opt.MapFrom(src => src));
Mapper.CreateMap<ProductviewModel, Product>()
.ForMember(dest => dest.ProductId, opt => opt.MapFrom(src => src.SelectedProductId));
NOTE: You should try to move away from using Mapper.CreateMap at this point, it is obsolete and will be unsupported soon.