同一属性中的不同对象类型

时间:2013-01-22 09:40:32

标签: c# ravendb

我正在玩RavenDb并正在构建某种测验。有不同类型的问题: - 多项选择(A,B,C或D?) - 日期(在什么日期......?) - 数量(多少......?)

我所做的是创建一个带有属性Question的基类问题,其中包含用户提供的字符串和答案列表的问题。

public class Question
{
    public string Question { get; set; }
    public List<Answer> Answers { get; set; }
}

然后我创建了多个继承自Question的子类。 例如,NumberQuestion包含属性MinimumValueMaximumValue,因为每个问题存在差异。

我希望用户给出答案,并将该答案保存到数据库中。问题是,答案可以是不同的类型,如DateTime,float或integer(多选)。 我的问题是,在RavenDb中保存答案的最佳方法是什么?

这就是我目前正在做的事情:

public class Answer
{
    public User User { get; set; }
    public string AnswerString { get; set; }
    public string AnswerType { get; set; }
}

我在这里做的是将答案保存为字符串并保存类型(DateTime,float等),以便稍后解析。

它有效,但我不太喜欢它。必须有另一种更好的方式。

2 个答案:

答案 0 :(得分:4)

如果要在同一属性中存储多个类型,则只需将该属性声明为公共基类型。在你的情况下,object会做得很好。序列化后,您将在json中获得额外的$type个字段,这样就可以将它们反序列化为适当的形式。

让我们看看我是否可以就您的特定域模型提供一些建议。

  • 不要将可能的答案与实际答案混淆。给出这些不同的名称以保持它们的直线。我将使用Choice来表示问题的可能答案,并使用Answer来表示用户给出的实际答案。

  • 注意聚合实体的位置。这些最终在RavenDB中作为实际文档并获得Id。在您的情况下,我只看到两个 - QuestionExam

尝试使用此尺寸:

public abstract class Question
{
    public string Id { get; set; }
    public string QuestionText { get; set; }
}

public class ValueQuestion : Question
{
    public object CorrectValue { get; set; }
}

public class RangeQuestion : Question
{
    public object MinCorrectValue { get; set; }
    public object MaxCorrectValue { get; set; }
}

public class MultipleChoiceQuestion : Question
{
    public int NumberOfChoicesAllowed { get; set; }
    public List<MultipleChoiceOption> Choices { get; set; }
}

public class MultipleChoiceOption
{
    public char Letter { get; set; }
    public bool Correct { get; set; }
    public object Value { get; set; }
}

public class EssayQuestion : Question
{
    public int MinAnswerLength { get; set; }
    public int MaxAnswerLength { get; set; }
}

public class Exam
{
    public string Id { get; set; }
    public string UserId { get; set; }
    public DateTime Taken { get; set; }
    public decimal Score { get; set; }
    public List<Answer> Answers { get; set; }
}

public class Answer
{
    public string QuestionId { get; set; }
    public bool Correct { get; set; }
    public object Value { get; set; }
}

泛型可能很诱人,但我认为你会发现它们并没有给你带来太大的收益。最后,您将在文档中使用相同的结构,并在数据库中使用相同的布局。可能唯一的区别是$type字段用于不同的地方。

如果您想尝试基于泛型的解决方案,请尝试以下方法:

public abstract class Question
{
    public string Id { get; set; }
    public string QuestionText { get; set; }
}

public class ValueQuestion<T> : Question
{
    public T CorrectValue { get; set; }
}

public class RangeQuestion<T> : Question
{
    public T MinCorrectValue { get; set; }
    public T MaxCorrectValue { get; set; }
}

public class MultipleChoiceQuestion<T> : Question
{
    public int NumberOfChoicesAllowed { get; set; }
    public List<MultipleChoiceOption<T>> Choices { get; set; }
}

public class MultipleChoiceOption<T>
{
    public char Letter { get; set; }
    public bool Correct { get; set; }
    public T Value { get; set; }
}

public class EssayQuestion : Question
{
    public int MinAnswerLength { get; set; }
    public int MaxAnswerLength { get; set; }
}

public class Exam
{
    public string Id { get; set; }
    public string UserId { get; set; }
    public DateTime Taken { get; set; }
    public decimal Score { get; set; }
    public List<IAnswer> Answers { get; set; }
}

public interface IAnswer
{
    string QuestionId { get; set; }
    bool Correct { get; set; }
}

public class Answer<T> : IAnswer
{
    public string QuestionId { get; set; }
    public bool Correct { get; set; }
    public T Value { get; set; }
}

答案 1 :(得分:0)

你可以使用泛型。创建Question的通用类TAnswer,其中TAnswer必须从抽象类Answer继承。特定问题类将从该类继承:

public abstract class Question<TAnswer> where TAnswer : Answer
{
    public Guid Id { get; set; }
    public string Question { get; set; }
    public List<TAnswer> Answers { get; set; }
}

public class DateQuestion : Question<DateAnswer>
{
    //...
}

您用于答案的类都继承自基类Answer,它引用了它所属的问题以及已经回答它的用户。然后,您可以为每种答案类型设置一个单独的表,从而防止解析类型,同时保持问题,答案和回答它们的用户之间的联系

public abstract class Answer {
    public Guid QuestionId { get; set; }
    public Guid UserId { get; set; }
}

public class MultipleChoiceAnswer :Answer {
    //...
}

public class DateAnswer : Answer {
    //...
}