NHibernate中有不同类型答案的问题

我正试图找到问卷调查问题的整洁解决方案。 让我们说我有一个Questionnaire ,其中包含一些Answer ,例如

 public class Questionnaire { public virtual ISet Answers {get;set;} } 

答案需要根据问题的不同类型,例如出生日期,十分之一,你为什么这么认为等等。

我的第一个想法是这样的:

 public class Question { public virtual QuestionType TypeOfQuestion {get;set;} public virtual string PromptText {get;set;} } public class Answer { public virtual Question Question {get;set;} } public class DateTimeAnswer : Answer { public virtual DateTime Response {get;set;} } public class IntegerAnswer : Answer { public virtual int Response {get;set;} } // etc. 

显而易见的问题是,从调查问卷中,无法访问Response属性:

 questionnaire.Answers[0].Response; // compile error 

这同样适用于接口。 使用通用接口会更好,例如:

 public interface IAnswer { public virtual Question Question {get;set;} public virtual T Response {get;set;} } public class DateTimeAnswer : IAnswer {} 

然后问题出现在Questionnaire类中,因为必须提供IAnswer的类型:

 public class Questionnaire { public virtual ISet<IAnswer> Answers {get;set;} } 

显然,我不希望有许多IAnswer的集合,每个集合都有不同的类型。 我可以用

 ISet<IAnswer> 

但是NHibernate不喜欢它。

我意识到某处需要妥协,但我不确定哪个是最漂亮的。 你会怎么做?

我认为子类化问题和答案是一个很好的计划 – 摆脱QuestionType枚举。

然后,您可以在Question上使用MakeAnswer(字符串)抽象方法,它为您封装了许多逻辑,并且可以在必要时进行类型转换等。

我对Rob的答案的一般问题的解决方案是这样的:

 public interface IAnswer { Question Question { get; } void Respond(IAnswerFormatter formatter); } 

IAnswerFormatter看起来像这样:

 public interface IAnswerFormatter { void Format(string value); void Format(DateTime value); ... } 

而DateTimeAnswer看起来像这样:

 public class DateTimeAnswer : IAnswer { public DateTimeAnswer(Question question, DateTime value) { Question = question; } public Question Question { get; protected set; } protected DateTime Response {get; set;} public virtual void Respond(IAnswerFormatter formatter) { formatter.Write(Response); } } 

而DateTimeQuestion可能如下所示:

 public class DateTimeQuestion : Question { public IAnswer MakeAnswer(string value) { // Ignoring error handling here return new DateTimeAnswer(this, DateTime.Parse(value)); } } 

这样你的答案可以用自己的方式保存值,你的NH映射可以指定它在数据库中的外观(我建议使用鉴别器进行映射)你的问题可以负责从一般响应中创建答案。

有趣的问题..

我的意见/想法:

  • 正如史蒂夫所说 – 摆脱那个令人讨厌的QuestionType枚举!
  • 删除ISet – 我认为它不会增加任何值..

我会按照以下方式思考:

 public class Questionnaire { public AnswerCollection Answers { get; set; } } public class AnswerCollection : Collection { // Subclassed Collection for Add/Remove Semantics etc. } public abstract class Answer : IAnswer { public override object Response { get { // Base Impl. Here }; } public abstract string CommonOperation() { // This is the key, the "common operation" - likely ToString? // (for rendering the answer to the screen) // Hollywood Principle - let the answers figure out how they // are to be displayed... } } public class DateTimeAnswer : Answer, IAnswer { public override DateTime Response { get { // Do Stuff }; } public override string CommonOperation() { return "I can haz DateTime"; } } 

这个想法在这里,我们需要了解你正在对所有对象做的事情的本质,这可能只是显示答案..我们通过generics添加类型安全,所以我们可以肯定我们不能创建没有类型参数的新响应..

然后,我们可以非常确定进出的内容仅限于我们实施的答案类型。 NHIB应该没有真正解决这个问题,因为它知道它需要什么。

虽然它很糟糕,我们有从该集合回来的Answer的“ object ”版本,这就是该集合的内容, Answers

这有帮助吗? 🙂

将答案存储在完整的数据模型中是否真的有意义? 你打算和他们一起做什么?

最可能的候选人似乎是报告,在这种情况下,您可能希望查看CQRS(命令查询责任分离)样式。 相反,你会有一个QuestionnaireCompletedCommand,其中包含一个答案列表,然后你会以某种方式持续存在可以针对它们运行报告的答案。

当您拥有业务逻辑(您可能)时,数据模型很棒,但如果您没有任何业务逻辑,则可能只会使解决方案不必要地复杂化。 说到看看CQRS时的复杂性,我不一定是指事件采购部分。 这是一个很少人需要的巨大复杂因素。