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

发布于 2024-09-09 07:23:14 字数 1448 浏览 3 评论 0原文

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

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

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

I'm trying to find a tidy solution to a questionnaire problem. Let us say that I have a Questionnaire class which has a collection of Answers, e.g.

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

Answers need to be of different types depending on the question, e.g. date of birth, marks out of ten, why do you think etc.

My first thought was something like this:

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.

The obvious problem would be that from the questionnaire, there is no access to the Response property:

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

The same would apply to an interface. It would be nicer to use a generic interface, such as:

public interface IAnswer<T> 
{
    public virtual Question Question {get;set;}
    public virtual T Response {get;set;}
}

public class DateTimeAnswer : IAnswer<DateTime> {}

The problem then comes in the Questionnaire class, as the type of IAnswer must be supplied:

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

Clearly I don't want to have many collections of IAnswer each with different types. I could use

ISet<IAnswer<dynamic>> 

but then NHibernate wouldn't like it.

I realise a compromise is needed somewhere, but I'm not sure which is the prettiest. What would you do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柏林苍穹下 2024-09-16 07:23:14

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

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

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

I think subclassing Question as well as answer is a good plan - get rid of that QuestionType enum.

You can then have a MakeAnswer(string) abstract method on Question which encapsulates a lot of logic for you, and can do type conversion etc if necessary.

My solution to the generic problem on Rob's answer would be something like this:

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

Where IAnswerFormatter looks something like this:

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

And DateTimeAnswer would look like this:

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);
    }
}

And DateTimeQuestion might look like:

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

This way your answer can hold the value in their own way, and you NH mappings can specify how it looks in the database (I'd recommend mapping using Discriminators) your Question can be responsible for creating answers from generic responses.

风渺 2024-09-16 07:23:14

有趣的问题..

我的评论/想法:

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

我会沿着这样的思路思考:

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,这就是集合, >答案

这有帮助吗? :)

Interesting Problem..

My comments/thoughts:

  • As Steve said - get rid of that nasty QuestionType enum!
  • Remove the ISet<T> - I don't think it adds any value..

I would be thinking along the lines of something like:

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"; }
}

The idea being here, we need to get to the essence of what you are doing to ALL of the objects, which is likely just displaying the answer.. We add type safety by way of generics so we can be sure that we can't create new responses without a type parameter..

We can then be pretty sure that what is going in and coming out is confined to the types of answers that we implement. NHib should have no real problem dealing with this since it knows what it needs.

While it sucks we have the "object" version of the Answer coming back from the collection, that is the what the collection is, Answers.

Does this help? :)

夜雨飘雪 2024-09-16 07:23:14

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

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

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

Does it really make sense to store the answers in a full on data model? What are you going to be doing with them?

The most likely candidate seems to be reporting in which case you may want to look into the CQRS (Command Query Responsibility Separation) style. You would instead have a QuestionnaireCompletedCommand which would contain a list of answers you would then persist in some way that a reports could be ran against them.

Data models are great when you have business logic (which you might) but if you don't have any business logic you are likely just unnecessarily complicating the solution. Speaking of complicating when I say look at CQRS I don't necessarily mean the event sourcing part. That is a huge complication that very few people need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文