How to OrderBy by related entity in EntityFramework - c#

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))

Related

How to map this using AutoMapper 11.0.0?

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/

How to map Object to KeyValuePair<char, string> using AutoMapper?

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));

automapper Error mapping types

Everything was fine, until I added DataTime property to my Model and viewModel. All data is correct, but error occurs anyway.
I think the error appears because of I added PublicationDate.
As I understand, Automapper2.0 should work fine with dates,so I dunno why does it happen
Error occurs here:
Publication viewModelPublication = new Publication(Mapper.Map<PublicationViewModel, Publication>(viewModel));
My AutoMapperConfig:
public class AutoMapperConfig
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Author, AuthorViewModel>()
.ForMember(a => a.AuthorId, opt => opt.MapFrom(a => a.AuthorId))
.ForMember(a => a.AuthorName, opt => opt.MapFrom(a => a.AuthorName));
cfg.CreateMap<AuthorViewModel, Author>()
.ForMember(a => a.AuthorId, opt => opt.MapFrom(a => a.AuthorId))
.ForMember(a => a.AuthorName, opt => opt.MapFrom(a => a.AuthorName));
cfg.CreateMap<Publication, PublicationViewModel>()
.ForMember(p => p.PublicationId, opt => opt.MapFrom(p => p.PublicationId))
.ForMember(p => p.PublicationName, opt => opt.MapFrom(p => p.PublicationName))
.ForMember(p => p.Pages, opt => opt.MapFrom(p => p.Pages))
.ForMember(p => p.Publisher, opt => opt.MapFrom(p => p.Publisher))
.ForMember(p => p.PublicationDate, opt => opt.MapFrom(p => p.PublicationDate))
.ForMember(p => p.Subscription, opt => opt.MapFrom(p => p.Subscription))
.ForMember(p => p.Authors, opt => opt.MapFrom(p => p.Authors.ToList()));
cfg.CreateMap<PublicationViewModel, Publication>()
.ForMember(p => p.PublicationId, opt => opt.MapFrom(p => p.PublicationId))
.ForMember(p => p.PublicationName, opt => opt.MapFrom(p => p.PublicationName))
.ForMember(p => p.Pages, opt => opt.MapFrom(p => p.Pages))
.ForMember(p => p.Publisher, opt => opt.MapFrom(p => p.Publisher))
.ForMember(p => p.PublicationDate, opt => opt.MapFrom(p => p.PublicationDate))
.ForMember(p => p.Subscription, opt => opt.MapFrom(p => p.Subscription))
.ForMember(p => p.Authors, opt => opt.MapFrom(p => p.Authors));
});
}
}
My models:
public class PublicationViewModel
{
public PublicationViewModel()
{
Authors = new List<AuthorViewModel>();
}
public int PublicationId { get; set; }
[Display(Name = "Publication Name")]
public string PublicationName { get; set; }
public int Pages { get; set; }
public string Publisher { get; set; }
[Display(Name = "Publication Date")]
public DateTime PublicationDate { get; set; }
public SubscriptionViewModel Subscription { get; set; }
[UIHint("AuthorsEditor")]
public virtual List<AuthorViewModel> Authors { get; set; }
}
public class Publication
{
public Publication()
{
}
public Publication(Publication publication)
{
PublicationId = publication.PublicationId;
PublicationName = publication.PublicationName;
Pages = publication.Pages;
Publisher = publication.Publisher;
Subscription = publication.Subscription;
PublicationDate = publication.PublicationDate;
Authors = publication.Authors;
}
public int PublicationId { get; set; }
public string PublicationName { get; set; }
public int Pages { get; set; }
public string Publisher { get; set; }
public DateTime PublicationDate { get; set; }
public Subscription Subscription { get; set; }
public virtual ICollection<Author> Authors { get; set; }
}
I've found a problem. I forgot to send id from my subscriptionViewModel(Subscription is an enum type).
.ForMember(p => p.Subscription, opt => opt.MapFrom(p => p.Subscription.SubscriptionId))//here

Automapper - How to map to IEnumerable

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()
})
))

AutoMapper settings properties to null on destination object

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());

Categories