【问题标题】:Questions with different types of answer in NHibernateNHibernate 中不同类型答案的问题
【发布时间】:2011-03-15 00:37:39
【问题描述】:

我正在尝试为问卷问题找到一个简洁的解决方案。假设我有一个 Questionnaire 类,其中包含 Answers 的集合,例如

public class Questionnaire
{
    public virtual ISet<Answer> 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<T> 
{
    public virtual Question Question {get;set;}
    public virtual T Response {get;set;}
}

public class DateTimeAnswer : IAnswer<DateTime> {}

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

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

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

ISet<IAnswer<dynamic>> 

但是 NHibernate 不喜欢它。

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

【问题讨论】:

    标签: c# nhibernate design-patterns collections interface


    【解决方案1】:

    我认为将 Question 和 answer 子类化是一个好计划 - 去掉 QuestionType 枚举。

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

    我对@9​​87654321@ 上的一般问题的解决方案是这样的:

    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 映射可以指定它在数据库中的外观(我建议使用鉴别器进行映射)您的问题可以负责从通用响应中创建答案。

    【讨论】:

      【解决方案2】:

      有趣的问题..

      我的想法/想法:

      • 作为Steve said - 摆脱那个讨厌的QuestionType 枚举!
      • 删除ISet&lt;T&gt; - 我认为它不会增加任何价值..

      我的想法是这样的:

      public class Questionnaire
      {
       public AnswerCollection Answers { get; set; }
      }
      
      public class AnswerCollection : Collection<Answer>
      {
        // Subclassed Collection<T> for Add/Remove Semantics etc.
      }
      
      public abstract class Answer : IAnswer<object>
      {
        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<DateTime>
      {
       public override DateTime Response { get { // Do Stuff }; }
       public override string CommonOperation() { return "I can haz DateTime"; }
      }
      

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

      然后,我们可以非常确定,输入和输出的内容仅限于我们实施的答案类型。 NHib 应该没有真正的问题来处理这个问题,因为它知道自己需要什么。

      虽然很糟糕,但我们从集合中返回了 Answer 的“object”版本,集合是什么,答案

      这有帮助吗? :)

      【讨论】:

      • 我同意一般结构,但我认为您已经暴露了将 IAnswer 作为泛型类的问题。在您的示例中,DateTimeAnswer 不是答案,因此无法进入 AnswersCollection。您还必须让它从 Answer 继承,并实现 Response 属性的对象版本以及您的强类型属性。从问题开始,您只会遍历 Answers,强类型的 DateTime 响应似乎是一种浪费。
      • 确实有帮助,谢谢。我会考虑一下并尝试一下。
      • 啊,史蒂夫 - 好地方,答案应该继承答案并实现接口。我会纠正的。强类型更多是为了在创建时安心,你不能创建“对象”版本,因为它是抽象的。这是“想要强类型,但实际上只有真的 10% 的时间都需要它”..
      【解决方案3】:

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

      最有可能的候选人似乎正在报告,在这种情况下,您可能需要研究 CQRS(命令查询职责分离)样式。相反,您将拥有一个 QuestionnaireCompletedCommand,其中包含一个答案列表,然后您将以某种方式坚持下去,以便针对它们运行报告。

      当您拥有业务逻辑时(您可能会这样做),数据模型非常棒,但如果您没有任何业务逻辑,您可能只是让解决方案变得不必要地复杂化。说到复杂化,当我说查看 CQRS 时,我并不一定是指事件采购部分。这是一个非常复杂的问题,很少有人需要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多