What is the EmitMapper equivalent for this AutoMapper configuration? - c#

I have the following class and interface definitions, and would like to use EmitMapper instead of AutoMapper to map from class to interface. The current code works, but I'd like to use EmitMapper, yet have not figured out how to use it.
public interface ITreeViewDTO
{
int Id { get; set; }
string Name { get; set; }
int? Parent_Id { get; set; }
string ExtraData { get; set; }
IEnumerable<ITreeViewDTO> SubNode { get; set; }
}
public class Navigation
{
public int Id { get; set; }
public string Name{ get; set; }
public int? Parent_Id { get; set; }
public virtual IList<Navigation> SubNavigations { get; set; }
}
This is the current AutoMapper configuration for the desired mapping:
Mapper.CreateMap<Navigation, ITreeViewDTO>()
.ForMember(dest => dest.SubNode,
opt => opt.MapFrom<IEnumerable<Navigation>>(src => src.SubNavigations));
var iTreeView = Mapper.Map<Navigation, ITreeViewDTO>(root);
What would be the equivalent EmitMapper code for the above sample?

Related

AutoMapper in C# Navigation Properties

i have a class
public class Order
{
public string Name{ get; set; }
public Nullable<int> StatusId { get; set; }
public virtual Status Status { get; set; }
}
public class Status
{
public int Id { get; set; }
public string Name { get; set; }
}
public class OrderDto
{
public Nullable<int> StatusId { get; set; }
public string Name{ get; set; }
}
public class StatusDto
{
public int Id { get; set; }
public string Name { get; set; }
}
autoMapper:
cfg.CreateMap<Order, OrderDto>().ReverseMap();
cfg.CreateMap<Status, StatusDto>().ReverseMap();
In model classes I was able to do this:
name = order.status.Name
How can i achieve that when using autoMapper
Your OrderDto does not have Status, so you can not get Status name from mapped object.
So either you will create new property in OrderDto class StatusName, and map it to status.name of your model, or add to OrderDto object StatusDto field, like this:
public class OrderDto
{
public string Name{ get; set; }
public Nullable<int> StatusId { get; set; }
public StatusDto Status { get; set; }
}
Then AutoMapper will map models's Status field to OrderDto's Status field.
You can do that, but you need to define it in mapper profile.
CreateMap<Order, OrderDTO>()
.ForMember(dst => dst.name, src => src.MapFrom(x => x.status.Name));

How to flattening child object used repeatedly in C# with Automapper

Using the following entities
public class User
{
public Guid Id { get; set; }
public string Username { get; set; }
}
public class GeneralEntity
{
public Guid Id { get; set; }
public User CreatedByUser { get; set; }
public User DeletedByUser { get; set; }
}
How do I flatten this to the GeneralEntityDto below?
public class GeneralEntityDto
{
public Guid Id { get; set; }
public string CreatedByUsername { get; set; }
public string DeletedByUsername { get; set; }
}
I have tried setting up my mappings as seen below but it fails with a complaint about "CreatedByUsername" and "DeletedByUsername" not being mapped.
protected void Configure()
{
CreateMap<GeneralEntity, GeneralEntityDto>()
.ForMember(dest => dest.CreatedByUsername,
opt => opt.MapFrom(src => src.CreatedByUser.Username))
.ForMember(dest => dest.DeletedByUsername, opt =>
opt.MapFrom(src => src.DeletedByUser.Username));
}
You can use the naming convention that automapper provides.
Basically if you include the exact string of the property name of the source Object you do not have to add ForMember() automapper is clever enough to do it automatically.
That means for example :
public class GeneralEntity
{
public Guid Id { get; set; }
public User CreatedBy { get; set; } // renaming just for simplicity
public User DeletedBy { get; set; } // renaming just for simplicity
}
public class GeneralEntityDto
{
public Guid Id { get; set; }
public string CreatedByUsername { get; set; }
public string DeletedByUsername { get; set; }
}
Reference also to these:
http://docs.automapper.org/en/stable/Flattening.html
AutoMapper TwoWay Mapping with same Property Name

