1

I am writing an Event Receiver on a Document Library to create a new list from the uploaded excel file. it runs on ItemAdded() and ItemUpdated() event.

Before calling a method to add new Items to List I need to clear old Items, I initially tried this with the traditional way (iterating in descending order and delete all records) and now with the ProcessBatchData

 using (SPSite site = new SPSite(properties.WebUrl))
                        {
                            if (site.OpenWeb().Lists[listName].Items.Count > 0)
                            {
                                var deleteBatch = site.OpenWeb().ProcessBatchData(BuildBatchDeleteCommand(site.OpenWeb().Lists[listName]));
                            }
                            InsertIntoList(ExcelRecords, site.OpenWeb().Lists[listName].Items);
                        }

ProcessBatachData is some what better but still not accurate the issue is that while debuggin once the control reaches the line var deleteBatch = site.OpenWeb().ProcessBatchData(BuildBatchDeleteCommand(site.OpenWeb().Lists[listName]));

It takes the control back to the document upload screen and most of the times doesn't hit the remaining code, However if records are really low ( < 100) then it process and takes the control to the next line ( InsertIntoList(ExcelRecords, site.OpenWeb().Lists[listName].Items); ).

Not able to figure out if its an thread issue, If I make this work my putting thread to sleep for sometime until the ProcessBatch finishes its job and control move to next line.

The complete function looks like

 public override void ItemUpdated(SPItemEventProperties properties)
        {
            UpdateMarketingCalendar(properties);
            base.ItemUpdated(properties);
        }


private void UpdateMarketingCalendar(SPItemEventProperties properties)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                try
                {
                    using (SPSite site = new SPSite(properties.WebUrl))
                    {
                        SPList list = site.OpenWeb().Lists[listName];

                        bool recordsRetrieved;
                        DataTable ExcelRecords = GetRecordsFromExcel(properties.ListItem, out recordsRetrieved);

                        if (recordsRetrieved)
                        {
                            if (list.ItemCount > 0)
                            {
                                var deleteBatch = site.OpenWeb().ProcessBatchData(BuildBatchDeleteCommand(list));
                            }
                            InsertIntoList(ExcelRecords, list.Items);
                        }
                    }
                }

            });
        }

Geeks please point me to the right direction. I really appreciate you help and time.

Vishal

  • Aside from Robert's advice I would also see if you can move this potentially-long-running code out of the SPListItem event receiver as it is primarily meant or short lived stuff. If you want to automatically generate the list from an excel, could perhaps use a workflow instead ? This will not get in the way of the user, and can also be made to run on a newly uploaded item's event. – Cameron Verhelst Feb 26 '14 at 15:41
  • Yepp, or use an SPLongTimeOperation: withinsharepoint.com/archives/39 for how to combine event receiver with Long Running Operation – Robert Lindgren Feb 26 '14 at 15:49
  • You so still have two memory leaks in your code, opening webs without disposing them.. – Robert Lindgren Feb 26 '14 at 18:15
1

This is probably not the root cause of your performance issues, but part of it. Your code is written in a quite inefficient way, opening webs to the right and left and looking for the list multiple times. Something like this does the same thing but more efficiently:

If your feature is web scoped:

    var web = properties.Feature.Parent as SPWeb;

if site scoped

    var site = properties.Feature.Parent as SPSite;
    if(site == null) return;
    var web = site.OpenWeb(properties.WebUrl);

and as MdMazzotti pointed out you would actually need to dispose the web in this scenario, I would use the using statement, replace the last line with this:

using(var web = site.OpenWeb(properties.WebUrl)){

and add an extra } at the end of the following :

    if(web == null || !web.Exists) return;

    var list = web.Lists[listName]
    if (list != null && list.Items.Count > 0)
    {
        var deleteBatch = web.ProcessBatchData(BuildBatchDeleteCommand(list));
    }

    InsertIntoList(ExcelRecords, list.Items);
}
  • Not sure if declaring and further using Var's would make much difference to return time, would it ? – foo-baar Feb 26 '14 at 15:42
  • 1
    I think it will be even better to get SPSite and SPWeb objects directly from properties object since it is an event reciver. Something like SPSite oSite = properties.Feature.Parent as SPSite; SPWeb oWeb = oSite.RootWeb; – Nadeem Yousuf Feb 26 '14 at 15:42
  • @VishalSachdeva, using vars by itself will not, but avoiding opening a lot of webs and iterating over all lists multiple times (yes that is what SP does when you call Lists["listName"]) will. – Robert Lindgren Feb 26 '14 at 15:44
  • @NadeemYousuf, probably! Since he is not modifying the web directly I see no harm in that :), Will update my post – Robert Lindgren Feb 26 '14 at 15:45
  • @Robert Lindgren thanks for valuable advice I'll quickly change my code and see if any luck, BTW any thing from top of your mind about putting thread to sleep before the request completes ? – foo-baar Feb 26 '14 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.