AutoMapper Map nested ICollection - c#

I want to map one call to another, but I got exception all the time /An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code/
Here are my Source Classes:
public class Snippet
{
public Snippet()
{
this.Labels = new HashSet<Label>();
this.Commennts = new HashSet<Comment>();
}
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
[Required]
public string Code { get; set; }
public int LanguageId { get; set; }
public string UserId { get; set; }
public DateTime CreatedOn { get; set; }
public ICollection<Label> Labels { get; set; }
public ICollection<Comment> Commennts { get; set; }
public virtual Language Language { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class Label
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Snippet> Snippets { get; set; }
}
Here are my Destination classes:
public class SnippetModels
{
public class Output
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreatedOn { get; set; }
public string Code { get; set; }
public string LanguageName { get; set; }
public string UserUsername { get; set; }
public IEnumerable<LabelModels.Output> Labels { get; set; }
//TODO Comments Labels
}
}
public class LabelModels
{
public class Output
{
public int Id { get; set; }
public string Name { get; set; }
}
}
public class HomeViewModel
{
public IEnumerable<SnippetModels.Output> Snippets { get; set; }
public IEnumerable<LabelModels.Output> Labels { get; set; }
}
And finally I tied everything that pop up in my mind but no success:
Mapper.CreateMap<Snippy.Models.Label, LabelModels.Output>();
Mapper.CreateMap<IEnumerable<Snippy.Models.Label>, IEnumerable<LabelModels.Output>>();
Mapper.CreateMap<Snippy.Models.Snippet, SnippetModels.Output>()
.ForMember(dest => dest.Labels, opt => opt.MapFrom(src => src.Labels));
Mapper.CreateMap<IEnumerable<Snippy.Models.Snippet>, IEnumerable<SnippetModels.Output>>();
var newestSnippetsDatabase = this.Data.Snippets
.All()
.OrderByDescending(s => s.CreatedOn)
.Take(HomeVisibleItemsCount)
.Select(s => s)
.ToList();
var homeScreenView = new HomeViewModel
{
Snippets = Mapper.Map<IEnumerable<SnippetModels.Output>>(newestSnippetsDatabase)
};

Related

ASP.NET Core Web API : how to format my results correctly using AutoMapper

