I have two classes below:
public class Module
{
public int Id { get; set; }
public string Name { get; set; }
public string ImageName { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
public class ModuleUI
{
public int Id { get; set; }
public string Text { get; set; }
public string ImagePath { get; set; }
public List<PageUI> PageUIs { get; set; }
}
The mapping must be like this:
Id -> Id
Name -> Text
ImageName -> ImagePath
Pages -> PageUIs
How can I do this using Automapper?
You can use ForMember and MapFrom (documentation).
Your Mapper configuration could be:
Mapper.CreateMap<Module, ModuleUI>()
.ForMember(s => s.Text, c => c.MapFrom(m => m.Name))
.ForMember(s => s.ImagePath, c => c.MapFrom(m => m.ImageName))
.ForMember(s => s.PageUIs, c => c.MapFrom(m => m.Pages));
Mapper.CreateMap<Page, PageUI>();
Usage:
var dest = Mapper.Map<ModuleUI>(
new Module
{
Name = "sds",
Id = 2,
ImageName = "sds",
Pages = new List<Page>
{
new Page(),
new Page()
}
});
Result:
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 2 objects which need to be converted to Dto object using automapper.
Those 2 objects are
public class Parent
{
public int ParentID { get; set;}
public string ParentCode { get; set; }
}
public class Child
{
public int ChildID { get; set; }
public int ParentID { get; set;}
public string ChildCode { get; set; }
}
And 2 Dto objects
public class ParentDto
{
public int ParentID { get; set; }
public string ParentCode { get; set; }
public List<ChildDTO> ListChild { get; set; }
}
public class ChildDto
{
public int ChildID { get; set; }
public string ChildCode { get; set; }
}
Need to convert 2 set of list
List<Parent> ListParent
List<Child> ListChild
To
List<ParentDto> ListParentDto
Because you have multiple sources, my solution was a method like
public static List<ParentDto> MapToDto(List<Parent> parents, List<Child> childs)
{
List<ParentDto> parentDtos = new List<ParentDto>();
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Parent, ParentDto>().BeforeMap((s, d) =>
{
d.ListChild = new List<ChildDto>();
foreach (var child in childs.Where(c => c.ParentID == s.ParentID))
{
d.ListChild.Add(new ChildDto { ChildCode = child.ChildCode, ChildID = child.ChildID });
}
}).ForMember(dest => dest.ParentID, opt => opt.MapFrom(src => src.ParentID)).ForMember(dest => dest.ParentCode, opt => opt.MapFrom(src => src.ParentCode));
});
IMapper mapper = config.CreateMapper();
foreach (var p in parents)
{
var source = p;
var destination = mapper.Map<Parent, ParentDto>(source);
parentDtos.Add(destination);
}
return parentDtos;
}
I need to populate a Product object which contains two collections.
The current code works fine and populates the Product.GraphicItems collection, but I also need to populate the Product.InfoItems collection, but I can't figure out the syntax for multiple collections.
Current select:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
Product.cs:
[Table("ShopProduct")]
public class Product : BaseShop
{
public bool Active { get; set; } = true;
public int CategoryId { get; set; }
public int CultureId { get; set; } = -1;
public List<ProductInfo> InfoItems { get; set; } = new List<ProductInfo>();
public List<ProductGraphic> GraphicItems { get; set; } = new List<ProductGraphic>();
}
ProductInfo.cs:
[Table("ShopProductInfo")]
public class ProductInfo : BaseShop, IInfoItem
{
public int? ProductId { get; set; }
public int CultureId { get; set; }
public Culture Culture { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Solution:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
I know that Automapper can automatically map from:
class SomeClassA
{
public int Id { get; set; }
public B Member { get; set; }
}
class SomeClassB
{
public int Id { get; set; }
public string Name { get; set; }
}
to:
class SomeClassADto
{
public int Id { get; set; }
public int Member_Id { get; set; }
public string Member_Name { get; set; }
}
But how can I make Automapper map from my SomeClassADto to SomeClassA automatically?
No the prettiest thing in the world, but it does work. Downside is maintenance is a pain as you add properties to the DTO and class you need to update the mapping.
var config = new MapperConfiguration(x =>
{
x.CreateMap<SomeClassADto, SomeClassB>()
.ForMember(i => i.Id, i => i.MapFrom(src => src.Member_Id))
.ForMember(i => i.Name, i => i.MapFrom(src => src.Member_Name));
x.CreateMap<SomeClassADto, SomeClassA>()
.AfterMap((s, d, r) => d.Member = r.Mapper.Map<SomeClassB>(s));
});
IMapper mapper = config.CreateMapper();
var foo = mapper.Map<SomeClassA>(new SomeClassADto() { Id = 1, Member_Id = 2, Member_Name = "Name" });
I have the following code:
context.Posts
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types
})
).ToList();
This is working but one Post has Many PostLocalized.
I would like to, in my query, pick the PostLocalized which .Culture == culture.
And I need to use its data to create the PostModel. something like:
context.Posts
// PICK the first PostLocalized which .Culture property equals culture
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types,
//Title = PostLocalized.Title,
//Body = PostLocalized.Body
})
).ToList();
How can I do this?
NOTE:
The Post and PostLocalized entities are the following:
public class Post {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public PostTypes Types { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Post
public class PostLocalized {
public Int32 Id { get; set; }
public String Culture { get; set; }
public String Body { get; set; }
public String Title { get; set; }
public virtual Post Post { get; set; }
public virtual ICollection<Pack> Packs { get; set; }
} // PostLocalized
public class Pack {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public DataType Type { get; set; }
public DateTime Updated { get; set; }
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Pack
public class File {
public Int32 Id { get; set; }
public Byte[] Data { get; set; }
public Guid Key { get; set; }
public String Mime { get; set; }
public virtual Pack Pack { get; set; }
} // File
Thank You,
Miguel
This is not exactly beautiful or efficient on its own but it should at least work and the query optimizer will hopefully make it fast.
context.Posts
.SelectMany(post => post.Packs
.SelectMany(pack => pack.Files
.Select(file => new PostModel
{
Id = post.Id,
File = file.Key,
Types = post.Types,
Title = post.PostsLocalized.First(pl => pl.Culture == culture).Title,
Body = post.PostsLocalized.First(pl => pl.Culture == culture).Body
})))
.ToList();