Automapper Finding Not Mapped properties

Using Automapper for a project, just mapping 2 objects to each other, nothing fancy. I must have something configured incorrectly because AutoMapper keeps saying that there are UnMapped Properties.
Here's the AutoMapper config.
var mapperConfig = new MapperConfiguration(cfg => {cfg.CreateMap<SrcObj, DestObj>()
.ForMember(dest => dest.Level, opt => opt.MapFrom(src => src.lvl));}
mapperConfig.AssertConfigurationIsValid();
SrcObj
public class SrcObj
{
public int Id { get; set; }
public int ParentNode { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public string AreaName { get; set; }
public int? DisplayOrder { get; set; }
public Int64 Type{ get; set; }
public int lvl { get; set; }
}
DestObj
public class DestObj
{
public int Id { get; set; }
public int ParentNode { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public string AreaName { get; set; }
public int? DisplayOrder { get; set; }
public Int64 Type{ get; set; }
public int Level { get; set; }
}
And the Implementation:
var items = await _context.Database.SqlQuery<SrcObj>($"EXEC spGenerateMenu {app1}").ToListAsync();
var rslt = _mapper.Map<DestObj>(items);
and the error:
{"\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expressio...}
The error actually lists every member of the DestObj. Not sure what I'm missing. probably something simple
Because your source is a List, you need to map it also to a List:
var rslt = _mapper.Map<List<DestObj>>(items);

Automapper - Unable to map nested objects/collecions

I've tried numerous examples on here and from the automapper wiki and I am still unable to get this issue resolved. I am trying to map a nested object and a nested collection and no matter what I do it always throws an error. The only way I can get the controller to return data is by turning on option.ignore for the two properties.
These are the business layer objects I am trying to map
public class LocationBL
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string Country { get; set; }
public DbGeography Coordinates { get; set; }
public int LocationType_Id { get; set; }
public virtual LocationTypeBL LocationType { get; set; }
public virtual ICollection<SportBL> Sports { get; set; }
}
public class LocationTypeBL
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<LocationBL> Locations { get; set; }
}
public class SportBL
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<LocationBL> Locations { get; set; }
public virtual ICollection<UserBL> Users { get; set; }
}
These are the data layer objects
public class Location : EntityData
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Company")]
public int? CompanyId { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string Country { get; set; }
[Required]
public DbGeography Coordinates { get; set; }
[ForeignKey("LocationType")]
public int LocationType_Id { get; set; }
public virtual LocationType LocationType { get; set; }
public virtual ICollection<Sport> Sports { get; set; }
public virtual Company Company { get; set; }
}
public class LocationType : EntityData
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
public class Sport : EntityData
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
public virtual ICollection<User> Users { get; set; }
}
This is my mapping profile
public class LocationProfile : Profile
{
public LocationProfile()
{
CreateMap<LocationType, LocationTypeBL>();
CreateMap<LocationTypeBL, LocationType>();
CreateMap<Location, LocationBL>()
.ForMember(Dest => Dest.Sports,
opt => opt.MapFrom(src => src.Sports))
.ForMember(Dest => Dest.LocationType,
opt => opt.MapFrom(src => src.LocationType));
CreateMap<LocationBL, Location>()
.ForMember(Dest => Dest.Sports,
opt => opt.MapFrom(src => src.Sports))
.ForMember(Dest => Dest.LocationType,
opt => opt.MapFrom(src => src.LocationType));
}
}
UPDATE *******
This is my LocationType profile
public class LocationTypeProfile : Profile
{
public LocationTypeProfile()
{
CreateMap<LocationType, LocationTypeBL>();
CreateMap<LocationTypeBL, LocationType>();
}
}
This is my Sport profile
public class SportProfile : Profile
{
public SportProfile()
{
CreateMap<Sport, SportBL>();
CreateMap<SportBL, Sport>();
}
}
Not sure if it matters but this is an Azure Mobile App backend using Autofac, WebAPI, and OWIN. This is my first time using AutoMapper and Autofac so please forgive me as I am still learning. The profiles are all registered and if I set the nested objects to ignore, the controller returns the proper data.
Thank you in advance!!!
You are almost there. You need to instruct AutoMapper on how to map the nested objects as well. So you need to create a map for the Sport to SportBL, and vice-versa, also.
// use ForMember if needed, but you know how to do that so I won't
// show it.
CreateMap<Sport, SportBL>();
Then AutoMapper will use that mapping when it mapping nested complex types.
Another note, if your classes have the same properties, you can just call the ReverseMap() method and it will do bidirectional mapping for you.
So instead of this:
CreateMap<LocationType, LocationTypeBL>();
CreateMap<LocationTypeBL, LocationType>();
You can just do this to accomplish the same thing:
Mapper.CreateMap<LocationType, LocationTypeBL>().ReverseMap();

