views:

941

answers:

5

I have a SPListItemCollection with ~500 items in. I am looping through using a for each loop, each time grabbing the file and writing it out to a pdf document. I am getting the error "Thread was being aborted". It looks to be a time out issue but I dont know how to fix it? Collections with 200-300 items work fine. Is there any way I can stop getting this error. If I cant increase the timeout I will need to batch it up.

Update

I have tried splitting up the processing in batches of 100 items. With each new 100 items using a new spweb and site and pulling the items from sharepoint. This is all done within the same method (so I am not recreating my custom object) however the same error is still occuring...

A: 

Iterating through an SPListItemCollection is never a good idea (but sadly sometimes the only way to go). You could consider wrapping up this code in a LongRunningOperation. Here's some code I adapted from some of my own:

ACTUAL CLASS:

using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.SharePoint;

namespace Common.LongRunningJobs
{
  /// <summary>
  /// Provides a long running job wrapper around converting multiple files
  /// </summary>
  [CLSCompliant(false)]
  public class FileToPdfLongRunningJob : LongRunningOperationJob
  {
    private List<string> fileUrls;

    /// <summary>
    /// Initializes a new instance of the <see cref="FileToPdfLongRunningJob"/> class.
    /// </summary>
    /// <param name="urls">The urls of the files to create pdfs from.</param>
    public FileToPdfLongRunningJob(List<string> urls)
    {
      fileUrls = urls;
    }

    /// <summary>
    /// Does the actual work of converting the files, while providing the user with status updates.
    /// </summary>
    public override void DoWork()
    {
      try
      {
        using (var currentWeb = Site.OpenWeb(parentWeb))
        {
          OperationsPerformed = 0;

          foreach (var url in fileUrls)
          {
            SPFile file = currentWeb.GetFile(url);

            // DO PDF OUTPUT

            StatusDescription = string.Format(CultureInfo.InvariantCulture, "busy converting {0} van {1}, file: {2}...", (OperationsPerformed + 1), TotalOperationsToBePerformed, file.Name);
            UpdateStatus();

            OperationsPerformed++;
          }
        }
      }
      catch (Exception ex)
      {
        // LOG ERROR
      }
    }
  }
}

USING ABOVE CLASS:

private void ConvertFiles()
{
  const string PROGRESS_PAGE_URL = "/_layouts/LongRunningOperationProgress.aspx";
  var urls = new List<string>();
  foreach (SPListItem item in yourlistitemcollection)
  {
    urls.Add(item.File.Url);
  }
  var longRunningJob = new FileMoveLongRunningJob(urls)
  {
    Title = "Pdf conversion Long Running Job",
    TotalOperationsToBePerformed = urls.Count,
    RedirectWhenFinished = true,
    NavigateWhenDoneUrl = urlToRedirectTo;//i.e. SPContext.GetContext(Context).List.RootFolder.ServerRelativeUrl
  };

  longRunningJob.Start(SPContext.Current.Web);
  string url = string.Format("{0}{1}?JobId={2}", SPContext.Current.Web.Url, PROGRESS_PAGE_URL, longRunningJob.JobId);
  SPUtility.Redirect(url, SPRedirectFlags.Default, Context);
}
Colin
Did not seem to work for me. I need to use the same object to write to my pdf file. I tried passing this to FileToPdfLongRunningJob and still get the same error...
Rupert
A: 

Keep in mind there is a right and a very wrong way to iterate over list items, and it makes a huge difference in performance. The wrong way will cause n+1 calls to the database for the list data; the right way only makes a single call to the database.

Worst:

for (int i = 0; i < myList.Items.Count; i++)
{
  SPListItem thisItem = myList.Items[i];

  // yada yada yada
}

Bad:

foreach (SPListItem item in myList.Items)
{
  // yada yada yada
}

Right:

SPListItemCollection myItems = myList.Items;
foreach (SPListItem item in myItems)
{
  // yada yada yada
}

Updated: To reflect more accurate guidance

Greg Hurlman
I am doing it the right way which is good.
Rupert
Greg - I think you are wrong here and the two examples are functionally the same. Check the article you quote, hes talking about using calls like SPListItem item = myList.Items[i] in the innerloop, NOT a For Eachhttp://blog.dynatrace.com/2009/01/11/the-wrong-way-to-iterate-through-sharepoint-splist-items/
Ryan
@Ryan, I wasn't quoting any articles; that's code I've written a million times over.
Greg Hurlman
Me bad - you've quoted it in the past when talking about iterating through SPListITemCollection. My point still stands though, why is foreach (SPListItem item in myList.Items) bad?
Ryan
Greg Hurlman
Greg - both of those links are comparing different methods of accessing data like SPListItemCollection v GetData table. I can't see anywhere that supports your comments that foreach (SPListItem item in myList.Items) is bad. I will spend a bit of time tracing the db access on the two methods above but don't expect to see any difference (i.e. no n+1 calls for the first pattern). I am not having a go, I just want to get to the bottom of this.
Ryan
A: 

Try creating new SPSite and SPWeb objects before getting items (and get them from new webs).

Often times that solves the problem.

Janis Veinbergs
All ready creating a new SPSite and SpWeb before I loop through
Rupert
See logs. Maybe you have excessive number of SPWeb/SPSite objects which you don't dispose or some other bug that would cause SPRequest objects reach their limits.
Janis Veinbergs
In my batch looping I am disposing of the objects which I dont need. This is getting frustrating now..
Rupert
Ohwell, then try using SPSiteDataQuery to query items or convert your SPListItemCollection to DataTable (.GetDataTable() method).
Janis Veinbergs
A: 

http://blogs.msdn.com/solutions/archive/2009/01/08/getting-around-thread-was-being-aborted-error-when-creating-ep-site.aspx

  1. In a basic text editor such as Notepad, open the web.config file for example %SYSTEMDRIVE%\Inetpub\wwwroot -or- %SYSTEMDRIVE%\Inetpub\wwwroot\wss\VirtualDirectories\80 folder

  2. Press CTRL + F to open the Find dialog box.

  3. Find the following tag:

  1. Replace it with this tag:

This seems to have got it working for me.

Rupert
A: 

There can be potential pit falls in the code that iterates list. It will be good if you can paste your code here. Also, its better to use Caml to query for list items and select only those fields that are required for your operation rather than all.

Faiz