如何用ExpressMapper映射递归嵌套对象



我正在处理Web应用程序(EF6代码),使用户能够填写评估。一个评估包含多个问题,一个问题包含多个子来源。每个子询问都有一个"映射功能",使用户能够将一个子提示与另一个现有子概论相关联。

我有以下实体框架模型(我删除了一些属性,因为我的示例不需要它们)

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)。我们有一种使用EF模型对所有DTO进行映射的方法:

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

所有内容都映射并正常工作,直到我在subquesionvm中添加以下属性:

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

此属性创建了一个多对多的表,以将子问题链接到映射功能中。

当我尝试启动应用程序时,我会收到以下错误:

"类型'system.stackoverflowException'的例外。"


我尝试的更改是:在subquehionvm

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

有我的新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; }
}

我还向执行映射的方法添加了新的VM:

Mapper.Register<SubQuestion, SubQuestionMappedVM>();

我认为我的问题是因为我正在映射一个子来源VM,其中包含一个subsecrevm的列表,创建了递归。我正在尝试创建一个不同的subqueStionVM来绕过问题,但是我的网页甚至都不显示在浏览器中。在冗长的1min45之后,Visual Studio响应"任务已取消"。

如果有人知道如何映射我的递归subsequesionvm,如何使用其他VM停止递归或任何其他阻止堆栈溢出错误的解决方案,我很感谢!

这是我解决这个问题的方式:

我无法弄清楚如何使用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);
        }
    }
}

最新更新