Property of one type map to another type of instance

I am using automapper for mapping view models and entity models with each other, all was good, but now i have a little different scenario where AutoMapper is not able to map my types.
My View Model:
public class CriminalSearchViewModel
{
public CriminalSearchParamsViewModel SearchParameters { get; set; }
public SelectList GenderSelectList { get; set; }
public SelectList NationalitySelectList { get; set; }
public SelectList CrimeSelectList { get; set; }
public SelectList CriminalStatusSelectList { get; set; }
}
second view model:
public class CriminalSearchParamsViewModel
{
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
public int? GenderID { get; set; }
public int? StatusID { get; set; }
public string CNIC { get; set; }
public int? AgeFrom { get; set; }
public int? AgeTo { get; set; }
public double? Height { get; set; }
public int Weight { get; set; }
public int? NationalityID { get; set; }
}
and my Business Model:
public class CriminalSearch
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int? GenderID { get; set; }
public int? StatusID { get; set; }
public string CNIC { get; set; }
public int? AgeFrom { get; set; }
public int? AgeTo { get; set; }
public double? Height { get; set; }
public int Weight { get; set; }
public int? NationalityID { get; set; }
}
I have defined mapping like:
Mapper.CreateMap<CriminalSearch, CriminalSearchParamsViewModel>();
also tried this as well:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearchViewModel>()
.ForMember(dest => dest.SearchParameters, opt =>
opt.MapFrom(src => Mapper.Map<CriminalSearchParamsViewModel, CriminalSearch>(src)));
and in controller i am trying like:
public ActionResult Search(CriminalSearchViewModel searchVM)
{
if (ModelState.IsValid)
{
var searchParams = searchVM.SearchParameters;
var criminalSearch = AutoMapper.Mapper.Map<CriminalSearch>(searchParams);
_criminalService.SearchCriminals(criminalSearch);
}
return View();
}
But it always throws exception:
Missing type map configuration or unsupported mapping.
Mapping types:
CriminalSearchParamsViewModel -> CriminalSearch
NationalCriminals.UI.ViewModels.CriminalSearchParamsViewModel -> NationalCriminals.Core.Models.CriminalSearch
Destination path:
CriminalSearch
Source value:
NationalCriminals.UI.ViewModels.CriminalSearchParamsViewModel
Anybody can pint me what is going wrong?
You just need to change the order of the generic args in the method CreateMap:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearch>()
Thats because the first generic arg is the Source type and the second is the Destination, it is not two way, you must to declare the both if you want to map from a type to another and viceversa like this:
Mapper.CreateMap<CriminalSearchParamsViewModel,CriminalSearch>()
Mapper.CreateMap<CriminalSearch,CriminalSearchParamsViewModel>()
The method CreateMap is described like this:
AutoMapper.Mapper.CreateMap<SourceClass, DestinationClass>();
Suggest: Using AutoMapper: Creating Mappings

Categories