I have something like this:
public class DomainEntity
{
public string Name { get; set; }
public string Street { get; set; }
public IEnumerable<DomainOtherEntity> OtherEntities { get; set; }
public IEnumerable<DomainAnotherEntity> AnotherEntities { get; set; }
}
public class ApiEntity
{
public string Name { get; set; }
public string Street { get; set; }
public int OtherEntitiesCount { get; set; }
}
And following mapper configuration:
Mapper.Configuration.AllowNullCollections = true;
Mapper.CreateMap<DomainEntity, ApiEntity>().
ForSourceMember(e => e.OtherEntities, opt => opt.Ignore()).
ForSourceMember(e => e.AntherEntities, opt => opt.Ignore()).
ForMember(e => e.OtherEntitiesCount, opt => opt.MapFrom(src => src.OtherEntities.Count()));
Mapper.CreateMap<ApiEntity, DomainEntity>().
ForSourceMember(e => e.OtherEntitiesCount, opt => opt.Ignore()).
ForMember(e => e.OtherEntities, opt => opt.Ignore()).
ForMember(e => e.AnotherEntities, opt => opt.Ignore());
To get the ApiEntity from the DomainEntity I'm using var apiEntity = Mapper.Map<DomainEntity, ApiEntity>(myDomainEntity);
To get the merged DomainEntity from an ApiEntity I'm using var domainEntity = Mapper.Map(myApiEntity, myDomainEntity);
But when using this, the properties OtherEntities and AnotherEntities are set to null - even when they had values before calling the mapping from myApiEntity to myDomainEntity. How can I avoid this so they really merge and not just replacing values?
Thanks for any help.
I think you're looking for UseDestinationValue instead of Ignore:
Mapper.CreateMap<ApiEntity, DomainEntity>().
ForSourceMember(e => e.OtherEntitiesCount, opt => opt.UseDestinationValue()).
ForMember(e => e.OtherEntities, opt => opt.UseDestinationValue()).
ForMember(e => e.AnotherEntities, opt => opt.UseDestinationValue());
Related
I wanna map Person to list of Client with AutoMapper:
and this is my models:
public class Person
{
public Guid Id { get; set;}
public string Name { get; set;}
public string Country { get; set;}
public string PhoneNumber { get; set;}
}
public class Member
{
public Guid Id { get; set;}
public string FullName { get; set; }
}
public class Client
{
public Member User { get; set; }
}
I tried to do it with AutoMapper but I couldn't:
CreateMap<Person, List<Client>>();
You need 4 mapping rules:
Map Person to Member.
Map Member to Client.
Map Person to Client.
Map Person to List<Client>.
CreateMap<Person, Member>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
CreateMap<Member, Client>()
.ForMember(dest => dest.User, opt => opt.MapFrom(src => src));
CreateMap<Person, Client>()
.ConvertUsing((src, dest, ctx) => new Client { User = ctx.Mapper.Map<Member>(src) });
CreateMap<Person, List<Client>>()
.ConvertUsing((src, dest, ctx) => new List<Client> { ctx.Mapper.Map<Client>(src) });
Demo # .NET Fiddle
You can map them using the following code:
CreateMap<Person, Client>()
.ForMember(dest => dest.User, opt => opt.MapFrom(src => new Member { Id = src.Id, FullName = src.Name }));
CreateMap<Person, List<Client>>()
.ConvertUsing(src => src.Select(x => new Client { User = new Member { Id = x.Id, FullName = x.Name } }).ToList());
I have a model classes shown below.
public class ModelA
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string DetailId{ get; set; }
public string DetailName { get; set; }
public string DetailDescription { get; set; }
}
public class ModelB
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class ModelC
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ModelB Detail { get; set; }
}
Now, I want to map ModelA to ModelC where
DetailId = Detail.Id
DetailName = Detail.Name
DetailDescription = Detail.Description
Is this possible using Automapper 11.0.0 ?
If possible, how ?
var config = new MapperConfiguration(cfg => {
cfg.CreateMap< ModelA, ModelC >().ForMember(u => u. Detail, o => o.MapFrom(s => s));
});
You want the modelB mapping in modelC,But the structure of modelA is different from that of modelC.
Use this code:
CreateMap<ModelA, ModelC>()
.ForMember(d => d.Detail.Id, src => src.MapFrom(e => e.DetailId))
.ForMember(d => d.Detail.Description, src => src.MapFrom(e => e.DetailDescription))
.ForMember(d => d.Detail.Name, src => src.MapFrom(e => e.DetailName))
.ReverseMap();
Thanks for the answers.
However I solved it already.
MapperConfiguration _config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ModelA, ModelB>()
.ForMember(d => d.DetailId, opt => opt.MapFrom(s => s.Id))
.ForMember(d => d.DetailName , opt => opt.MapFrom(s => s.Name))
.ForMember(d => d.DetailDescription, opt => opt.MapFrom(s => s.Description))
.ReverseMap();
cfg.CreateMap<ModelC, ModelA>()
.ForMember(d => d.DetailId, opt => opt.MapFrom(s => s.Detail.Id ))
.ForMember(d => d.DetailName , opt => opt.MapFrom(s => s.Detail.Name ))
.ForMember(d => d.DetailDescription, opt => opt.MapFrom(s => s.Detail.Description ))
cfg.CreateMap<ModelA, ModelC>()
.ForMember(d => d.Detail, opt => opt.MapFrom(s => Mapper.Map<ModelA, ModelB>(s)))
});
UPDATE
Found a better solution.
Check out the link below.
https://dotnettutorials.net/lesson/mapping-complex-type-to-primitive-type-using-automapper/
i am developing an API which returns records about representatives, everything works fine, i am getting the desired results, the problem starts when i want to sort by a related entity.
I am using Entity Framework to link the tables.
The following is a snippet of the DB diagram related to my piece of work.
I would like to order by Level in Tier table.
The following is my current working code:
var profiles = _context.Profile
.OrderBy(p => p.Person.FirstName)
.Include(p => p.Person)
.Include(p => p.Person.Address)
.Include(p => p.Person.Representative.RepresentativeTierHistory)
.ThenInclude(r => r.Tier)
.Skip(start)
.Take(limit);
var mappedProfiles = _mapper.Map<List<ShortLeaderProfile>>(profiles);
If someone could guide me on how to order the results by Tier.Level i would be really thankful. I have tried the following and it does not work..
Attempt:
.OrderBy(p => p.Person.Representative.RepresentativeTierHistory.OrderByDescending(t => t.Tier.Level))
This is my Mapping Code:
public ProfilesProfile()
{
MapAddressToLeaderProfile();
}
private void MapAddressToLeaderProfile()
{
CreateMap<Models.DataModels.Profile, ShortLeaderProfile>()
.ForMember(lp => lp.Id, opt => opt.MapFrom(p => p.Person.Id))
.ForMember(lp => lp.FirstName, opt => opt.MapFrom(p => p.Person.FirstName))
.ForMember(lp => lp.LastName, opt => opt.MapFrom(p => p.Person.LastName))
.ForMember(lp => lp.PreviousOccupation, opt => opt.MapFrom(p => p.PreviousOccupation))
.ForMember(lp => lp.Code,
opt => opt.MapFrom(p =>
ActiveTier(p.Person.Representative.RepresentativeTierHistory, DateTime.Now.Date)))
.ForMember(lp => lp.location, spt => spt.MapFrom(l => l));
CreateMap<Models.DataModels.Profile, Location>()
.ForMember(lp => lp.Name, opt => opt.MapFrom(p => p.Person.Address.AddressCityOrTown))
.ForMember(lp => lp.Latitude, opt => opt.MapFrom(p => p.Latitude))
.ForMember(lp => lp.Longitude, opt => opt.MapFrom(p => p.Longitude));
}
public static string ActiveTier(IEnumerable<RepresentativeTierHistory> representativeTierHistories, DateTime now) =>
representativeTierHistories?
.SingleOrDefault(x => x.StartDate <= now && x.EndDate > now)?
.Tier?
.Code;
}
This is my Profile Entity Class:
[Table(nameof(Profile), Schema = "common")]
[ExcludeFromCodeCoverage]
public class Profile
{
public int Id { get; set; }
public string PreviousOccupation { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
[Column(TypeName = "numeric(10, 6)")]
public decimal? Longitude { get; set; }
[Column(TypeName = "numeric(10, 6)")]
public decimal? Latitude { get; set; }
public int DisplayOrder { get; set; }
public int PersonId { get; set; }
[ForeignKey(nameof(PersonId))]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDatetime { get; set; }
public List<ProfileSocialMedia> ProfileSocialMedias { get; set; }
public Person Person { get; set; }
My Class Model:
public class ShortLeaderProfile
{
public int Id;
public string FirstName;
public string LastName;
public string PreviousOccupation;
[CanBeNull] public string Code;
[CanBeNull] public Location location;
}
.OrderBy(p => p.Person.Representative.RepresentativeTierHistory.Max(t => t.Tier.Level))
I'm trying to map my object model to a KeyValuePair<char, string> but my results are KeyValuePairs where Key = null and Value = null.
What's the correct way to do this?
Model:
public class Symbol
{
public int Id { get; set; }
public int TemplateId { get; set; }
public Template Template { get; set; }
public char Letter { get; set; }
public string ImgSource { get; set; }
}
Profile:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Symbol, KeyValuePair<object, object>>()
.ForMember(dest => dest.Key, opt => opt.MapFrom(src => src.Letter))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.ImgSource))
.ReverseMap();
}
}
Attempt:
using (_dbContext)
{
var q = await _dbContext.Symbol
.Where(x => x.TemplateId == templateId)
.OrderBy(x => x.Letter)
.Select(x => _mapper.Map<KeyValuePair<char, string>>(x))
.ToListAsync();
//return _mapper.Map<List<KeyValuePair<char, string>>>(q);
return q;
}
Solved by using the following :
CreateMap<Symbol, KeyValuePair<char, string>>()
.ConstructUsing(sym => new KeyValuePair<char, string>(sym.Letter, sym.ImgSource));
I'm making Forum system, I have SubCategoryThreadsViewModel in which I'm trying to map LastComment and Date of last post for every thread. This is my code:
public class SubCategoryThreadsViewModel : IHaveCustomMappings
{
public string Title { get; set; }
public string Description { get; set; }
public IEnumerable<Thread> Threads { get; set; }
public ThreadInfoSubCategoryViewModel ThreadInfoSubCategoryViewModel { get; set; }
public void CreateMappings(IConfiguration configuration)
{
configuration.CreateMap<Thread, SubCategoryThreadsViewModel>()
.ForMember(m => m.Title, opt => opt.MapFrom(t => t.SubCategory.Title))
.ForMember(m => m.Description, opt => opt.MapFrom(t => t.SubCategory.Description))
.ForMember(m => m.Threads, opt => opt.MapFrom(t => t.SubCategory.Threads))
.ForMember(m => m.ThreadInfoSubCategoryViewModel, opt => opt.MapFrom(t => new ThreadInfoSubCategoryViewModel()
{
LastCommentBy = t.Posts.Select(a => a.Author.UserName),
DateOfLastPost = t.Posts.Select(a => a.CreatedOn.ToString()),
}))
.ReverseMap();
}
The code
.ForMember(m => m.ThreadInfoSubCategoryViewModel, opt => opt.MapFrom(t => new ThreadInfoSubCategoryViewModel()
{
LastCommentBy = t.Posts.Select(a => a.Author.UserName),
DateOfLastPost = t.Posts.Select(a => a.CreatedOn.ToString()),
}))
is working but only when property ThreadInfoSubCategoryViewModel is not Ienumerable as in the code above, and inside are two IEnumerable strings.
public class ThreadInfoSubCategoryViewModel
{
public IEnumerable<string> LastCommentBy { get; set; }
public IEnumerable<string> DateOfLastPost { get; set; }
}
This works, but I want ThreadInfoSubCategoryViewModel to be Ienumerable, and in the class properties to be string for easy foreach.
I have tried to make it IEnumerable, but with current automapper code it doesn't work.
You need to manually map the member to an IEnumerable<ThreadInfoSubCategoryViewModel> rather than a single object.
I assume each Post in t.Posts represents one ThreadInfoSubCategoryViewModel, so a simple Select() should do it:
public IEnumerable<ThreadInfoSubCategoryViewModel> ThreadInfoSubCategoryViewModel { get; set; }
...
.ForMember(m => m.ThreadInfoSubCategoryViewModel, opt => opt.MapFrom(t =>
t.Posts.Select(p => new ThreadInfoSubCategoryViewModel()
{
LastCommentBy = p.Author.UserName,
DateOfLastPost = p.CreatedOn.ToString()
})
))