Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a Multiple Choice Exam Using DLINQ - Part 1

0.00/5 (No votes)
14 Jan 2008 1  
An article on creating a multiple choice exam using DLINQ (Part 1)

Introduction

A few months ago, I wrote an article in which I talked about creating an online exam using NHibernate. In this article, I will use LINQ to Classes to create the online exam. This is a multi-series article and in this part, I will discuss the architecture and the design of the application.

Application Requirement

The client needs an application where students can log in and give the exam. The exam is uploaded using an XML file provided by the client. The user will take the exam, then view his scores.

I will not discuss the user login and authentication section and will focus on the exam modules.

Database Design

The database name is School and it consists of five tables. Take a look at the database diagram shown below:

  • Users: This table holds the users.
  • Exams: This table holds the exams.
  • Questions: This table holds the questions of the exams.
  • Choices: This table holds the choices of the questions.
  • UserExams: This table holds the user grade and score of the exams.

Class Diagram

LINQ to Classes allows you to create classes using the database tables. Simply, drag and drop the table from the server explorer on the design view and it will automatically create the class diagram. Take a look at the entity class diagram below:

Apart from the entity class diagram, I have also included the Repository and Services class diagram. This one is a big diagram so don't be scared!

Let me explain the architecture of the repositories. Each aggregate root has its own repository. Exam has ExamRepository, User has UserRepository etc. There are no repositories for Question and Choices. This is because they are handled by the ExamRepository.

Each repository inherits from the particular repository interface and the BaseRepository class. Every concrete repository inherits from the base repository interface called IBaseRepository. This is because each concrete repository i.e. ExamRepository, UserRepository must expose some common methods like GetById, Add, PersistAll, GetAll. If I put these methods in the corresponding repository interfaces, then I have to implement those interfaces in the concrete repositories which means repetitive code.

The BaseRepository comes to the rescue and implements the common methods exposed by all the repositories. The methods are also marked as virtual so they can be overridden in the sub repositories.

Let�s take a look at the IBaseRepository interface:

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

As you can see, the IBaseRepository interface works with the generic type T. I will explain the purpose of using the constraints later in this article. Now, let�s see the implementation of the generic GetById method implemented in the BaseRepository class:

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

The GetById method above looks a little complicated but I will try my best to explain it. First, let�s talk about the constraint as shown below:

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

The constraint says that the type T must be a class. This is because the method school.GetTable<t>() only works on the reference types. The purpose of GetById method is to return the object based on its Id. The problem is that each entity class has a different name for its primary key column. User class has UserID, Exam class has ExamID and so on. This makes things more complicated as we have to find the name of the column which serves as the primary key. For this, I am using the following code which finds all the columns of the table and then finds the column name which serves as the 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;

Once we get the primary key, we can extract the value out of that column and compare it with our passed parameter id as shown below:

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

There is a performance hit when using the above approach since we are using reflection to find the value. But this was a trade off I took over writing repetitive code.

Let�s also take a look at the other three BaseRepository methods:

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

The Add<t>(T item) method adds the item to the table collection but does not commit it to the database. The PersistAll method commits the item to the database. Finally, the AddAndPersistAll method adds and persists the item in the database.

Conclusion

In this article we discussed the architecture of the online exam application. In the next article, we will write some unit tests to test our domain layer and the repositories.

The download will be provided in the next part of this series.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here