1

从EF开始,我正在使用EF,因为我正在C#上构建MVC应用程序。我希望不同类型的考试有不同类型的问题。这里是我的抽象类:是不是在考试类声明的典型public virtual ICollection<Question>,我创建了一个抽象的setter和getter如何强制子类具有超类属性的特定子类?

public abstract class Exam 
{ 
    public int Id { get; set; } 
    public string Description { set; get; } 

    public abstract ICollection<Question> GetQuestions(); 
    public abstract void SetQuestions(ICollection<Question> questions); 
} 

public abstract class Question 
{ 
    public int Id { get; set; } 
    public string Description { set; get; } 

    public abstract Exam getExam(); 
    public abstract void setExam(Exam exam); 
} 

通知。 Question类的Exam属性也是如此。

这里是我的具体考试类:

[Table("SingleExam")] 
public class SingleExam : Exam 
{ 
    public virtual ICollection<SingleQuestion> Questions { get; set; } 

    public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; } 
    public override void SetQuestions(ICollection<Question> questions) 
    { 
     if (!(questions is ICollection<SingleQuestion>)) 
      throw new ArgumentException("You must set single questions."); 

     Questions = questions as ICollection<SingleQuestion>; 
    } 
} 

[Table("MultipleExam")] 
public class MultipleExam : Exam 
{ 
    public virtual ICollection<MultipleQuestion> Questions { get; set; } 

    public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; } 
    public override void SetQuestions(ICollection<Question> questions) 
    { 
     if (!(questions is ICollection<MultipleQuestion>)) 
      throw new ArgumentException("You must set multiple questions."); 

     Questions = questions as ICollection<MultipleQuestion>; 
    } 
} 

...我的具体问题类:

[Table("SingleQuestion")] 
public class SingleQuestion : Question 
{ 
    public int ExamId { get; set; } 
    public virtual SingleExam Exam { get; set; } 

    public override Exam getExam() { return Exam; } 
    public override void setExam(Exam exam) 
    { 
     if (!(exam is SingleExam)) 
      throw new ArgumentException("You must set a SingleExam"); 

     Exam = exam as SingleExam; 
    } 
} 

[Table("MultipleQuestion")] 
public class MultipleQuestion : Question 
{ 
    public int ExamId { get; set; } 
    public virtual MultipleExam Exam { get; set; } 

    public override Exam getExam() { return Exam; } 
    public override void setExam(Exam exam) 
    { 
     if (!(exam is MultipleExam)) 
      throw new ArgumentException("You must set a MultipleExam"); 

     Exam = exam as MultipleExam; 
    } 
} 

我所做的这一切都是因为一个MultipleExam应该只有MultipleQuestions和SingleExam应只有SingleQuestions,与MultipleQuestion应该有一个MultipleExam和Single问题应该有一个SingleExam相同的方式。

有没有更好的方法来确保类'A'的子类包含或具有类'B'的特定子类(就像我的考试和问题一样),并通过抽象类没有抽象的getters和setter?

+0

为什么问题需要了解它所在的考试? – Servy

+0

它们只是导航属性,但它们并不真正重要(尽管需要正确映射数据库的ExamId)。真正重要的是确保不同类型的考试包含特定的问题类别 –

+0

除非您遗漏了关于单独(多/单)考试和(多/单)问题类型目的的其他细节,这种方法似乎已经结束-复杂。单一的考试和问题类不会更容易吗?如果您需要在单考和多考之间添加区别,您可以添加在构造函数中设置的bool单一属性,并控制考试是否只能有一个或多个问题。 – Richard

回答

0

正如其他人所说,我认为你已经过度复杂化你的问题。 但是;你的问题是关于类型保证,我会尽力回答。

首先代码:

所有的
public interface IExam<out T> where T:IQuestion { 
    int Id { get; set; } 
    string Description { set; get; } 
    IEnumerable<T> GetQuestions(); 
} 

public interface IQuestion{ 
    int Id { get; set; } 
    string Description { set; get; } 
    IExam<IQuestion> Exam { get; } 
} 

public class SingleQuestion:IQuestion { 
    public string Description { get; set; } 
    public int Id { get; set; } 
    IExam<IQuestion> IQuestion.Exam { 
    get { return Exam; } 
    } 
    public SingleExam Exam { get; set; } 
} 

public class SingleExam:IExam<SingleQuestion> { 
    public int Id { get; set; } 
    public string Description { get; set; } 

    private IEnumerable<SingleQuestion> _questions; 
    public IEnumerable<SingleQuestion> GetQuestions() { 
    return _questions; 
    } 

    public void SetQuestions(IEnumerable<SingleQuestion> questions) { 
    _questions = questions; 
    } 
} 

首先我们与接口取代了抽象类。 这是必需的,因为我们希望在IQuestion上做IExam协变,并且协方差只能在接口中定义。这也是为什么我们更改为集合的IEnumerable。

注意,我们并没有在IExam中定义SetQuestions方法,因为我们不能。长久以来,这是因为这会使T变得不对等,反过来又导致无法提供类型保证的情况。

IQuestions相当直接,没有真正的变化。我想,你可以把它作为一个抽象类型。

现在的实现: 在SingleQuestion中,我们必须明确地实现Exam,期望IExam然后用一个返回SingleExam的属性对它进行遮蔽。 这使我们可以返回最准确的考试类型。

SingleQuestion sq = new SingleQuestion(); 
IQuestion q = sq; //Upcast 
sq.Exam; //returns a SingleExam 
q.Exam; //returns a IExam<IQuestion> 

在SingleExam您现在可以设置的问题和限制,这样只有SingleQuestions可以加入。

顺便说一下,现在更容易明白为什么SetQuestions无法在IExam中定义。考虑以下几点:

SingleExam se = new SingleExam(); 
IExam<IQuestion> singleUpcast = se; 
//What type of question can we set on singleUpcast? 

所有我们知道的是,singleUpcast包含IQuestions但我们不能只是添加IQuestions因为singleUpcast最终许诺,只有SingleQuestions可以这样设置SingleExam的一个实例。简而言之,不可能知道哪些类型可以添加到IExam中,而没有潜在的破坏类型保证