Automapper ReverseMap not automatically mapping interfaces - automapper

I have created a simple test case to reproduce my problem.
Given the following two interfaces
public interface IOne
{
int Id { get; }
string PropOne { get; }
string PropTwo { get; }
string PropThree { get; }
}
public interface ITwo
{
int Id { get; }
string PropOne { get; }
string PropTwo { get; }
string PropThree { get; }
}
And the following mapping code and test
Mapper.CreateMap<IOne, ITwo>()
.ReverseMap()
;
Mapper.AssertConfigurationIsValid();
I get this exception.
AutoMapper.AutoMapperConfigurationException:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
===============================================================================================================================================================================================================
ITwo -> IOne (Source member list)
Vertafore.Services.Producer.DomainModels.ApplicationService.Test.Map.DomainAutoMapperProfileTest+ITwo -> Vertafore.Services.Producer.DomainModels.ApplicationService.Test.Map.DomainAutoMapperProfileTest+IOne (Source member list)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Id
PropOne
PropTwo
PropThree
Am i missing something obvious? It is my understanding that CreateMap will automatically create mappings for your properties as long as they are named the same.
Does the automatic mapping not work when mapping from an interface to an interface?

AutoMapper needs setters to do the mapping. Adding these to the interfaces helps:
public interface IOne
{
int Id { get; set; }
string PropOne { get; set; }
string PropTwo { get; set; }
string PropThree { get; set; }
}
public interface ITwo
{
int Id { get; set; }
string PropOne { get; set; }
string PropTwo { get; set; }
string PropThree { get; set; }
}
I hope this helps.

Related

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.

Autmapper nested mapping

I have the following main class:
public class ResearchOutcome
{
public ResearchOutcomeCategory ResearchOutcomeCategory { get; set; }
public string? UniqueIdentifier { get; set; }
}
And the category class is:
public class ResearchOutcomeCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
The View models for above classes are:
public class ResearchOutcomeDetailVm : IMapFrom<ResearchOutcome>
{
public int Id { get; set; }
public virtual ResearchOutcomeCategoryDetailVm ResearchOutcomeCategory { get; set; }
}
public class ResearchOutcomeCategoryDetailVm : IMapFrom<ResearchOutcomeCategory>
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Now, I have used the following mapping profile:
// First this one
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>();
//Then I tried this one
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>()
.ForMember(o => o.ResearchOutcomeCategory,
cat => cat.MapFrom( o => o.ResearchOutcomeCategory));
But the ResearchOutcomeCategory is always null. Any help would be appreciated.
After digging more, I identified that I was not "Including" the relevant item in the query, hence, the view model was always empty. Pretty dumb on my part :D
Regarding the mapping, if the properties (even complex ones) have the same names, then the mapper will map them automatically. So simply this line worked
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
Hope it helps someone

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 one to many relation

I'm starting to use AutoMapper for my project.
For this I want to do the following 'one-to-many' mapping:
Source:
public class Team
{
int Id { get; set; }
string TeamName { get; set; }
List<Person> Member { get; set; }
}
public class Person
{
int Id { get; set; }
string Name { get; set; }
}
Destination:
public class TeamDetailsViewModel
{
int Id { get; set; }
string TeamName { get; set; }
List<int> MemberIds { get; set; }
}
How to proceed with AutoMapper? Is this possible?
Thanks a lot in advance.
This map should work for you:
CreateMap<Team, TeamDetailsViewModel>()
.ForMember(d=>d.MemberIds, o=>o.MapFrom(s=>s.Member.Select(m=>m.Id)));
FYI...If you are getting the Team from a db, make sure you are eager loading the Member list.

automapper class and nested class map to one class

i have written alot of description but i figured making a picture will make my problem clearer than words
i have written this to map but it throws an exception
Mapper.CreateMap<GenericStory, GenericStoryDisplayViewModel>().ForMember(
gs => gs.StoryBody,dest => dest.MapFrom( gs => gs));
Trying to map StoryWriting.Web.Models.GenericStory to StoryWriting.Web.ViewModels.StoryBodyViewModel.
Using mapping configuration for StoryWriting.Web.Models.GenericStory to StoryWriting.Web.ViewModels.GenericStoryDisplayViewModel
Destination property: StoryBody
Missing type map configuration or unsupported mapping.
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
I thought with AutoMapper you had to map sub-types as well, regardless of if they were contained in another mapped type?
So in this case you'd add
Mapper.CreateMap<GenericStory, StoryBodyViewlModel>();
and then your current mapping.
EDIT:
I've updated my test case to even match your images and it's functioning as expected:
public class GenericStory
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsFavoritedByCurrentUser { get; set; }
public int StoryTypeId { get; set; }
public string StoryTypeName { get; set; }
public string Html { get; set; }
public string Title { get; set; }
public int TotalFavoritedByUsers { get; set; }
}
public class GenericStoryDisplayViewModel
{
public string Description { get; set; }
public int Id { get; set; }
public int StoryTypeId { get; set; }
public string StoryTypeName { get; set; }
public StoryBodyViewModel StoryBody { get; set; }
}
public class StoryBodyViewModel
{
public string Title { get; set; }
public string Html { get; set; }
public int TotalFavoritedByUsers { get; set; }
public bool IsFavoritedByCurrentUser { get; set; }
}
and then my test
private static void Main()
{
var story = new GenericStory
{
Description = "Lorem ipsum dolor sit amet,....etc",
Html = "<h1>ZOMG!</hl>\r\n\r\n<h2>BEES!</h2>",
Id = 9,
IsFavoritedByCurrentUser = true,
StoryTypeId = 1,
StoryTypeName = "ShortStory",
Title = "Test Story",
TotalFavoritedByUsers = 1
};
var vm = new GenericStoryDisplayViewModel();
Mapper.CreateMap<GenericStory, StoryBodyViewModel>();
Mapper.CreateMap<GenericStory, GenericStoryDisplayViewModel>()
.ForMember(dest => dest.StoryBody, opt => opt.MapFrom(src => src));
Mapper.Map(story, vm);
Console.ReadKey();
}
Results:
You can use reverse mapping for configure unflattening. Look at the official doc

Resources