Getting straight to the point, I've got the following Models:
public abstract class ControlData
{
public DateTime CreatedDate { get; set; }
public int CreatedById { get; set; }
[ForeignKey("CreatedById")]
public Collaborator CreatedBy { get; set; }
public DateTime? UpdatedDate { get; set; }
public int? UpdatedById { get; set; }
[ForeignKey("UpdatedById")]
public Collaborator UpdatedBy { get; set; }
}
[Table("My_Position_Table")]
public class Position : ControlData
{
[Key]
[Column("PositionId")]
public int Id { get; set; }
[Column("PositionStatusId")]
public int StatusId { get; set; }
[ForeignKey("StatusId")]
public PositionStatus Status { get; set; }
public int OpportunityId { get; set; }
[ForeignKey("OpportunityId")]
public Opportunity Opportunity { get; set; }
public int Total { get; set; }
[Column("PositionDurationId")]
public int DurationId { get; set; }
[ForeignKey("DurationId")]
public PositionDuration Duration { get; set; }
public DateTime StartDate { get; set; }
//TODO Agregar las otras propiedades con sus respectivos catalogos
public string PracticeId { get; set; }
[ForeignKey("PracticeId")]
public Practice Practice { get; set; }
public int RoleId { get; set; }
[ForeignKey("RoleId")]
public PersonRole Role { get; set; }
public int PlatformId { get; set; }
[ForeignKey("PlatformId")]
public Platform Platform { get; set; }
public int LevelId { get; set; }
[ForeignKey("LevelId")]
public Level Level { get; set; }
public int EnglishLevelId { get; set; }
[ForeignKey("EnglishLevelId")]
public EnglishLevel EnglishLevel { get; set; }
public string CountryId { get; set; }
public int LocationId { get; set; }
[ForeignKey("LocationId")]
public Location Location { get; set; }
public int? OfficeId { get; set; }
public int OperationId { get; set; }
[ForeignKey("OperationId")]
public Person Operation { get; set; }
public int? EvaluatorId { get; set; }
[ForeignKey("EvaluatorId")]
public Collaborator Evaluator { get; set; }
public int? SourcerId { get; set; }
[ForeignKey("SourcerId")]
public Collaborator Sourcer { get; set; }
public List<Candidate> Candidates { get; set; }
public int? PositionCancellationReasonId { get; set; }
[ForeignKey("PositionCancellationReasonId")]
public PositionCancellationReason CancellationReason { get; set; }
public string CancellationComments { get; set; }
public int? CancellationById { get; set; }
[ForeignKey("CancellationById")]
public Collaborator CancellationBy { get; set; }
public DateTime? CancellationDate { get; set; }
public bool Active { get; set; }
public bool WhereAvailable { get; set; }
public bool RequestAsset { get; set; }
public string CityZone { get; set; }
public string TravelsTo { get; set; }
public string Description { get; set; }
public string SpecificationFile { get; set; }
public int PositionPriorityId { get; set; }
public int? SourcingGroupId { get; set; }
}
[Table("My_Opportunity_Table")]
public class Opportunity : ControlData
{
[Column("OpportunityId")]
[Key]
public int Id { get; set; }
[Column("OpportunityStatusId")]
public int StatusId { get; set; }
[ForeignKey("StatusId")]
public OpportunityStatus Status { get; set; }
public string ProjectId { get; set; }
[ForeignKey("ProjectId")]
public Project Project { get; set; }
public string MarketId { get; set; }
[ForeignKey("MarketId")]
public Market Market { get; set; }
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
public string Name { get; set; }
[Column("OpportunityTypeId")]
public int TypeId { get; set; }
[ForeignKey("TypeId")]
public OpportunityType Type { get; set; }
[Column("OpportunityPriorityId")]
public int PriorityId { get; set; }
[ForeignKey("PriorityId")]
public OpportunityPriority Priority { get; set; }
public int? OpportunityCancellationReasonId { get; set; }
[ForeignKey("OpportunityCancellationReasonId")]
public OpportunityCancellationReason CancellationReason { get; set; }
public string CancellationComments { get; set; }
public int? CancellationById { get; set; }
[ForeignKey("CancellationById")]
public Collaborator CancellationBy { get; set; }
public DateTime? CancellationDate { get; set; }
public bool Active { get; set; }
public List<OpportunityRole> OpportunityRoles { get; set; }
public List<Position> Positions { get; set; }
}
And also, I've got their equivalents in DTO's:
public abstract class ControlDataDTO
{
public DateTime CreatedDate { get; set; }
public int CreatedById { get; set; }
public CollaboratorPlainDTO CreatedBy { get; set; }
public DateTime? UpdatedDate { get; set; }
public int? UpdatedById { get; set; }
public CollaboratorPlainDTO UpdatedBy { get; set; }
}
public class PositionDTO: ControlDataDTO
{
public int Id { get; set; }
public int StatusId { get; set; }
public PositionStatusDTO Status { get; set; }
public int OpportunityId { get; set; }
public OpportunityDTO Opportunity { get; set; }
public int Total { get; set; }
public int DurationId { get; set; }
public PositionDurationDTO Duration { get; set; }
public DateTime StartDate { get; set; }
public string PracticeId { get; set; }
public PracticeDTO Practice { get; set; }
public int RoleId { get; set; }
public PersonRoleDTO Role { get; set; }
public int PlatformId { get; set; }
public PlatformDTO Platform { get; set; }
public int LevelId { get; set; }
public LevelDTO Level { get; set; }
public int EnglishLevelId { get; set; }
public EnglishLevelDTO EnglishLevel { get; set; }
public string CountryId { get; set; }
public int LocationId { get; set; }
public LocationDTO Location { get; set; }
public int? OfficeId { get; set; }
public int OperationId { get; set; }
public PersonDTO Operation { get; set; }
public string OperationIS { get; set; }
public bool WhereAvailable { get; set; }
public bool RequestAsset { get; set; }
public string CityZone { get; set; }
public string TravelsTo { get; set; }
public string Description { get; set; }
public int CandidatesAccepted { get; set; }
public int CandidatesRejected { get; set; }
public int CandidatesWaiting { get; set; }
public bool HasCandidatesWaiting { get; set; }
public int TotalCandidates { get; set; }
public string SpecificationFile { get; set; }
public int? EvaluatorId { get; set; }
public int? SourcerId { get; set; }
public CollaboratorDTO Sourcer { get; set; }
public int? SourcingGroupId { get; set; }
public PositionCancellationReasonDTO CancellationReason { get; set; }
}
public class OpportunityDTO: ControlDataDTO
{
public int Id { get; set; }
public int StatusId { get; set; }
public OpportunityStatusDTO Status { get; set; }
public string ProjectId { get; set; }
public ProjectDTO Project { get; set; }
public string MarketId { get; set; }
public MarketDTO Market { get; set; }
public string CustomerId { get; set; }
public CustomerDTO Customer { get; set; }
public string Name { get; set; }
public int TypeId { get; set; }
public OpportunityTypeDTO Type { get; set; }
public int PriorityId { get; set; }
public OpportunityPriorityDTO Priority { get; set; }
public int? OpportunityCancellationReasonId { get; set; }
public OpportunityCancellationReasonDTO CancellationReason { get; set; }
public string CancellationComments { get; set; }
public int? CancellationById { get; set; }
public CollaboratorPlainDTO CancellationBy { get; set; }
public DateTime? CancellationDate { get; set; }
public CollaboratorDTO Responsible { get; set; }
public List<OpportunityRoleDTO> OpportunityRoles { get; set; }
public int TotalPositions { get; set; }
public bool CandidatesWarning { get; set; }
public bool Active { get; set; }
public List<PositionDTO> Positions { get; set; }
}
For this mapping initialization we are using Profiles, like this way:
public class AutoMapperConfig
{
public static void RegisterMappings()
{
Mapper.Initialize(cfg =>
{
// ...
cfg.AddProfile<OpportunityMappingProfile>();
// ...
});
}
}
public class OpportunityMappingProfile : Profile
{
public OpportunityMappingProfile()
{
CreateMap<Opportunity, OpportunityDTO>()
.ForMember(x => x.Responsible, x => x.MapFrom(c => GetFromOpportunityRoles(c.OpportunityRoles, Constants.OpportunityResponsible)))
.ForMember(x => x.TotalPositions, x => x.MapFrom(c => c.Positions.Count()))
.ForMember(x => x.CandidatesWarning, x => x.MapFrom(c => c.Positions.Count() > 0 ?
c.Positions.Any(pos => pos.Candidates.Any(cand => cand.StatusId == 3)) :
false))
.ForMember(x => x.CreatedBy, x => x.MapFrom(c => Mapper.Map<CollaboratorPlainDTO>(c.CreatedBy)))
.ForMember(x => x.UpdatedBy, x => x.MapFrom(c => Mapper.Map<CollaboratorPlainDTO>(c.UpdatedBy)))
.ForMember(x => x.Positions, x => x.MapFrom(c => Mapper.Map<List<PositionDTO>>(c.Positions))).PreserveReferences(); --> Even using this method, StackOverflow exception is still occurring...
CreateMap<OpportunityDTO, Opportunity>()
.ForMember(x => x.CancellationReason, x => x.Ignore())
.ForMember(x => x.CreatedBy, x => x.Ignore())
.ForMember(x => x.UpdatedBy, x => x.Ignore())
.ForMember(x => x.Positions, x => x.Ignore());
}
private Collaborator GetFromOpportunityRoles(List<OpportunityRole> opportunityRoles, string rol)
{
var opportunityRole = opportunityRoles.FirstOrDefault(opp => opp.ProjectRoleTypeId == rol);
return opportunityRoles != null ? opportunityRole.Collaborator : null;
}
}
And finally, the logic that does the mapping where I'm getting the commented error...
public class OpportunityLogic : IOpportunityLogic
{
// Properties...
public OpportunityLogic(parameters list here, though irrelevant for this example)
{
// ...
}
public ActionResponse<List<OpportunityDTO>> GetOpportunitiesWithPositions(int personId)
{
// Information is retrieved from DB, here...
List<Opportunity> listOpportunities = opportunityRepository.Get(
opp => opp.Status,
opp => opp.Market,
opp => opp.Customer,
opp => opp.Type,
opp => opp.Project,
opp => opp.Status,
opp => opp.Positions,
opp => opp.Positions.Select(pos => pos.Candidates),
opp => opp.Positions.Select(pos => pos.Status),
opp => opp.Positions.Select(pos => pos.Practice),
opp => opp.Positions.Select(pos => pos.Role),
opp => opp.Positions.Select(pos => pos.Platform),
opp => opp.Positions.Select(pos => pos.Sourcer));
// After having retrieved data, here I re-define my model.
listOpportunities = listOpportunities
.Where( opp => opp.StatusId == (int)Constants.OpportunityStatus.Open &&
opp.Active == true &&
opp.Positions.Any(pos => pos.StatusId == (int)Constants.PositionStatus.Open &&
pos.Candidates.Any(can => can.PersonId == personId &&
can.Active == true &&
(can.StatusId == (int)Constants.CandidateStatus.Lead ||
can.StatusId == (int)Constants.CandidateStatus.Waiting))))
.ToList();
// MY PROBLEM IS HERE....
var mappedOpportunities = Mapper.Map<List<OpportunityDTO>>(listOpportunities);
return new ActionResponse<List<OpportunityDTO>> (mappedOpportunities);
}
}
My problem starts when trying to map my Model (List) to the DTO (List); the error is the well-known "StackOverflow Exception". If I'm using the "PreserveReferences()" method, why is still throwing the same exception?. The same goes for "MaxDepth() method", after having tried different depth levels (1,2,3...).
I've spent too many hours trying to solve this issue and to be honest, I'm out of ideas already. If anyone has an idea what to do here, I'll be really grateful.
Thanks and advance & regards!!
Starting from 6.1.0 PreserveReferences is set automatically at config time whenever the recursion can be detected statically. If that doesn't happen in your case, open an issue with a full repro and we'll look into it.
http://automapperdocs.readthedocs.io/en/latest/5.0-Upgrade-Guide.html#circular-references
But you have to remove the Map calls inside the MapFroms. Those ForMember-s are not needed.
The issue here is that I was missing another Mapping Profile that needed to preserve its references, but had not mentioned here, since I was missing that part and that was the one that had causing me all the issues.
How could I map a custom many to many relationship with addition fields? In this case DateTime in Schedule.
Problem now is it creates a new table named StaffSchedules
public class Schedule
{
public int Id { get; set; }
public DateTime DateTime { get; set; }
public List<Staff> Staff { get; set; }
public int StaffId { get; set; }
public Patient Patient { get; set; }
public int PatientId { get; set; }
}
public class Staff
{
public int Id { get; set; }
public string FirstName { get; set; }
public List<Schedule> Schedules { get; set; }
}
...
public DbSet<Schedule> Schedules { get; set; }
public DbSet<Staff> Staff { get; set; }
Desired outcome:
Add a class like so:
public class StaffSchedule
{
public int StaffId { get; set; }
public int ScheduleId { get; set; }
public virtual Staff Staff { get; set; }
public virtual Schedule Schedule { get; set; }
public DateTime Date { get; set; }
}
Modify your Staff and Schedule classes:
public class Schedule
{
public int Id { get; set; }
//Other Fields
public ICollection<StaffSchedule> StaffSchedules { get; set; }
}
public class Staff
{
public int Id { get; set; }
//Other Fields
public ICollection<StaffSchedule> StaffSchedules { get; set; }
}
And lastly, in your context file (OnModelCreating method):
modelBuilder.Entity<Staff>().HasKey(x => x.Id);
modelBuilder.Entity<Schedule>().HasKey(x => x.Id);
modelBuilder.Entity<StaffSchedule>().HasKey(x =>
new
{
x.StaffId,
x.ScheduleId
});
modelBuilder.Entity<StaffSchedule>()
.HasRequired(x => x.Staff)
.WithMany(x => x.StaffSchedules)
.HasForeignKey(x => x.StaffId);
modelBuilder.Entity<StaffSchedule>()
.HasRequired(x => x.Schedule)
.WithMany(x => x.StaffSchedules)
.HasForeignKey(x => x.ScheduleId);
UPDATE:
Looks like you actually wanted this:
public class Patient
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Staff
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Schedule
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int PatientId { get; set; }
public int StaffId { get; set; }
[ForeignKey("PatientId")]
public virtual Patient Patient { get; set; }
[ForeignKey("StaffId")]
public virtual Staff Staff { get; set; }
}
Trying to get Collection from Many to many table with no luck.
currently have 3 tables. I'm not sure how to get to the SkillPool_lookup.skillname for each job.
I currently have.
using(Entities dbCtx = new Entities())
{
this.JobList = dbCtx.Jobs.Where(x => x.JobStatusId == 1)
.Include(x => x.JobStatus_Lookup)
.Include(x => x.Budget_Lookup)
.Include(x => x.JobCategories_Lookup)
.Include(x => x.JobType_Lookup)
.Include(x => x.Job_SubCategories_Lookup)
.Include(x => x.TimeLine_Lookup)
.Include(x => x.RequiredJobSkills)
.OrderByDescending(x => x.DateCreated)
.ToList();
}
but Requirejobskills.skillpool_lookup always null??? stuck. please help.
public Job()
{
this.RequiredJobSkills = new HashSet<RequiredJobSkill>();
this.JobReporteds = new HashSet<JobReported>();
this.JobsEmaileds = new HashSet<JobsEmailed>();
this.JobsSaveds = new HashSet<JobsSaved>();
}
public int Id { get; set; }
public string Description { get; set; }
public string UserId { get; set; }
public int QuoteCount { get; set; }
public int BudgetAmountId { get; set; }
public int MaxBidCount { get; set; }
public int JobCategoryId { get; set; }
public int JobStatusId { get; set; }
public System.DateTime DateCreated { get; set; }
public int JobCategorySubCatId { get; set; }
public int JobTypeId { get; set; }
public string Title { get; set; }
public int QuoteCountryOfOrginId { get; set; }
public Nullable<System.DateTime> DateFilled { get; set; }
public Nullable<System.DateTime> DateCanceled { get; set; }
public Nullable<System.DateTime> DateCompleted { get; set; }
public int TimeLineId { get; set; }
public Nullable<System.Guid> UserIdentifier { get; set; }
public System.Guid JobIdentifier { get; set; }
public Nullable<int> UnseenQuotesCount { get; set; }
public Nullable<int> UnseenMessagesCount { get; set; }
public Budget_Lookup Budget_Lookup { get; set; }
public Job_SubCategories_Lookup Job_SubCategories_Lookup { get; set; }
public JobCategories_Lookup JobCategories_Lookup { get; set; }
public JobStatus_Lookup JobStatus_Lookup { get; set; }
public JobType_Lookup JobType_Lookup { get; set; }
public TimeLine_Lookup TimeLine_Lookup { get; set; }
public ICollection<RequiredJobSkill> RequiredJobSkills { get; set; }
public ICollection<JobReported> JobReporteds { get; set; }
public ICollection<JobsEmailed> JobsEmaileds { get; set; }
public ICollection<JobsSaved> JobsSaveds { get; set; }
}
public partial class RequiredJobSkill
{
public int Id { get; set; }
public int SkillId { get; set; }
public int JobId { get; set; }
public Job Job { get; set; }
public SkillPool_lookup SkillPool_lookup { get; set; }
}
public partial class SkillPool_lookup
{
public SkillPool_lookup()
{
this.UserSkills = new HashSet<UserSkill>();
this.RequiredJobSkills = new HashSet<RequiredJobSkill>();
}
public int Id { get; set; }
public string SkillName { get; set; }
public string Description { get; set; }
public ICollection<UserSkill> UserSkills { get; set; }
public ICollection<RequiredJobSkill> RequiredJobSkills { get; set; }
}
I have this piece of code which performs quite poorly, and delays loading of web pages as a result. The objects underlying the query are not overly complicated and I only have around 6,000 rows in the time entry table, and a small number of rows in the included tables.
How can I re-write it to be more efficient? Why is it so slow? Is it the multiple includes, the lambdas, or the ToList()? When I remove the ToList() it is quite fast, but the code following this query expects a list of time entries, so I would like to find a faster alternative that still returns a list.
var timeEntries = db.TimeEntries
.Include(t => t.Assignment)
.Include(t => t.TimeStatus)
.Include(t => t.User)
.Where(t => t.UserID == strCurrentUserId)
.Where(t => t.TimeDate >= wcDate && t.TimeDate <= sundayDate)
.OrderBy(t => t.TimeDate)
.ToList();
A few of the classes are below (but, I'm not exposing my user class):
public class TimeEntry
{
[Key]
public int ID { get; set; }
public int AssignmentID { get; set; }
public virtual Assignment Assignment { get; set; }
public DateTime TimeDate { get; set; }
public decimal TimeUsed { get; set; }
public int TimeStatusID { get; set; }
public TimeStatus TimeStatus { get; set; }
public string UserID { get; set; }
public virtual User User { get; set; }
[DataType(DataType.MultilineText)]
public string Comment { get; set; }
}
public class TimeStatus
{
[Key]
public int ID { get; set; }
public string Status { get; set; }
}
public class Assignment
{
[Key]
public int ID { get; set; }
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
public string UserID { get; set; }
public virtual User User { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int AssignmentStatusID { get; set; }
public AssignmentStatus AssignmentStatus { get; set; }
public decimal ChargeRate { get; set; }
public int MaxDays { get; set; }
public bool Billable { get; set; }
public bool BillableInternal { get; set; }
[Display(Name = "Assignment No.")]
public string AssignmentNumber
{
get
{
return "A" + ID.ToString("D5");
}
set { }
}
}
I have a one-to-many relationship setup. IT is working fine but now I am trying to use pdfsharp and I need some help with the navigation properties. Every Measurement has 4 pictures. Front, Back, Right, Left. I need to include all 4 pics in the pdf. I am unable to reach them with what i have now. I can return the list of pictures per measurement in a table. Just dont know how to change it for this to work as well.
public class Measurement
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public DateTime? MeasurementDate { get; set; }
public decimal? BustMeasurement { get; set; }
public decimal? ChestMeasurement { get; set; }
public decimal? FAT { get; set; }
public string MeasurementUploadBy { get; set; }
public DateTime? MeasurementUploadDate { get; set; }
public DateTime? MeasurementEditDate { get; set; }
public string MeasurementEditBy { get; set; }
public int? ClientId { get; set; }
[ForeignKey("ClientId")]
public virtual Client Client { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
}
public class Picture
{
public int Id { get; set; }
public string PictureFrontUrl { get; set; }
public string PictureBackUrl { get; set; }
public string PictureRightSideUrl { get; set; }
public string PictureLeftSideUrl { get; set; }
public string PictureNote { get; set; }
public string PictureUploadBy { get; set; }
public DateTime? PictureUploadDate { get; set; }
public DateTime? PictureDate { get; set; }
public string ClientName { get; set; }
public string PictureType { get; set; }
public Guid MeasurementId { get; set; }
[ForeignKey("MeasurementId")]
public virtual Measurement Measurement { get; set; }
}
MeasurementApi
public Measurement GetMeasurement(Guid id)
{
using (var context = new ApplicationDbContext())
{
Measurement model = new Measurement();
model = context.Measurements
.Include(x => x.Pictures)
.FirstOrDefault(j => j.Id == id);
return model;
}
}
PDFSharp GEt Call
apiMeasurementController adapter = new apiMeasurementController();
Measurement model = new Measurement();
model = adapter.GetMeasurement(id);
If you want your Measurement to have 4 pictures, and not a colleciton of pictures you should have something like this:
public class Measurement
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
....
// Won't need these
// public virtual ICollection<Picture> Pictures { get; set; }
public virtual Picture LeftPicture { get; set; }
public virtual Picture TopPicture { get; set; }
public virtual Picture RightPicture { get; set; }
public virtual Picture BottomPicture { get; set; }
}
And your picture:
public class Picture
{
public int Id { get; set; }
// Don't need these 4 any more.
// public string PictureFrontUrl { get; set; }
// public string PictureBackUrl { get; set; }
// public string PictureRightSideUrl { get; set; }
// public string PictureLeftSideUrl { get; set; }
public string PictureUrl { get; set; }
public string PictureNote { get; set; }
public string PictureUploadBy { get; set; }
public DateTime? PictureUploadDate { get; set; }
public DateTime? PictureDate { get; set; }
public string ClientName { get; set; }
public string PictureType { get; set; }
public Guid MeasurementId { get; set; }
[ForeignKey("MeasurementId")]
public virtual Measurement Measurement { get; set; }
}
And in your API
public Measurement GetMeasurement(Guid id)
{
using (var context = new ApplicationDbContext())
{
Measurement model = new Measurement();
model = context.Measurements
.Include(x => x.LeftPicture)
.Include(x => x.TopPicture)
.Include(x => x.RightPicture)
.Include(x => x.BottomPicture)
.FirstOrDefault(j => j.Id == id);
return model;
}
}