0

I am struggling with the problem from Entity Framework - Include Multiple Levels of Properties

Given these classes:

class Survey {
    public virtual List<QuestionBase> Questions {get;set;}
    ...
}

class QuestionType1 : QuestionBase {
    public List<Answers> Answers {get;set;}
    ...
}
class QuestionType2 : QuestionBase {
    ...
}

Iam trying to get an instance for deep cloning and cannot get the Answers included using:

Survey originalEntity = DBSet
            .Include(s => s.Questions)
            .Include(s => s.Questions.OfType<QuestionType1>().Select(q => q.Answers))
            .AsNoTracking()
            .Single( e => e.Id == sourceId );

using this i get the error 'The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties. Parameter name: path'

Community
  • 1
  • 1
amaters
  • 2,266
  • 2
  • 24
  • 44

2 Answers2

0

Alright, so this took me longer than I thought, but I came up with a solution. However, note that this is not a "full-LINQ" solution and you need an instance of your DB-Context. I hope this is okay for your needs. The key part of the solution is to explicitly load the Answers collection for each instance of QuestionType1.

Survey survey;
int sourceId = 1;

using (var context = new YourDbContext())
{
  survey = context.Surveys.Include(s => s.Questions).Single(s => s.Id == sourceId);
  var questions = survey.Questions.OfType<QuestionType1>();
  foreach (var question in questions)
  {
    context.Entry(question).Collection(q => q.Answers).Load();
  }
}

/* just for testing to check if Questions and Answers are correctly loaded... */
Console.WriteLine(survey);
foreach (var questionBase in survey.Questions)
{
  Console.WriteLine("Question id " + questionBase.Id);
  var questionType1 = questionBase as QuestionType1;
  if (questionType1 != null)
  {
    foreach (var answer in questionType1.Answers)
    {
      Console.WriteLine("Answer " + answer.Id);
    }
  }
}

Remark:

This is a pretty uncommon solution for a pretty uncommon use case. You might want to rethink if you really need exactly that kind of behavior or if you can come up with a more common and simpler solution.

M.E.
  • 2,759
  • 3
  • 24
  • 33
  • This will give a compile error: Cannot implicitly convert type QuestionType1 to Survey – amaters Feb 23 '16 at 18:53
  • thank you for your effort. however when i use this code i get another error: Member 'Load' cannot be called for property 'Answers' because the entity of type 'QuestionType1' does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet. I have to look into this some more, perhaps using a different approach – amaters Feb 23 '16 at 23:08
  • As the error states, you need to add a property IDbSet { get; set; } to your context to make this work. – M.E. Feb 24 '16 at 17:38
  • ... and IDbSet { get; set; } too? – M.E. Feb 24 '16 at 19:54
0

To help others with the same problem I will post my solution here:

Survey cloneSurvey = Context.Surveys
            .Include( s => s.Questions )
            .AsNoTracking()
            .FirstOrDefault( e => e.Id == sourceId );
        Context.Surveys.Add( cloneSurvey );

        IEnumerable<QuestionType1> questions = cloneSurvey.Questions.OfType<QuestionType1>();
        foreach( QuestionType1question in questions )
        {
            IEnumerable<Answer> answers = Context.Answers.AsNoTracking().Where( a => a.Question.Id == question.Id );
            foreach( Answer answer in answers )
            {
                Context.Set<Answer>().Add( answer );
                question.Answers.Add( answer );

            }
        }
        Context.SaveChanges();

Tried a lot of solutions and in the end I ended up looping through the questions and anwsers adding each answer to the Context.Set

amaters
  • 2,266
  • 2
  • 24
  • 44