5

I'm doing some custom code for a SharePoint webpart in C#. Specifically, I'm making a quiz, my main point here addressing the list that holds the question, answer choices, and correct answer.

At the last stage of the quiz I need to check the answers selected by the user against the correct answer in the list. Currently, I'm doing the following to check if each is correct, which I'm assuming isn't very efficient because it iterates through each question. Is there a method, specifically for the SPList foreach loop, that would be more efficient?

                // 1. Store questions and answers in class
                    List<submittedAnswers> answeredQuestions = new List<submittedAnswers>();

                // 2. From POST pull answered question IDs and answer IDs (which correspond to the question primary key and answer choice number both stored in the list)
                    // INSERT BEAUTFIUL AND EFFICIENT WHILE LOOP HERE

                // 3. Loop through each question is list, if question was given, test if correct/incorrect
                using (SPWeb myWeb = mySite.OpenWeb())
                {
                    SPList answerList = myWeb.Lists[questionList];
                    foreach (SPListItem quizEntry in answerList.Items)
                    {
                        int pullAnswerId = int.Parse(quizEntry["Answer"].ToString()); // Pull answer number from list
                        int pullQuestionId = int.Parse(quizEntry["ID"].ToString()); // Pull primary key of question

                        submittedAnswers result = answeredQuestions.Find(delegate(submittedAnswers e) { return e.questionId == int.Parse(quizEntry["ID"].ToString()); });
                        if (result != null)
                        {
                            if (result.responseId != pullAnswerId) // If the response was different from the answer
                                incorrectAnswers++;
                            else
                                correctAnswers++;
                        }
                    }
                }
                // C# quiz grading magic here....
Eric Di Bari
  • 3,767
  • 7
  • 40
  • 49
  • Where is this code running (event receiver? webpart?) Are you trying to avoid grading each quiz every time? Not sure why you are trying to optimize... – Kit Menke Mar 08 '11 at 19:34
  • The quiz is running in a web part, and I have two concerns: 1. When we release the quiz it will likely have a spike maybe a few hundred people taking it and I want to minimize bottlenecks. 2. This is the first ASP.NET programming I've ever done and since I mainly work in PHP, OO designs and programming throw me off. Additionally, I've read a few blogs that say this method of iterating through the list is very inefficient. – Eric Di Bari Mar 08 '11 at 19:40
  • Well if your "Questions" list holds all of the questions that appear on the quiz and you need to validate each user's answer then I'd say a foreach is OK. – Kit Menke Mar 08 '11 at 20:14
  • That sounds like the answer I'm looking for. Too bad I can't mark your comment as an answer... – Eric Di Bari Mar 08 '11 at 20:50

4 Answers4

3

If you need to access every item in the list, then using a foreach is perfectly ok:

SPList answerList = myWeb.Lists[questionList];
foreach (SPListItem quizEntry in answerList.Items)
{
    // todo...
}

Generally, most people need to work with a subset of items from a list. In this case, you'd most likely want to retrieve a subset of items from the list and then do your processing. For example, by using SPQuery and SPList.GetItems (code from the full example here):

// Build a query.
SPQuery query = new SPQuery();
query.Query = string.Concat(
                    "<Where><Eq>",
                        "<FieldRef Name='Status'/>",
                        "<Value Type='CHOICE'>Not Started</Value>",
                    "</Eq></Where>",
                    "<OrderBy>",
                        "<FieldRef Name='DueDate' Ascending='TRUE' />",
                        "<FieldRef Name=’Priority’ Ascending='TRUE' />", 
                    "</OrderBy>");                    

query.ViewFields = string.Concat(
                          "<FieldRef Name='AssignedTo' />",
                          "<FieldRef Name='LinkTitle' />",
                          "<FieldRef Name='DueDate' />",
                          "<FieldRef Name='Priority' />");

query.ViewFieldsOnly = true; // Fetch only the data that we need.

// Get data from a list.
string listUrl = web.ServerRelativeUrl + "/lists/tasks";
SPList list = web.GetList(listUrl);
SPListItemCollection items = list.GetItems(query);

FYI... Here is a good link going over some other options: https://www.nothingbutsharepoint.com/sites/devwiki/SP2007Dev/Pages/Accessing%20list%20items%20using%20the%20object%20model.aspx

SharePoint has a lot of tools. It is always worth determining which tool is right for the job at hand. :)

Kit Menke
  • 7,046
  • 1
  • 32
  • 54
  • If the list contains maybe 200-300 entries, do you think it's more efficient to query the list with SPQuery (to pull the 20-30 I need), or just use foreach to iterate through each? I like your comment that 'SharePoint has a lot a tools'. Overall, I find it difficult to use, largely because I can't find any decent documentation source. – Eric Di Bari Mar 09 '11 at 15:42
  • You should definitely use SPQuery then. When you iterate using a foreach over `SPList.Items` it'll retrieve ALL of the items in the list from the db. You will first want to use `SPQuery` to get the subset of items and **then** use the foreach to iterate over `SPListItemCollection items` returned from `list.GetItems(query)`. – Kit Menke Mar 09 '11 at 16:08
  • What import statement do I have to use? – SearchForKnowledge Aug 03 '15 at 16:24
  • 1
    @SearchForKnowledge [SPListItemCollection](https://msdn.microsoft.com/library/Microsoft.SharePoint.SPListItemCollection) is in the Microsoft.SharePoint namespace. You'll need to reference the Microsoft.SharePoint.dll which is a part of the server side API. – Kit Menke Aug 06 '15 at 14:47
1

This may seem dumb but instead of using a class to store the variables why not use a simple array? Or an even easier way would be to write an sql query to compare the answer array to the answers stored in the database. Your solution seems to be way to much OO wizardry but maybe its just me.

Also look at answer Performance of Arrays vs. Lists

It looks like it would be faster to use a for loop than a foreach loop to iterate over a list.

Community
  • 1
  • 1
Zen Devel
  • 21
  • 2
  • I don't think your answer applies to using a foreach with an SPList (a SharePoint object). Also, using a for loop with an SPList.Items is OK as long as you do it correctly: http://blog.dynatrace.com/2009/01/11/the-wrong-way-to-iterate-through-sharepoint-splist-items/ – Kit Menke Mar 09 '11 at 14:46
  • @Kit Menke, that's a good point I was looking at the algorithm and not the API. – Zen Devel Mar 09 '11 at 16:01
1

I would imagine using the SpList.GetItems with a correctly formatted query will give you the number of correct answers.

The U2U CAML Query Builder tool may be of use in getting a correct query.

Nat
  • 14,175
  • 5
  • 41
  • 64
0

Why not use LINQ in Sharepoint? Its really easy and no need to loop

i.e

SPLinqDataContext dc = new SPLinqDataContext(SPContext.Current.Web.Url);

EntityList<QuizItem> Answers = dc.GetList<QuizItem>("Quiz");
EntityList<QuestionsItem> Questions = dc.GetList<QuestionsItem>("Questions");

int iCorrectAnswers = (from q in Questions
            from a in Answers
            where (q.Question == a.Question) && (q.CorrectAnswer == a.Answer)
            select a).Count();

int iWrongAnswers = (from q in Questions
                        from a in Answers
                        where (q.Question == a.Question) && (q.CorrectAnswer != a.Answer)
                        select a).Count();

here is a guide to use LINQ in Sharepoint

Raymund
  • 7,684
  • 5
  • 45
  • 78