I am losing my mind trying to coerce data from Entity Framework Core and output the data using an ASP.NET Core Web API where the data will ultimately be consumed by a Blazor WASM app. The data store is a Postgresql database. My partner in crime is requesting the data be formatted specifically so that some child classes/objects are presented in summarized form(id/name) and others with full detail.
The first batch of code is my entity classes. Most of this is autogenerated database-first as I am working with an existing batch of Winforms apps and existing data:
//entity classes
public partial class Appuser
{
public int Appuserid { get; set; }
public string Familyname { get; set; }
public string Givennames { get; set; }
public string Login { get; set; }
public string? Hash { get; set; }
public string? Salt { get; set; }
public int? Oup { get; set; }
public short Active { get; set; }
public string? Email { get; set; }
}
public partial class Part
{
public int Partid { get; set; }
public int Partclassid { get; set; }
public string Partnumber { get; set; }
public string Description { get; set; }
public int Qty { get; set; }
public string? Uom { get; set; }
public string? Customernumber { get; set; }
public decimal? Width { get; set; }
public decimal? Length { get; set; }
public string? Comment { get; set; }
public bool? Pstexempt { get; set; }
public int? Sagecode { get; set; }
public bool? Active { get; set; }
public virtual Partclass Partclass { get; set; }
public virtual Sagecode? SagecodeNavigation { get; set; }
}
public partial class Partclass
{
public int Partclassid { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
public partial class Purchaserequest
{
public int Purchaserequestid { get; set; }
public string Purchaserequestnumber { get; set; }
public int? Vendorid { get; set; }
public int? Enteredby { get; set; }
public DateTime Datecreated { get; set; }
public DateTime? Datesubmitted { get; set; }
public DateTime? Dateclosed { get; set; }
public int? Purchaserequeststatusid { get; set; }
public string? Comments { get; set; }
public string? Purpose { get; set; }
public string? Authcomments { get; set; }
public DateTime? Authactiondate { get; set; }
public int? Authority { get; set; }
public int? Urgencyid { get; set; }
public virtual Appuser? AuthorityNavigation { get; set; }
public virtual Appuser? EnteredbyNavigation { get; set; }
public virtual Purchaserequeststatus? Purchaserequeststatus { get; set; }
public virtual Urgency? Urgency { get; set; }
public virtual Vendor? Vendor { get; set; }
public virtual ICollection<Purchaserequestline> Purchaserequestlines { get; set; }
}
public partial class Purchaserequestline
{
public int Purchaserequestlineid { get; set; }
public int Purchaserequestid { get; set; }
public int? Partid { get; set; }
public string Uom { get; set; } = null!;
public string? Itemdescription { get; set; }
public string? Comments { get; set; }
public decimal? Price { get; set; }
public string? Vendorpartno { get; set; }
public string? Customerpartnumber { get; set; }
public decimal? Qty { get; set; }
public int? Sagecode { get; set; }
public virtual Part? Part { get; set; }
public virtual Sagecode? SagecodeNavigation { get; set; }
}
public partial class Purchaserequeststatus
{
public int Purchaserequeststatusid { get; set; }
public string Description { get; set; } = null!;
}
public partial class Sagecode
{
public int Code { get; set; }
public string Description { get; set; }
public int? Class { get; set; }
public int? Parent { get; set; }
public virtual Sagecodeclass? ClassNavigation { get; set; }
public virtual Sagecode? ParentNavigation { get; set; }
}
public partial class Sagecodeclass
{
public int Classid { get; set; }
public string Classname { get; set; } = null!;
public virtual ICollection<Sagecode> Sagecodes { get; set; }
}
public partial class Urgency
{
public int Urgencyid { get; set; }
public string? Name { get; set; }
}
public partial class Vendor
{
public int Vendorid { get; set; }
public string Vendorname { get; set; } = null!;
public string? Address { get; set; }
public string? Address2 { get; set; }
public string? City { get; set; }
public string? Provincecd { get; set; }
public string? Countrycd { get; set; }
public string? Postalcode { get; set; }
public string? Comments { get; set; }
public short Active { get; set; }
public short? Pstexempt { get; set; }
}
The following are my data transfer objects used to pass my data:
//DTO records
public record AppUserDTO
{
public int Appuserid { get; init; }
public string Familyname { get; init; }
public string Givennames { get; init; }
public string Login { get; init; }
public int? Oup { get; init; }
public short Active { get; init; }
public string? Email { get; init; }
}
public record LookupDTO
{
public int id { get; init; }
public string name { get; init; }
}
public record PartDTO
{
public int Partid { get; init; }
public virtual Partclass Partclass { get; init; }
public string Partnumber { get; init; }
public string Description { get; init; }
public int Qty { get; init; }
public string? Uom { get; init; }
public string? Customernumber { get; init; }
public decimal? Width { get; init; }
public decimal? Length { get; init; }
public virtual Pricemethod? MaterialpricemethodNavigation { get; init; }
public string? Comment { get; init; }
public bool? Pstexempt { get; init; }
public virtual Sagecode? SagecodeNavigation { get; init; }
public bool? Active { get; init; }
}
public record PartclassDTO
{
public int Partclassid { get; init; }
public string Name { get; init; }
public string? Description { get; init; }
}
public record PreqDTO
{
public int Purchaserequestid { get; init; }
public string Purchaserequestnumber { get; init; } = null!;
public virtual LookupDTO? Vendor { get; init; }
public virtual LookupDTO? EnteredbyNavigation { get; init; }
public DateTime Datecreated { get; init; }
public DateTime? Datesubmitted { get; init; }
public DateTime? Dateclosed { get; init; }
public virtual LookupDTO? Purchaserequeststatus { get; init; }
public string? Comments { get; init; }
public string? Purpose { get; init; }
public string? Authcomments { get; init; }
public DateTime? Authactiondate { get; init; }
public virtual LookupDTO? AuthorityNavigation { get; init; }
public virtual LookupDTO? Urgency { get; init; }
public virtual ICollection<PurchaseRequestLineDTO> Purchaserequestlines { get; init; }
public virtual ICollection<Purchaserequesturl> Purchaserequesturls { get; init; }
}
public record PurchaseRequestLineDTO
{
public int Purchaserequestlineid { get; init; }
public int Purchaserequestid { get; init; }
public virtual LookupDTO? Part { get; init; }
public string Uom { get; init; }
public string? Itemdescription { get; init; }
public string? Comments { get; init; }
public decimal? Price { get; init; }
public string? Vendorpartno { get; init; }
public string? Customerpartnumber { get; init; }
public decimal? Qty { get; init; }
public virtual SagecodeDTO? SagecodeNavigation { get; set; }
}
public record PurchaseRequestStatusDTO
{
public int Purchaserequeststatusid { get; init; }
public string Description { get; init; }
}
public record SagecodeDTO
{
public int Code { get; init; }
public string Description { get; init; }
public virtual Sagecodeclass? ClassNavigation { get; init; }
public virtual Sagecode? ParentNavigation { get; init; }
}
public record SagecodeclassDTO
{
public int Classid { get; init; }
public string Classname { get; init; }
}
public record UrgencyDTO
{
public int Urgencyid { get; init; }
public string? Name { get; init; }
}
public record VendorDTO
(
public int Vendorid { get; init; }
public string Vendorname { get; init; }
public string? Address { get; init; }
public string? Address2 { get; init; }
public string? City { get; init; }
public string? Provincecd { get; init; }
public string? Countrycd { get; init; }
public string? Postalcode { get; init; }
public string? Comments { get; init; }
public short Active { get; init; }
public short? Pstexempt { get; init; }
);
A partial listing of my AutoMapper configuration follows:
//AutoMapper Profile
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Part, PartDTO>();
CreateMap<Partclass, PartclassDTO>();
CreateMap<Vendor, VendorDTO>();
CreateMap<Sagecode, SagecodeDTO>();
CreateMap<Sagecodeclass, SagecodeclassDTO>();
CreateMap<Urgency, UrgencyDTO>();
CreateMap<Purchaserequestline, PurchaseRequestLineDTO>();
CreateMap<Purchaserequeststatus, PurchaseRequestStatuysDTO>();
CreateMap<Appuser, AppUserDTO>();
CreateMap<Purchaserequest, PreqDTO>();
CreateMap<Appuser, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Appuserid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Givennames + " " + src.Familyname));
CreateMap<Purchaserequeststatus, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Purchaserequeststatusid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Description));
CreateMap<Urgency, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Urgencyid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Name));
CreateMap<Vendor, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Vendorid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Vendorname));
CreateMap<Part, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Partid))
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Partnumber + " - " + src.Description));
CreateMap<Purchaserequestline, PurchaseRequestLineDTO>();
CreateMap<Purchaserequesturl, PurchaseRequestURLDTO>();
}
}
My partner in crime wants the data for a single purchase request pulled from the PurchaserequestController returning a fairly complete object graph. I am providing this data from my PurchaserequestRepository using:
//the repository does the magic to pull the data required
public async Task<Purchaserequest> GetPurchaseRequestById(int id, bool trackChanges)
{
return await FindByCondition(p => p.Purchaserequestid.Equals(id), trackChanges)
.Include(p => p.Purchaserequestlines)
.ThenInclude(l=> l.SagecodeNavigation)
.Include(p => p.Purchaserequesturls)
.Include(p => p.Vendor)
.Include(p => p.Purchaserequeststatus)
.Include(p => p.Urgency)
.Include(p => p.AuthorityNavigation)
.Include(p => p.EnteredbyNavigation)
.FirstOrDefaultAsync();
}
This is called from the PurchaserequestService:
//The controller uses the following Service call
// my service pulls the entity graph from the repository
// and formats it using the AutoMapper before returning to the caller as json
public async Task<PreqDTO> GetPurchaseRequestByIdAsync(int id, bool trackChanges)
{
try
{
var preq = await _repository.PurchaseRequests.GetPurchaseRequestById(id, trackChanges);
var dto = _mapper.Map<Purchaserequest, PreqDTO>(preq);
return dto;
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong in the {nameof(GetPurchaseRequestByIdAsync)} service method {ex}");
throw;
}
}
The problem with all of this is that it feels like I am spinning my tires furiously for little benefit. I feel I am going to run into a lot of future issues with the AutoMapper mappings with multiple destination mappings for some source entities.
Am I doing this all wrong? Is there a better way to do this? Any ideas are much appreciated.
Sorry for the length of this post ... I am sure many will get tired of scrolling and run away.

