【问题标题】:Creating a complex data structure创建复杂的数据结构
【发布时间】:2020-07-16 01:03:35
【问题描述】:

我正在尝试通过创建一个小测验应用程序来自学 c#(asp.net 核心),但我对如何创建复杂的数据模型感到困惑。 想象一个包含如下问题集合的测验模型:

    public class Quiz
    {
       public int Id { get; set; }
       public Icollection<Question> Questions { get; set; }
    }

但是,如果我想要不同类型的问题(多项选择、真/假、文本答案...)我可以使用继承来缩短某些内容,还是我只需要为每种类型的问题创建不同的模型并输入他们是这样的:

Public class Quiz
{
   public Icollection<MultipleChoicQuestion> Questions { get; set; }
   public Icollection<TrueOrFalseQuestion> Questions { get; set; }
   public Icollection<TextQuestion> Questions { get; set; }
}

【问题讨论】:

  • 仅供参考,public 没有大写,ICollection 大写 C...
  • 简短的回答是肯定的,你可以使用继承并且仍然有一个ICollection&lt;Question&gt;(假设其他类型继承自Question)。当您尝试访问子类特定方法时,问题会更进一步。然后你可能会得到一堆if (Questions[i] is TextQuestion)。但是如果你可以把所有的公共方法放在问题类中(实际上它应该是一个接口),那么它就可以正常工作,因为每个类处理它的实现方式不同。
  • 感谢您的回答,但请您把一些代码写下来,以便我更好地理解,我是新手。如果问题类型继承自 Question,那么我不必在 Quiz 模型中放置每种类型问题的集合?只是一组问题?
  • 正确。试试看会发生什么!

标签: c# asp.net-core data-modeling


【解决方案1】:

这个问题与 .NET Core 或 EF 并不完全相关,正如您所标记的,它与数据建模有关。

对于那些有点不同类型的模型,我建议你做如下。

这是Quiz 模型。

public class Quiz
{
    public int Id { get; set; }
    public List<Question> Questions { get; set; }
}

枚举问题

public class Question
{
    public int Id { get; set; }

    public QuestionType Type { get; set; }

    public List<Answer> Answers { get; set; }
}

public enum QuestionType
{
    MultipleChoice,
    TrueFalse,
    Text
}

最后一个Answers

public class Answer
{
    public int Id { get; set; }

    public int QuestionId { get; set; } 

    public Question Question { get; set; }

    public bool IsCorrect { get; set; }

    public string Value { get; set; }
}

这样,应用层将处理所有进程,但您将非常容易地存储数据。

对于MultipleChoice 问题,您添加多个答案并设置正确的IsCorrect = true

对于TrueFalse,只添加2个答案并将IsCorrect = true设置为正确。

对于Text,只添加1个答案并设置IsCorrect = true

【讨论】:

  • 老兄!这太棒了。还有一件事,多人参加测验怎么样?我会为每个新学生建立另一个实体,对吗?类似于“UserAnswers”的东西,有两个外键指向测验 ID 和问题 ID,以及所有学生答案的集合以供评分。
  • 没错。您还需要一个存储用户答案的​​对象。
【解决方案2】:

一种方法是创建一个IQuestion 接口,其中包含运行测验所需的所有面向公众的方法和属性:

public interface IQuestion
{
    void AskQuestion();
    string CorrectAnswer { get; }
    bool IsCorrect { get; }
}

那么你可以在你的Quiz类中拥有这个接口的集合:

public class Quiz
{
    public ICollection<IQuestion> Questions { get; set; }
}

现在我们可以创建单独的类,每个类都以自己的方式实现IQuestion 属性和方法。

例如:

public class TextQuestion : IQuestion
{
    public bool IsCorrect => string.Equals(_userAnswer?.Trim(), CorrectAnswer?.Trim(),
        StringComparison.OrdinalIgnoreCase);

    public string CorrectAnswer { get; }

    private readonly string _question;
    private string _userAnswer;

    public TextQuestion(string question, string answer)
    {
        _question = question;
        CorrectAnswer = answer;
    }

    public void AskQuestion()
    {
        Console.WriteLine(_question);
        _userAnswer = Console.ReadLine();
    }
}

public class MultipleChoiceQuestion : IQuestion
{
    public bool IsCorrect => _userIndex == _correctIndex + 1;

    public string CorrectAnswer => (_correctIndex + 1).ToString();

    private readonly string _question;
    private readonly List<string> _choices;
    private readonly int _correctIndex;
    private int _userIndex;

    public MultipleChoiceQuestion(string question, List<string> choices, int correctIndex)
    {
        _question = question;
        _choices = choices;
        _correctIndex = correctIndex;
    }

    public void AskQuestion()
    {
        Console.WriteLine(_question);

        for (var i = 0; i < _choices.Count; i++)
        {
            Console.WriteLine($"{i + 1}: {_choices[i]}");
        }

        _userIndex = GetIntFromUser($"Answer (1 - {_choices.Count}): ",
            i => i > 0 && i <= _choices.Count);
    }

    private static int GetIntFromUser(string prompt, Func<int, bool> validator = null)
    {
        int result;

        do
        {
            Console.Write(prompt);
        } while (!int.TryParse(Console.ReadLine(), out result) &&
                 (validator == null || validator.Invoke(result)));

        return result;
    }
}

public class TrueOrFalseQuestion : IQuestion
{
    public bool IsCorrect => _userAnswer == _correctAnswer;

    public string CorrectAnswer => _correctAnswer.ToString();

    private readonly string _question;
    private readonly bool _correctAnswer;
    private bool _userAnswer;

    public TrueOrFalseQuestion(string question, bool correctAnswer)
    {
        _question = question;
        _correctAnswer = correctAnswer;
    }

    public void AskQuestion()
    {
        _userAnswer = GetBoolFromUser(_question + " (true/false)");
    }

    private static bool GetBoolFromUser(string prompt)
    {
        bool result;

        do
        {
            Console.Write(prompt + ": ");
        } while (!bool.TryParse(Console.ReadLine(), out result));

        return result;
    }
}

使用示例

static void Main()
{
    var quiz = new Quiz
    {
        Questions = new List<IQuestion>
        {
            new MultipleChoiceQuestion("Which color is also a fruit?",
                new List<string> {"Orange", "Yellow", "Green"}, 0),
            new TextQuestion("What is the last name of our first president?", 
                "Washington"),
            new TrueOrFalseQuestion("The day after yesterday is tomorrow", false),
        }
    };

    foreach (var question in quiz.Questions)
    {
        question.AskQuestion();
        Console.WriteLine(question.IsCorrect
            ? "Correct!\n"
            : $"The correct answer is: {question.CorrectAnswer}\n");
    }

    Console.WriteLine("Your final score is: " +
        $"{quiz.Questions.Count(q => q.IsCorrect)}/{quiz.Questions.Count}");

    GetKeyFromUser("\nPress any key to exit...");
}

【讨论】:

  • 谢谢,您帮了大忙。但只有一个问题。带有 IQuestion 集合的 Quiz 类,我无法创建带有 IQuestion 集合集合的 EfCore 表,我不应该为 Question 集合创建一个表吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-12
  • 2017-04-03
  • 1970-01-01
  • 2011-10-06
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多