I'm new to automapper and I'm trying very simple map. I have the following class and I', trying to map personmodel to person.
It keeps failing at orders. Sorry if this silly but I could not figure out. I removed all properties on orders to see what is wrong
public class Person
{
public Person()
{
Orders = new List<Orders>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<Orders> Orders { get; set; }
}
public class Orders
{
public string OrderName { get; set; }
}
public class PersonModel
{
public PersonModel()
{
Orders = new List<OrderModel>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public string OrderName { get; set; }
}
I have the mapper defined as Mapper.Initialize(x => x.CreateMap<PersonModel, Person>());
The error that I get is:
Error mapping types.
Mapping types:
PersonModel -> Person
PersonModel -> Person
Type Map configuration:
PersonModel -> Person
PersonModel -> Person
Property:
Orders
Create Map between Orders and OrderModel.
Mapper.CreateMap<Orders, OrderModel>().ReverseMap();
I use this code for map a list<>
var result = NewMapperHelper.MapList<OrderModel, Orders>(YourSource);
and:
public static class NewMapperHelper
{
public static List<TDestination> MapList<TDestination, TSource>
(List<TSource> entity)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TSource, TDestination>().ReverseMap();
});
var _mapper = config.CreateMapper();
return _mapper.Map<List<TDestination>>(entity);
}
}
Related
I'm mapping select expression (projection) of Linq query. This is done to decouple logic layer from data access layer and logic layer should use only DTOs.
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id,
Citizens = c.Citizens.Select(p => new CitizenDto
{
}).ToList()
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
This mapping fails with error Expression of type 'DTOs.CitizenDto' cannot be used for return type 'Entities.Citizen' however in CountyInfoDto property Citizens has type CitizenDto. Please note all mapping profiles are valid and simple objects can be mapped properly.
If I do like this, all works:
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
or this also works:
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id,
Citizens = new List<CitizenDto>
{
new CitizenDto
{
Id = c.Citizens.First().Id
}
}
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
is there any possibility to avoid this error?
Classes:
public class CountyInfo
{
public CountyInfo()
{
Citizens = new HashSet<Citizen>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Citizen> Citizens { get; set; }
}
public class Citizen
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
}
public class CountyInfoDto
{
public CountyInfoDto()
{
Citizens = new List<CitizenDto>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public List<CitizenDto> Citizens { get; set; }
}
public class CitizenDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
}
Mappings:
CreateMap<CountyInfo, CountyInfoDto>().ReverseMap();
CreateMap<Citizen, CitizenDto>().ReverseMap();
I'm using AutoMapper.Extensions.ExpressionMapping, after update to latest version error is: No coercion operator is defined between types 'Entities.CountyInfo' and 'DTOs.CountyInfoDto'.
I have created classes using EF Code First that have collections of each other.
Entities:
public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<AppUser> Teachers { get; set; }
public Field()
{
Teachers = new List<AppUser>();
}
}
public class AppUser
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public virtual List<Field> Fields { get; set; }
public AppUser()
{
Fields = new List<FieldDTO>();
}
}
DTOs:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<AppUserDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<AppUserDTO>();
}
}
public class AppUserDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
Mappings:
Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
And I am getting StackOverflowException when calling this code (Context is my dbContext):
protected override IQueryable<FieldDTO> GetQueryable()
{
IQueryable<Field> query = Context.Fields;
return query.ProjectTo<FieldDTO>();//exception thrown here
}
I guess this happens because it loops in Lists calling each other endlessly. But I do not understand why this happens. Are my mappings wrong?
You have self-referencing entities AND self-referencing DTOs. Generally speaking self-referencing DTOs are a bad idea. Especially when doing a projection - EF does not know how to join together and join together and join together a hierarchy of items.
You have two choices.
First, you can force a specific depth of hierarchy by explicitly modeling your DTOs with a hierarchy in mind:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<TeacherDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<TeacherDTO>();
}
}
public class TeacherDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
}
public class AppUserDTO : TeacherDTO
{
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
This is the preferred way, as it's the most obvious and explicit.
The less obvious, less explicit way is to configure AutoMapper to have a maximum depth it will go to traverse hierarchical relationships:
CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
I prefer to go #1 because it's the most easily understood, but #2 works as well.
Other option is using PreserveReferences() method.
CreateMap<AppUser, AppUserDTO>().PreserveReferences();
I use this generic method:
public static TTarget Convert<TSource, TTarget>(TSource sourceItem)
{
if (null == sourceItem)
{
return default(TTarget);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var serializedObject = JsonConvert.SerializeObject(sourceItem, deserializeSettings);
return JsonConvert.DeserializeObject<TTarget>(serializedObject);
}
...
MapperConfiguration(cfg =>
{
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
...
When you giving 1 navigation_property to 2nd entity and visa-versa it go in an infinite loop state. So, the compiler automatically throws a Stackoverflow exception.
So, to avoid that, you just need to remove one navigation_property from any of the entities.
I have 2 DTO classes that have multiple common properties, I'm trying to avoid having to repeat myself when writing mapping code for entity to DTO conversion, I'm wondering how I could achieve this, I have a feeling I need to probably use a Func or Action delegate to achieve this. For example I have 2 classes StudentDTO and EmployeeDTO:
public class StudentDTO : PersonDTO
{
public int CourseId { get; set; }
//other properties
}
public class EmployeeDTO : PersonDTO
{
public int OccupationId { get; set; }
//other properties
}
and both naturally inherit from PersonDTO:
public class PersonDTO
{
public int Id { get; set; }
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int Age { get; set; }
}
How could I reuse the mapping code that maps the common properties? Thanks.
You probably can do something like this (very basic and not elegant):
(note Entity can off course be a DataReader, DataSet etc.)
public class Entity
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int CourseId { get; set; }
public int OccupationId { get; set; }
}
public class BaseDto
{
}
public class PersonDto : BaseDto
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public static void Map(Entity entity, PersonDto personDto)
{
personDto.FirstName = entity.FirstName;
personDto.FamilyName = entity.FamilyName;
}
}
public class StudentDto : PersonDto
{
public int CourseId { get; set; }
public static StudentDto Map(Entity entity)
{
var studentDto = new StudentDto { CourseId = entity.CourseId };
// ..can call map to PersonDto if you want
return studentDto;
}
}
public class EmployeeDto : PersonDto
{
public int OccupationId { get; set; }
public static EmployeeDto Map(Entity entity)
{
var employeeDto = new EmployeeDto() { OccupationId = entity.OccupationId };
// ..can call map to PersonDto if you want
return employeeDto;
}
}
public class Mapper<TDto>
where TDto : BaseDto
{
private TDto _dto;
private readonly Entity _entity;
public Mapper(Entity entity)
{
_entity = entity;
}
public Mapper<TDto> Map(Func<Entity, TDto> map)
{
_dto = map(_entity);
return this;
}
public Mapper<TDto> Map<TBaseDto>(Action<Entity, TBaseDto> map)
where TBaseDto : BaseDto
{
map(_entity, _dto as TBaseDto);
return this;
}
public TDto Result
{
get { return _dto; }
}
}
class Program
{
static void Main(string[] args)
{
var studentEntity = new Entity() { FirstName = "John", FamilyName = "Doe", CourseId = 1 };
var studentDto = new Mapper<StudentDto>(studentEntity)
.Map(StudentDto.Map)
.Map<PersonDto>(PersonDto.Map)
.Result;
}
}
Use a library.. that's what they are there for!
Automapper
ValueInjecter
In Automapper, your above mapping becomes incredibly simple:
Mapper.CreateMap<EmployeeDTO, StudentDTO>();
Mapper.CreateMap<StudentDTO, EmployeeDTO>();
..then when you want to map:
var studentInstance = ...; // go get student instance
var employee = Mapper.Map<Employee>(studentInstance);
I have a viewmodel which needs data from two models person and address:
Models:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Gender { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public int Zip { get; set; }
public int PersonId {get; set; }
}
The Viewmodel is as such
public class PersonAddViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
}
I have tried several ways to get data into the viewmodel and pass it to the view. There will be multiple records returned to display.
My latest method is populating the view model as such:
private AppContexts db = new AppContexts();
public ActionResult ListPeople()
{
var model = new PersonAddViewModel();
var people = db.Persons;
foreach(Person p in people)
{
Address address = db.Addresses.SingleOrDefault(a => a.PersonId == p.Id)
model.Id = p.Id;
model.Name = p.Name;
model.Street = address.Street;
}
return View(model.ToList());
}
I get an error on the Address address = db... line of "EntityCommandExecutionException was unhandled by user code.
How can you populate a view model with multiple records and pass to a view?
Final Solution:
private AppContexts db = new AppContexts();
private AppContexts dbt = new AppContexts();
public ActionResult ListPeople()
{
List<PersonAddViewModel> list = new List<PersonAddViewModel>();
var people = db.Persons;
foreach(Person p in people)
{
PersonAddViewModel model = new PersonAddViewModel();
Address address = dbt.Addresses.SingleOrDefault(a => a.PersonId == p.Id)
model.Id = p.Id;
model.Name = p.Name;
model.Street = address.Street;
}
return View(list);
}
First, EntityCommandExecutionException errors indicates an error in the definition of your entity context, or the entities themselves. This is throwing an exception because it's found the database to be different from the way you told it that it should be. You need to figure out that problem.
Second, regarding the proper way to do this, the code you've shown should work if your context were correctly configured. But, a better way would be to use Navigational properties, so long as you want to get all related records and not specify other Where clause parameters. A navigational property might look like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Gender { get; set; }
public virtual Address Address { get; set; }
// or possibly, if you want more than one address per person
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public int Zip { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
Then you would simply say:
public ActionResult ListPeople()
{
var model = (from p in db.Persons // .Includes("Addresses") here?
select new PersonAddViewModel() {
Id = p.Id,
Name = p.Name,
Street = p.Address.Street,
// or if collection
Street2 = p.Addresses.Select(a => a.Street).FirstOrDefault()
});
return View(model.ToList());
}
For displaying lists of objects, you could use a generic view model that has a generic list:
public class GenericViewModel<T>
{
public List<T> Results { get; set; }
public GenericViewModel()
{
this.Results = new List<T>();
}
}
Have a controller action that returns, say all people from your database:
[HttpGet]
public ActionResult GetAllPeople(GenericViewModel<People> viewModel)
{
var query = (from x in db.People select x); // Select all people
viewModel.Results = query.ToList();
return View("_MyView", viewModel);
}
Then make your view strongly typed, taking in your generic view model:
#model NameSpace.ViewModels.GenericViewModel<NameSpace.Models.People>
I have this line of code in my global.asax
Mapper.CreateMap<Order, OrderDTO>();
These are my classes:
public class Customer
{
public string Name {get; set;}
}
public class Order
{
public int OrderId { get; set; }
public Customer Customer { get; set; }
}
public class OrderDTO
{
public int OrderId { get; set; }
public string Name { get; set; }
}
And this is my code:
Customer cust = new Customer { Name = "Jaggu" };
Order order = new Order { Customer = cust, OrderId = 123 };
OrderDTO dto = Mapper.Map<Order,OrderDTO>(order);
my dto contains OrderId but Name is null. As per documentation it should work:
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
If I change my global.asax's mapping to this:
Mapper.CreateMap<Order, OrderDTO>().ForMember(dest => dest.Name,
mapping => mapping.MapFrom(order => order.Customer.Name));
it works! This make me curious. Is the doc wrong? or am I doing it wrong?
It will work if you follow the standard naming convention:
public class OrderDTO
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
}
Notice that the property is called CustomerName and not Name. When flattening the Order model into a Dto, the Customer.Name goes into CustomerName.