map collection of model to viewmodel with automapper - automapper

iwant to map collection of model to viewmodel with automapper
this is my code
public class LanguageViewModel
{
public string Name { get; set; }
public IList<Word> Categories { set; get; }
public IList<string> PictureURL { set; get; }
}
and this my model
public class Word : BaseFieldsTables
{
public string Text { get; set; }
public virtual Category Category { get; set; }
public int CategoryID { get; set; }
public virtual Language Language { get; set; }
public int LanguageID { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
[InverseProperty("MainWord")]
public virtual ICollection<RelationshipBetweenWords> MainWords { get; set; }
[InverseProperty("RelatedWord")]
public virtual ICollection<RelationshipBetweenWords> RelatedWords { get; set; }
}

Fot his example you start with top domain model and map it with inner collections called Nested mappings.
mapper.CreateMap<Word , LanguageViewModel>()
.ForMember(dest => dest.Name, src => src.MapFrom(s => s.Text));
For inner collections within Word domain model, you need to define separate mappings for Categories and Pictures.
mapper.CreateMap<Picture, PictureModel>()
.ForMember(dest => dest.PictureURL , src => src.MapFrom(x => x.Picture.Urltext));
mapper.CreateMap<Category, CategoryModel>()
.ForMember(dest => dest.CategoryId, src => src.MapFrom(x => x.Category.CategoryInt));
and for all remaining inner collections you need to define mapping to make it work

Related

How can I Automap a missmatch between two models with a different number of fields?

I use Visual Studio 2019 and .net core 3.1. I have a couple of related models, CustomerCountries and CustomerRegions in a one-to-many relationship, i.e. 1 CustomerCountries has many CustomersRegions.
In the views of the regions I want to show the name of the countries, not the id, that's why I created a ViewModel:
public class CustomerCountryRegionVM
{
public int IdCustomerRegion { get; set; }
public string CustomerRegion { get; set; }
public int IdCustomerCountry { get; set; }
public string CustomerCountry { get; set; }
}
The issue is that the regions model does not have the CustomerCountry field, it has the foreign primary key IdCustomerCountry. So I don't know how to do the mapping, I've tried it but I get a missmatch. One has 4 fields and the other 3. Do I have to make a discount for the viewmodel? or is there another option?
public class CustomerRegionsDto
{
public int IdCustomerRegion { get; set; }
public string CustomerRegion { get; set; }
[ForeignKey("CustomerCountryId")]
public int IdCustomerCountry { get; set; }
public CustomerCountriesDto CustomerCountryDto { get; set; }
}
public class CustomerRegions
{
[Key]
public int IdCustomerRegion { get; set; }
[StringLength(50, ErrorMessage = "Longitud máxima para la región: 50")]
public string CustomerRegion { get; set; }
[ForeignKey("IdCustomerCountry")]
public int IdCustomerCountry { get; set; }
public CustomerCountries CustomerCountry { get; set; }
}
Automapping class
CreateMap<CustomerRegionsDto, CustomerRegions>();
CreateMap<CustomerRegions, CustomerRegionsDto>();
CreateMap<CustomerCountryRegionVM, CustomerRegions>();
CreateMap<CustomerRegions, CustomerCountryRegionVM>();
CreateMap<CustomerCountryRegionVM, CustomerCountryRegionDto>();
CreateMap<CustomerCountryRegionDto, CustomerCountryRegionVM>();
In the controller:
var model = _mapper.Map<CustomerCountryRegionVM, CustomerRegionsDto>(ccr);
The other model:
public class CustomerCountries
{
public int IdCustomerCountry { get; set; }
[StringLength(50, ErrorMessage = "Longitud máxima para el país: 50")]
public string CustomerCountry { get; set; }
public ICollection<CustomerRegions> CustomerRegions { get; set; }
}
You can map particular property
CreateMap<BusinessService.Model.PermitDashboard, BusinessService.Model.Permit>()
.ForMember(x => x.PermitId, opt => opt.MapFrom(z => z.PermitId))
.ForMember(x => x.PermitTitle, opt => opt.MapFrom(z => z.PermitTitle))
.ForPath(x => x.PermitType.PermitTypeName, opt => opt.MapFrom(z => z.PermitType))
.ForMember(x => x.EffectiveDt, opt => opt.MapFrom(z => z.EffectiveDt))
.ForMember(x => x.ExpirationDt, opt => opt.MapFrom(z => z.ExpirationDt))
.ReverseMap();

How do I get automapper to map enums that are part of a list of child objects

I've got the majority of my automapper maps working but I'm facing a problem trying to translate an enum from the value to the string when part of list of child objects. I have the enum to string converter working when at the top level but it seems when I am converting from RecipeStep to RecipeStepResource it isn't using the map defined for Ingredient to IngredientResource and therefore the conversion from enum to string isn't being called.
I've looked around but can't seem to find a similar example to work from and am having trouble deciphering the automapper help on this which says it should automatically pick up the map defined, which it doesn't seem to be, unsure if this is because the Ingredient items are part of a list. Major code snippets below, any help appreciated.
Model:
public class RecipeStep
{
[Required]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Description { get; set; }
public IList<Ingredient> Ingredients { get; set; }
public Timer Timer { get; set; }
public int RecipeID { get; set; }
[JsonIgnore]
[IgnoreDataMember]
[ForeignKey("RecipeID")]
public Recipe Recipe { get; set; }
}
public class Ingredient
{
[Required]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
public ETypeOfIngredient Type { get; set; }
[Required]
public double Amount { get; set; }
[Required]
public EUnitOfMeasure Unit { get; set; }
public int RecipeStepID { get; set; }
[JsonIgnore]
[IgnoreDataMember]
[ForeignKey("RecipeStepID")]
public RecipeStep RecipeStep { get; set; }
}
Resources:
public class RecipeStepResource
{
public int ID { get; set; }
public string Description { get; set; }
public List<IngredientResource> Ingredients { get; set; }
public TimerResource Timer { get; set; }
}
public class IngredientResource
{
public int ID { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public double Amount { get; set; }
public string Unit { get; set; }
public int RecipeStepID { get; set; }
}
Mapping code:
CreateMap<Ingredient, IngredientResource>()
.ForMember(src => src.Type,
opt => opt.MapFrom(src => src.Type.ToDescriptionString()))
.ForMember(src => src.Unit,
opt => opt.MapFrom(src => src.Unit.ToDescriptionString()));
CreateMap<Timer, TimerResource>();
CreateMap<RecipeStep, RecipeStepResource>()
.ForMember(dest => dest.Ingredients,
opt => opt.MapFrom(src => src.Ingredients))
.ForMember(src => src.Timer,
opt => opt.MapFrom(src => src.Timer));
Enum to string conversion code:
public static string ToDescriptionString<TEnum>(this TEnum #enum)
{
FieldInfo info = #enum.GetType().GetField(#enum.ToString());
var attributes = (DescriptionAttribute[])info.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes?[0].Description ?? #enum.ToString();
}
What I have tried:
Creating an Ingredient -> IngredientResource map
Creating a List<Ingredient> -> List<IngredientResource> map
Adding an AfterMap call to the List<Ingredient> -> List<IngredientResource> map to convert the enum value
None of these have worked. Really struggling to understand why AutoMapper is not picking up the Ingredient to IngredientResource map for a List property on the RecipeStep object, I thought it would have done this automatically.
The issue came down to the parent object, I had it incorrectly mapped with both the model and resource files referring to RecipeStep, instead of RecipeStep -> RecipeStepResource. Really want to thank #Lucian for helping me and making me go back to basics to work through the understanding from a simpler standpoint and building up to a representative model.

How to map related data with AutoMapper

I'm trying to map three entity models into one viewmodel using AutoMapper. The final output should be a recursive category tree with products in the categories. The category tree is working, but the Products-property of the viewmodel is null. My query is returning categories and products, so I'm thinking that the mapping doesn't know how to map the products to the viewmodel.
My entity models:
public class ProductCategory
{
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
// Nav.props:
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; }
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class ProductInCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
public int ProductCategoryId { get; set; }
public bool IsProductCategoryFrontPage { get; set; }
// Nav.props.
public Product Product { get; set; }
public ProductCategory ProductCategory { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
// Nav.prop:
public List<ProductInCategory> InCategories { get; set; }
}
My viewmodel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public string ProductCountInfo
{
get
{
return Products != null && Products.Any() ? Products.Count().ToString() : "0";
}
}
public ViewModelProductCategory ParentCategory { get; set; }
public IEnumerable<ViewModelProductCategory> Children { get; set; }
public IEnumerable<ViewModelProduct> Products { get; set; }
}
I have tried this mapping (maps categories, but no products):
CreateMap<ProductCategory, ViewModelProductCategory>();
CreateMap<ViewModelProductCategory, ProductCategory>();
I have tried this mapping (maps categories, but no products):
CreateMap<ProductCategory, ViewModelProductCategory>()
.ForMember(dto => dto.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dto => dto.ParentId, opt => opt.MapFrom(src => src.ParentId))
.ForMember(dto => dto.Title, opt => opt.MapFrom(src => src.Title))
.ForMember(dto => dto.SortOrder, opt => opt.MapFrom(src => src.SortOrder))
.ForMember(dto => dto.Children, opt => opt.MapFrom(src => src.Children))
.ForMember(dto => dto.Products, opt => opt.MapFrom(src => src.ProductInCategory));
Looks like you want to skip the junction (link) entity in the view model.
To do that, first create a mapping from Product to ViewModelProduct and then define a mapping from ProductCategory.ProductInCategory to ViewModelProductCategory.Products using projection converting List<ProductInCategory> to IEnumerable<Product>. AutoMapper will take care converting IEnumerable<Product> to List<ViewModelProduct> the same way as if you were querying Products:
CreateMap<Product, ViewModelProduct>();
CreateMap<ProductCategory, ViewModelProductCategory>()
.ForMember(dst => dst.Products, opt => opt.MapFrom(
src => src.ProductInCategory.Select(pc => pc.Product)));

how do i use AutoMapper in ICollation<> Fields

when i use AutoMapper for mapping my ViewModels and get All News, thrown error for me.
Errors...
The following property on Mosque.Core.ViewModels.CategoryViewModel cannot be mapped:
Categories
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type Mosque.Core.ViewModels.CategoryViewModel.
please help me, thank you
//Models
public class News
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual User User { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<News> News { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<News> News { get; set; }
}
//ViewModels
public class NewsViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<CategoryViewModel> Categories { get; set; }
public virtual UserViewModel User { get; set; }
}
public class CategoryViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
how do i use for select All News?
--Update1--
I used onion architecture in the project and i installed AutoMapper in the Service layer and i want get all news from repository and fill into ViewModels and pass to the UI.
my code in service layer is...
public List<NewsViewModel> GetAll()
{
Mapper.CreateMap<News, NewsViewModel>()
.ForMember(dest => dest.Categories, src => src.MapFrom(p => p.Categories))
.ForMember(dest => dest.User, src => src.MapFrom(p => p.User));
Mapper.AssertConfigurationIsValid();
var viewModels = new List<NewsViewModel>();
foreach (var item in _newsRepository.GetAll())
{
var viewModel = Mapper.Map<News, NewsViewModel>(item);
viewModels.Add(viewModel);
}
return viewModels;
}
You don't seem to have created maps for Catagory and User.
Add the following maps:
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<Category, CategoryViewModel>();
By the way, why are you creating the maps inside the GetAll method? You can create the maps once, usually at application startup.

AutoMapper how to flatten nested lists

I am trying to map out some nested lists into a list of flattened structures using automapper.
Currently I am able to map the correct number of credits with names but I can not get the role and role description to map across.
I am trying to get a flattened list like this.
John Smith, Actor, Dr Evil
John Smith, Writer, null
Frank Brown, Director, null
Joe Green, Actor, Henchman
Here is a cutdown version of my domain model, view model and mapping.
What am I doing wrong with the Domain.Credit to ViewModel.Credit mapping?
//Domain Model
public class Talent
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<Credit> Credits { get; set; }
}
public class Credit
{
public virtual Role Role { get; set; }
public virtual string Subtitle { get; set; }
}
public class Role
{
public virtual string Title { get; set; }
}
//View Model
public class EpisodeViewModel
{
public IList<Credit> Credits { get; set; }
public class Credit
{
public string Name { get; set; }
public string Role { get; set; }
public string RoleDescription { get; set; }
}
}
//Mappings
Mapper.CreateMap<Episode, EpisodeViewModel>()
.ForMember(destination => destination.Credits, options => options.MapFrom(source => source.Talent));
Mapper.CreateMap<Talent, Credit>()
.ForMember(destination => destination.Name, options => options.MapFrom(source => string.Format("{0} {1}", source.FirstName, source.LastName)));
Mapper.CreateMap<Domain.Credit, Credit>()
.ForMember(destination => destination.Role, options => options.MapFrom(source => source.Role.Title))
.ForMember(destination => destination.RoleDescription, options => options.MapFrom(source => source.Subtitle));

Resources