65.9K
CodeProject 正在变化。 阅读更多。
Home

创建多项选择考试(DLINQ - 第一部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (10投票s)

2008 年 1 月 14 日

CPOL

4分钟阅读

viewsIcon

104577

一篇关于使用 DLINQ 创建多项选择考试的文章(第一部分)。

引言

几个月前,我写了一篇文章,其中讨论了使用 NHibernate 创建在线考试。 在本文中,我将使用 LINQ to Classes 创建在线考试。 这是一个多系列文章,在这一部分中,我将讨论应用程序的架构和设计。

应用程序需求

客户需要一个应用程序,学生可以登录并参加考试。 考试是使用客户提供的 XML 文件上传的。 用户将参加考试,然后查看他的分数。

我将不讨论用户登录和身份验证部分,而将重点放在考试模块上。

数据库设计

数据库名称为 School,它由五个表组成。 请看下面的数据库图

  • Users:此表保存用户。
  • Exams:此表保存考试。
  • Questions:此表保存考试的问题。
  • Choices:此表保存问题的选项。
  • UserExams:此表保存用户的考试成绩和分数。

类图

LINQ to Classes 允许您使用数据库表创建类。 简单地,从服务器资源管理器将表拖放到设计视图中,它将自动创建类图。 请看下面的实体类图

除了实体类图,我还包含了 RepositoryServices 类图。 这是一个很大的图,所以不要害怕!

让我解释一下存储库的架构。 每个聚合根都有自己的存储库。 ExamExamRepositoryUserUserRepository 等。 没有 QuestionChoices 的存储库。 这是因为它们由 ExamRepository 处理。

每个存储库都继承自特定的存储库接口和 BaseRepository 类。 每个具体的存储库都继承自名为 IBaseRepository 的基本存储库接口。 这是因为每个具体的存储库,即 ExamRepositoryUserRepository 必须公开一些通用方法,例如 GetByIdAddPersistAllGetAll。 如果我将这些方法放在相应的存储库接口中,那么我必须在具体的存储库中实现这些接口,这意味着重复的代码。

BaseRepository 来救援并实现了所有存储库公开的通用方法。 这些方法也被标记为 virtual,因此可以在子存储库中覆盖它们。

让我们看一下 IBaseRepository 接口

public interface IBaseRepository
{
    T GetById<t>(int id) where T : class;
    void Add<t>(T item) where T : class;
    void PersistAll();
    void AddAndPersistAll<t>(T item) where T : class;
}

如您所见,IBaseRepository 接口使用泛型类型 T。 我将在本文后面解释使用约束的目的。 现在,让我们看看在 BaseRepository 类中实现的泛型 GetById 方法的实现

public T GetById<t>(int id) where T : class
{
    var table = school.GetTable<t>();

    // finding the name of the PK column in the database
    MetaModel mapping = table.Context.Mapping;
    ReadOnlyCollection<metadatamember /> members = mapping.GetMetaType(typeof(T)).DataMembers;

    string pk = (members.Single<metadatamember />(m => m.IsPrimaryKey)).Name;

    // getting the object by Id
    return table.SingleOrDefault<t>
        (delegate(T t)
    {
        int memberId = (int)t.GetType().GetProperty(pk).GetValue(t, null);
        return memberId == id;
    });
}

上面的 GetById 方法看起来有点复杂,但我会尽力解释它。 首先,让我们谈谈约束,如下所示

public T GetById<t>(int id) where T : class

约束表示类型 T 必须是一个类。 这是因为方法 school.GetTable<t>() 仅适用于引用类型。 GetById 方法的目的是根据其 Id 返回对象。 问题是每个实体类的 primary key 列的名称都不同。 User 类有 UserIDExam 类有 ExamID 等等。 这使得事情更加复杂,因为我们必须找到充当 primary key 的列的名称。 为此,我使用以下代码,该代码查找表的所有列,然后查找充当 primary key 的列名。

// finding the name of the PK column in the database
MetaModel mapping = table.Context.Mapping;
ReadOnlyCollection<metadatamember /> members = mapping.GetMetaType(typeof(T)).DataMembers;

string pk = (members.Single<metadatamember />(m => m.IsPrimaryKey)).Name;

一旦我们获得了 primary key,我们就可以从该列中提取值并将其与我们传递的参数 id 进行比较,如下所示

return table.SingleOrDefault<t>
    (delegate(T t)
{
    int memberId = (int)t.GetType().GetProperty(pk).GetValue(t, null);
    return memberId == id;
});   

当使用上述方法时,由于我们使用反射来查找值,因此会产生性能损失。 但这是我为了避免编写重复代码而做出的权衡。

让我们也看看其他三个 BaseRepository 方法

public void Add<t>(T item) where T : class
{
    var table = school.GetTable<t>();
    table.InsertOnSubmit(item);
}

public void PersistAll()
{
    school.SubmitChanges();
}

public void AddAndPersistAll<t>(T item) where T : class
{
    var table = school.GetTable<t>();
    table.InsertOnSubmit(item);
    PersistAll();
}

Add<t>(T item) 方法将项目添加到表集合,但不将其提交到数据库。 PersistAll 方法将项目提交到数据库。 最后,AddAndPersistAll 方法添加并将项目保存在数据库中。

结论

在本文中,我们讨论了在线考试应用程序的架构。 在下一篇文章中,我们将编写一些单元测试来测试我们的域层和存储库。

下载将在本系列的下一部分中提供。

© . All rights reserved.