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

时间:2010-07-12 21:19:47

标签: c# nhibernate design-patterns collections interface

我正在努力为问卷调查问题找到一个整洁的解决方案。我们假设我有一个Questionnaire类,其中包含Answer s的集合,例如

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不喜欢它。

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

3 个答案:

答案 0 :(得分:4)

我认为继承问题和答案是一个很好的计划 - 摆脱那个QuestionType枚举。

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

我对Rob's answer上的一般问题的解决方案是这样的:

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映射可以指定它在数据库中的外观(我建议使用Discriminators进行映射)你的问题可以负责从一般的响应中创建答案。 / p>

答案 1 :(得分:1)

有趣的问题..

我的意见/想法:

  • 作为Steve said - 摆脱那令人讨厌的QuestionType枚举!
  • 删除ISet<T> - 我认为它不会增加任何价值..

我会按照以下方式思考:

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应该没有解决这个问题的真正问题,因为它知道它需要什么。

虽然很糟糕,我们从集合中回来了object版本的Answer 集合是什么,答案< / em>的

这有帮助吗? :)

答案 2 :(得分:-1)

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

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

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