Как отобразить рекурсивные вложенные объекты с помощью ExpressMapper - PullRequest
1 голос
/ 13 марта 2019

Я работаю над веб-приложением (сначала код EF6), которое позволяет пользователям заполнять оценки. Оценка содержит несколько вопросов, вопрос содержит несколько вопросов. Каждый подзапрос имеет «функцию отображения», которая позволяет пользователю связать подзапрос с другим существующим подзапросом.

У меня есть следующие модели Entity Framework (я удалил некоторые свойства, так как они не нужны для моего примера)

public class Question
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<SubQuestion> SubQuestions { get; set; }
}
public class SubQuestion
{
    public int ID { get; set; }
    public int QuestionID { get; set; }
    public virtual Question Question { get; set; }

    [Required]
    [MaxLength(255)]
    public string Name { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }

    //These 2 properties are used to create a many-to-many table for the mapping of subquestions
    //Automatic name = SubQuestion_ID
    //Subquestion of current evaluation to map to another evaluation
    public virtual ICollection<SubQuestion> SubquestionCurrentMapping { get; set; }

    //Automatic name = SubQuestion_ID1
    //Subquestion of evaluation the current evaluation is mapped to
    public virtual ICollection<SubQuestion> SubquestionPreviousMapping { get; set; }

}

В проекте мы используем следующие объекты DTO

public class QuestionVM
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<SubQuestionVM> SubQuestions { get; set; } = new List<SubQuestionVM>();
}

public class SubQuestionVM
{
    public int ID { get; set; }
    public int QuestionID { get; set; }
    public string Name { get; set; }
    public List<AnswerVM> Answers { get; set; }

    public List<SubQuestionVM> SubquestionCurrentMapping { get; set; }
}

Мы используем ExpressMapper (см. http://expressmapper.org). У нас есть метод, который выполняет сопоставление всех DTO с моделями EF, который выглядит следующим образом:

public void MappingRegistration()
{
    Mapper.Register<Question, QuestionVM>();
    Mapper.Register<SubQuestion, SubQuestionVM>();
    Mapper.Compile();
}

Все было сопоставлено и работало нормально, пока я не добавил следующее свойство в подзапрос VM:

public List<SubQuestionVM> SubquestionCurrentMapping { get; set; }

Это свойство создает таблицу «многие ко многим», чтобы связать подзапросы вместе для функции отображения.

Когда я пытаюсь запустить приложение, я получаю следующую ошибку:

"Исключение типа 'System.StackOverflowException' было сгенерировано."


Я попробовал следующие изменения: В подзапросе VM

//public List<SubQuestionVM> SubquestionCurrentMapping { get; set; }                                                                         
public List<SubQuestionMappedVM> SubquestionCurrentMapping { get; set; } = new List<SubQuestionMappedVM>(); //Trying to fix by changing vm

Есть моя новая виртуальная машина для тестирования:

public class SubQuestionMappedVM
{
    public int ID { get; set; }
    public int QuestionID { get; set; }
    public string Name { get; set; }
    //Remove this property, don't need more than 1 level of recursion anyway
    //public List<SubQuestionMappedVM> SubquestionCurrentMapping { get; set; }
    public List<AnswerVM> Answers { get; set; }
}

Я также добавил новую виртуальную машину в метод, который выполняет сопоставление:

Mapper.Register<SubQuestion, SubQuestionMappedVM>();

Я думаю, что моя проблема в том, что я сопоставляю подзапрос VM, который содержит список подзапроса VM, создающий рекурсию. Я пытаюсь создать другой подзапрос VM, чтобы обойти проблему, но моя веб-страница даже не отображается в моем браузере. Через 1 минуту 45 минут Visual Studio отвечает «Задача была отменена».

Если кто-нибудь знает, как отобразить мою рекурсивную SubquestionVM, как использовать другую виртуальную машину для остановки рекурсии или любое другое решение, предотвращающее ошибку переполнения стека, я благодарен!

1 Ответ

0 голосов
/ 28 марта 2019

Вот как я решил эту проблему:

Я не мог понять, как использовать ExpressMapper или другую модель представления для обхода исключения переполнения стека, поэтому я создал метод, который выполняет ручное отображение из модели EF в модель DTO.

/// <summary>
/// Map list of SubquestionVM (SubquestionCurrentMapping) with data from current Component (EF model).
/// 
/// Why are we doing this?
///     Because when using the ExpressMapping to map 'SubQuestion' to 'SubQuestionVM', it creates a stack overflow error on the property 'SubquestionCurrentMapping'
///     which is caused by recursive VM.
///     I originaly tried alternative solution like:
///         changing 'List<SubQuestionVM>' for 'List<SubQuestionMappedVM>' but the website was not even loading (even with proper value in the new VM, and global.asax),
///         loading the faulty property later on, but any attempt to populate the object was resulting in an overflow at a moment or another.
///     Thankfully the manual mapping is proven to be effective and errorless!
/// </summary>
/// <param name="evaluationVM"></param>
/// <param name="component"></param>
private static void ManualMappingOfRecursiveSubquestionVM(CurrentEvaluationVM evaluationVM, Component component)
{
    foreach (var subquestion in component?.Question?.SubQuestions)
    {
        //Find corresponding subquestionVM and manually map them
        var subquestionVM = evaluationVM.CurrentComponent?.Question?.SubQuestions.Find(s => s.ID == subquestion.ID);

        foreach (var subquestionMapping in subquestion.SubquestionCurrentMapping.ToList())
        {
            var tempSubquestionVM = new SubQuestionVM
            {
                ID = subquestionMapping.ID,
                QuestionID = subquestionMapping.QuestionID,
                Name = subquestionMapping.Name,
                Clarification = subquestionMapping.Clarification,
                Description = subquestionMapping.Description,
                Index = subquestionMapping.Index,
                CanSelectGoal = subquestionMapping.CanSelectGoal,
                IsDate = subquestionMapping.IsDate,
                Deprecated = subquestionMapping.Deprecated,
                MultipleChoices = subquestionMapping.MultipleChoices.Map<ICollection<MultipleChoice>, List<MultipleChoiceVM>>(),
                Answers = subquestionMapping.Answers.Map<ICollection<Answer>, List<AnswerVM>>()
            };
            subquestionVM.SubquestionCurrentMapping.Add(tempSubquestionVM);
        }
    }
}
...