How to get all sales from all shops?

There is ICollection<InformationСontent> InformationСontents in Area class. Schema: Area -> Floor -> Place
I'd like to get all InformationContent from all Area in all Floor in all Place. How to do it?
I have only 1 linq query that returns all InformationContent in Area by id:
public async Task<IEnumerable<InformationСontent>> GetAreaInformationContentToList(int areaId)
{
// version 1
var sales = await _unitOfWork.Context.Areas.Include(x => x.Floor)
.ThenInclude(x => x.Place)
.Where(x => x.Id == areaId)
.SelectMany(x => x.InformationСontents)
.Where(x => x.ActiveTo > DateTime.Now)
.ToListAsync();
return sales;
//version 2
//var sales = await _unitOfWork.Context.Areas.FromSqlRaw("").ToListAsync();
}
public class Area
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsClosed { get; set; }
public string WorkingTime { get; set; }
public bool? IsLableShow { get; set; }
public bool? IsLogoShow { get; set; }
public int FloorId { get; set; }
public Floor Floor { get; set; }
public int? AreaCategoryId { get; set; }
public AreaCategory AreaCategory { get; set; }
public int? FileId { get; set; }
public FileModel File { get; set; }
public ICollection<AreaImage> AreaImages { get; set; }
public ICollection<AreaPoint> AreaPoints { get; set; }
public ICollection<InformationСontent> InformationСontents { get; set; }
}
public class Floor : IHaveId
{
public int Id { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public string Name { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
public int? FileId { get; set; }
public FileModel File { get; set; }
public ICollection<AreaPoint> AreaPoints { get; set; }
public ICollection<Area> Areas { get; set; }
}
public class Place : BaseEntity
{
//public int Id { get; set; }
[MaxLength(150)]
public string Name { get; set; }
[MaxLength(150)]
public string Adress { get; set; }
[MaxLength(20)]
public string PhoneNumber { get; set; }
public string Description { get; set; }
public string WebSiteURL { get; set; }
public int? AreaInMeters { get; set; }
public string WorkingTime { get; set; }
public bool? IsLableShow { get; set; }
public bool? IsLogoShow { get; set; }
public bool ClockIsShowing { get; set; }
public bool TickerIsShowing { get; set; }
public string TickerText { get; set; }
public bool WeatherIsShowing { get; set; }
[Required]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public int? FileId { get; set; }
public FileModel File {get;set;}
public ICollection<Floor> Floors { get; set; }
public ICollection<StationMedia> StationMedias { get; set; }
public ICollection<AreaCategoryType> AreaCategoryTypes { get; set; }
}
public class InformationСontent : BaseEntity
{
public string Name { get; set; }
public int AreaId { get; set; }
public Area Area { get; set; }
public Byte[] Image { get; set; }
public string Description { get; set; }
public int FileId { get; set; }
public FileModel File { get; set; }
public DateTime ActiveFrom { get; set; }
public DateTime ActiveTo { get; set; }
}

Mapping multi-level nestingobjects using automapper

I have dto object:
public class SwivelInfoToViewDTO
{
public ResponseValue Value { get; set; }
public List<ResponseDocument> Documents { get; set; }
public string Status { get; set; }
}
public class ResponseValue
{
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
}
public class ResponseDocument
{
public string Language { get; set; }
public int DocumentTypeId { get; set; }
public string DocumentType { get; set; }
}
And i have my sorce class:
public class SwivelInformationResponse
{
public ResponseValue Value { get; set; }
public string Status { get; set; }
}
public class ResponseValue
{
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[JsonProperty("product_code")]
public string ProductCode { get; set; }
public List<ResponseDocument> Documents { get; set; }
}
public class ResponseDocument
{
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("document_type_id")]
public int DocumentTypeId { get; set; }
[JsonProperty("document_type")]
public string DocumentType { get; set; }
}
I use auto-mapper and my profile looks like this:
public MappingProfile()
{
CreateMap<SwivelInformationResponse, SwivelInfoToViewDTO>()
.ForMember(x => x.Value, s => s.MapFrom(src => src.Value))
.ForMember(x => x.Documents, s => s.MapFrom(src => src.Value.Documents));
}
But somehow I get an error:
Error mapping types.
Destination Member:
Value
How do I make bindings correctly?
You forgot to map your ResponseValue and ResponseDocument classes. It's not the same classes so you need to map them as well.
public MappingProfile()
{
CreateMap<SourceNamespace.ResponseValue, DtoNamespace.ResponseValue>();
CreateMap<SourceNamespace.ResponseDocument, DtoNamespace.ResponseDocument>();
}

E.F Core does not return all values When Include another table

public IEnumerable<Parties> GetAll()
{
return database.Parties;
}
Works very well and the output is:
But when I Include another table by foreignkey like this:
public IEnumerable<Parties> GetAll()
{
return database.Parties.Include(i=>i.User);
}
It does not work, it returns first value of the table and nothing else,the output is :
Users.cs :
public partial class Users
{
public Users()
{
Parties = new HashSet<Parties>();
PartyParticipants = new HashSet<PartyParticipants>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Avatar { get; set; }
public string Biography { get; set; }
public string Password { get; set; }
public virtual ICollection<Parties> Parties { get; set; }
public virtual ICollection<PartyParticipants> PartyParticipants { get; set; }
}
Parties.cs :
public partial class Parties
{
public Parties()
{
Image = new HashSet<Image>();
PartyParticipants = new HashSet<PartyParticipants>();
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime PartyDate { get; set; }
public DateTime CreatedDate { get; set; }
public int ParticipantCount { get; set; }
public int MaxParticipant { get; set; }
public string PartySplash { get; set; }
public string ShortDescription { get; set; }
public string Description { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public bool EntranceFree { get; set; }
public int? FreeParticipant { get; set; }
public int? FreeParticipantMax { get; set; }
public int UserId { get; set; }
public virtual Users User { get; set; }
public virtual ICollection<Image> Image { get; set; }
public virtual ICollection<PartyParticipants> PartyParticipants { get; set; }
}
As you can see on the 2nd picture it interrupts at first row of the table.
I have added this answer based on Vidmantas's comment. ReferenceLoopHandling should be ignored like this in startup.cs:
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

Automapper: Error mapping types. Mapping types: IEnumerable`1 -> IEnumerable`1

I am trying to map a model to a view, but I receive the error above when I am trying to display all my elements, since Automapper doesn't recognize the IEnumerable I think. I receive the error when I am trying to map FixedAssets to FixedAssetsView and FixedAssetsView to FixedAssets.
Here are the objects I am trying to map:
FixedAssets
public class FixedAssets : IEntityBase
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<Category> category { get; set; }
public string serialNo { get; set; }
public string provider { get; set;
public DateTime acquisitionDate { get; set; }
public DateTime warrantyEnd { get; set; }
public int inventoryNo { get; set; }
public string allocationStatus { get; set; }
public string owner { get; set; }
public DateTime allocationDate { get; set; }
public string serviceStatus { get; set; }
public string serviceResolution { get; set; }
public FixedAssets()
{
this.category = new HashSet<Category>();
}
}
FixedAssetsView
public class FixedAssetsView
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<CategoryView> category { get; set; }
public string serialNo { get; set; }
public string provider { get; set; }
public DateTime acquisitionDate { get; set; }
public DateTime warrantyEnd { get; set; }
public int inventoryNo { get; set; }
public string allocationStatus { get; set; }
public string owner { get; set; }
public DateTime allocationDate { get; set; }
public string serviceStatus { get; set; }
public string serviceResolution { get; set; }
}
Category
public class Category : IEntityBase
{
public int ID { get; set; }
public string categoryName { get; set; }
public virtual ICollection<FixedAssets> fixedasset { get; set; }
public Category()
{
this.fixedasset = new HashSet<FixedAssets>();
}
}
CategoryView
public class CategoryView
{
public int ID { get; set; }
public string categoryName { get; set; }
public virtual ICollection<FixedAssetsView> fixedasset { get; set; }
}
Automapper configuration
Mapper.Initialize(x =>
{
x.CreateMap<FixedAssets, FixedAssetsView>();
x.CreateMap<FixedAssetsView, FixedAssets>();
x.CreateMap<Category, CategoryView>();
x.CreateMap<CategoryView, Category>();
});
I believe you need a .ForMember in your Mapper initialization.
eg:
Mapper.CreateMap<IEnumerable<Source>, IEnumerable<Target>>()
.ForMember(f => f, mp => mp.MapFrom(
mfrom => mfrom.Select(s => AutoMapper.Mapper.Map(s, new Target())
)
);

Categories

Resources