EF : no mapping specified for properties

Viewed 52

I have two tables MeetingPollingQuestion and MeetingPollingParts. Meeting polling question pk MeetingPollingQuestionId is a foreign key to meeting polling parts. I would like to insert the MeetingPollingQuestion and then use the PK to then insert rows into MeetingPollingParts. I am getting this error about "No mapping specified for properties" and I am not sure what that means.

enter image description here

My code:

   mf.MeetingPollingId = mpq.MeetingPollingId;
   mf.MeetingPollingQuestionType = mpq.MeetingPollingQuestionType;
   mf.SequenceOrder = mpq.SequenceOrder;
    
   db.MeetingPollingQuestions.Add(mf);
    
   var MeetingPollingPartList = new List<EFModel.MeetingPollingPart>();
    
   foreach (var p in mpq.MeetingPollingParts)
   {
       var mfParts = new EFModel.MeetingPollingPart();
       mfParts.MeetingPollingQuestionId = mf.MeetingPollingQuestionId;
       mfParts.Type = p.Type;
       MeetingPollingPartList.Add(mfParts);
   }

   db.MeetingPollingParts.AddRange(MeetingPollingPartList);
    
   db.SaveChanges();
   dbTran.Commit();

This is the error I get:

Problem in mapping fragments starting at line 4481:No mapping specified for properties MeetingPollingPart.MeetingPollingQuestionMeetingPollingQuestionId, MeetingPollingPart.MeetingPollingQuestionMeetingPollingQuestionId1 in Set MeetingPollingParts.
An entity with key (PK) will not round-trip

Updated with Include getting error MeetingPollings not include MeetingPollingParts

var meetingPolling = db.MeetingPollings
                     .Include(x => x.MeetingPollingQuestions)
                     .Include(x => x.MeetingPollingParts)
                     .Single(x => x.MeetingPollingId = =mpq.MeetingPollingId);

EFModel

namespace Repository.EFModel
{
    using System;
    using System.Collections.Generic;
    
    public partial class MeetingPolling
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public MeetingPolling()
        {
            this.MeetingPollingQuestions = new HashSet<MeetingPollingQuestion>();
        }
    
        public int MeetingPollingId { get; set; }
        public Nullable<int> MeetingId { get; set; }
        public Nullable<System.DateTime> StartDate { get; set; }
        public Nullable<System.DateTime> EndDate { get; set; }
    
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<MeetingPollingQuestion> MeetingPollingQuestions { get; set; }
    }
}

    namespace Repository.EFModel
    {
        using System;
        using System.Collections.Generic;
        
        public partial class MeetingPollingQuestion
        {
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
            public MeetingPollingQuestion()
            {
                this.MeetingPollingParts = new HashSet<MeetingPollingPart>();
            }
        
            public int MeetingPollingQuestionId { get; set; }
            public string MeetingPollingQuestionType { get; set; }
            public Nullable<int> MeetingPollingId { get; set; }
            public Nullable<int> SequenceOrder { get; set; }
            public int MeetingPollingMeetingPollingId { get; set; }
        
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
            public virtual ICollection<MeetingPollingPart> MeetingPollingParts { get; set; }
            public virtual MeetingPolling MeetingPolling { get; set; }
        }
    }


namespace Repository.EFModel
{
    using System;
    using System.Collections.Generic;
    
    public partial class MeetingPollingPart
    {
        public int MeetingPollingParts { get; set; }
        public string Type { get; set; }
        public Nullable<int> MeetingPollingQuestionId { get; set; }
        public int MeetingPollingQuestionMeetingPollingQuestionId { get; set; }
    
        public virtual MeetingPollingQuestion MeetingPollingQuestion { get; set; }
    }
}

UnitTest

MeetingPollingQuestion mpq = new MeetingPollingQuestion();
            mpq.MeetingPollingId = 22;
            mpq.MeetingPollingQuestionType = "MultipleChoice";
            mpq.SequenceOrder = 1;
            mpq.MeetingPollingParts = new List<MeetingPollingParts>();

            MeetingPollingParts mpp1 = new MeetingPollingParts();
            mpp1.Type = "Image";
            mpp1.MeetingPollingPartsValues = new List<MeetingPollingPartsValues>();

            MeetingPollingPartsValues mppv1 = new MeetingPollingPartsValues();
            mppv1.FileManagerId = 500;
            mpp1.MeetingPollingPartsValues.Add(mppv1);

            mpq.MeetingPollingParts.Add(mpp1);

            MeetingService ms = new MeetingService();
            var r = ms.SaveMeetingPollingQuestion(mpq);
1 Answers

Use navigation properties to insert related entities.

The issue with your approach is that when EF is set up to use identity columns you won't have a PK to use as a FK until after you call SaveChanges on a DbContext. If you use tracked navigation properties then EF will automatically ensure that the entities are inserted in the correct order and associate the FKs for newly inserted parent records.

For instance, a MeetingPolling should have a collection of MeetingPollingQuestions, and a MeetingPollingQuestion would have a collection of MeetingPollingParts.

I.e.

public class MeetingPolling
{
    // ID, and various fields...

    public virtual ICollection<MeetingPollingQuestion> MeetingPollingQuestions { get; protected set; } = new List<MeetingPollingQuestion>();
}

public class MeetingPollingQuestion
{
    // ID, and various fields....

    public virtual ICollection<MeetingPollingPart> MeetingPollingParts { get; protected set; } = new List<MeetingPollingPart>();
}

The next important detail when updating an existing MeetingPolling to add/update questions and parts is to ensure that these collections are eager loaded so they can be modified:

var meetingPolling = db.MeetingPollings
    .Include(x => x.MeetingPollingQuestions)
        .ThenInclude(x => x.MeetingPollingParts)
    .Single(x => x.Id == meetingPollingId);

Edit: The above applies to EF Core. If you are using EF 6 (for .Net Framework) then:

var meetingPolling = db.MeetingPollings
    .Include(x => x.MeetingPollingQuestions)
    .Include(x => x.MeetingPollingQuestions.Select(q => q.MeetingPollingParts))
    .Single(x => x.Id == meetingPollingId);

To eager load the children and grandchildren collections.

Now when you go to create your question, you populate a new MeetingPollingQuestion and fill in the details much like you are now, except rather than adding it to the DbContext, you add it to the MeetingPolling's questions collection that you had loaded:

meetingPolling.MeetingPollingQuestions.Add(newMeetingPollingQuestion);

The same goes for populating the MeetingPollingParts. Create a new entity populating the details, but append those to the new MeetingPollingQuestion.

The ends result would look more like this:

var meetingPolling = db.MeetingPollings
    .Include(x => x.MeetingPollingQuestions)
        .ThenInclude(x => x.MeetingPollingParts)
    .Single(x => x.Id == meetingPollingId);

var newMeetingPollingQuestion = new MeetingPollingQuestion
{
    MeetingPollingQuestionType = mpq.MeetingPollingQuestionType,
    SequenceOrder = mpq.SequenceOrder,
    // ...
    MeetingPollingParts = mpq.MeetingPollingParts.Select(p => new MeetingPollingPart
    {
        Type = p.Type,
        // ...
    }).ToList()
};

meetingPolling.MeetingPollingQuestions.Add(newMeetingPollingQuestion);
db.SaveChanges();

EF will take care of all of the FK assignments for new records automatically.

Related