SharePoint Internals – Hristo Pavlov’s Blog

24 February, 2009

Understanding SharePoint: Event Receivers

This blog post shows some details about how the event receivers in SharePoint work and also provides a solution how to wait for an ItemAdded event receiver to complete before the EditForm.aspx page is displayed when uploading a document. The solution could be used to wait for any asynchronous event receiver when you are changing the list using code.

All list item, list and web event receivers in SharePoint with the exception of the ListItemFileConverted receiver are run from the SPRequest unmanaged class. There are two types of event receivers – synchronous (such as ItemAdding and ItemUpdating) and asynchronous (such as ItemAdded and ItemUpdated). Because the event receivers are implemented in managed code, the unmanaged SPRequest class needs a way to invoke them. This is done via the ISPEventManager COM interface which is implemented by the Microsoft.SharePoint.SPEventManager internal class.

[ComImport, SuppressUnmanagedCodeSecurity, InterfaceType((short)1), Guid(“BDEADF0F-C265-11D0-BCED-00A0C90AB50F”)]

public interface ISPEventManager

{

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void ExecuteItemEventReceivers(

        ref byte[] userToken, ref objecteventReceivers, ref ItemEventReceiverParams itemEventParams,

        out objectchangedFields, outEventReceiverResult eventResult, out stringerrorMessage);

 

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void EnqueueItemEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref ItemEventReceiverParams itemEventParams);

 

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void ExecuteListEventReceivers(

        ref byte[] userToken, ref objecteventReceivers, ref ListEventReceiverParams ListEventParams,

        outEventReceiverResult eventResult, out stringerrorMessage);

 

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void EnqueueListEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref ListEventReceiverParams ListEventParams);

 

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void ExecuteWebEventReceivers(

        ref byte[] userToken, ref objecteventReceivers, ref WebEventReceiverParams webEventParams,

        outEventReceiverResult eventResult, out stringerrorMessage);

 

    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

    void EnqueueWebEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref WebEventReceiverParams webEventParams);

}

As we can see from the interface all event receivers are passed to be executed in one call and there are two types of methods in the ISPEventManager class: EnqueueEventReceivers – used for the asynchronous ones and ExecuteEventReceivers – used for the synchronous.

So because the unmanaged code will make a call back to the managed SPEventManager class to run the event receivers, the first important question is which process will those event receivers run in. Well the answer is both synchronous and asynchronous event receivers will execute in the same process that has made the change that resulted in the event receivers being called. So for example if you are adding a new file into a document library from a web part, then the event receivers will execute in the w3wp.exe process of the application pool of the web application. If you are adding the file from a rich client (MyWinFormsApp.exe) then the event receivers, including the asynchronous ones will execute in the MyWinFormsApp.exe process.

The asynchronous event receivers may need some time to complete (could be a lot of time) and if you close your MyWinFormsApp.exe immediately after you have added the file, and while the event receivers are still running, what will happen is that they will be terminated and some of them may not run at all.

The situation with IIS is not very different unfortunately. If you do an iisreset without the /noforce switch then all currently running asynchronous event receivers will be terminated immediately. My tests showed that executing iisreset /noforce will indeed wait for an asynchronous event receiver to finish however if you are trying to save any changes to the item for example, those changes will not survive.

So this means that by doing iisreset you may be interrupting a running asynchronous (or synchronous) event receiver and corrupting your application integrity/state. When executing iisreset /noforce the event receivers will complete but they may not be able to update any of the SharePoint objects they could be trying to manipulate. This finding it actually very alarming! I wouldn’t recommend to anyone writing any event receiver that will take more than 1 second to complete as an iisreset may corrupt your application!

The asynchronous event receivers will be run in their own thread from the System.Threading.ThreadPool. And a new thread will be queued up with every call from the unmanaged SPRequest class. This means that if you add say 100 files to a document library this will result in 100 calls to EnqueueItemEventReceivers for each of the items. And this will create and en-queue 100 threads each of which will run one by one all asynchronous event receivers for the corresponding file. Now if some of those event receivers are taking some time to complete, which is a few times more than the time it takes you to add a file, what will happen is you will end up with enormous amount of running threads.

Switching between threads is a very resource intensive operation and if you have 100 threads running in your application or in IIS, this will pretty much slow down the process to a crawl. I’ve seen this happening with a custom built migration tool that moves documents from one document library to another. There were some heavy ItemAdded event receivers and after few hundreds files were moved for a minute or so, it took another 45 minutes for the event receivers to complete. The migration would have been faster if after adding each file the migration tool was waiting for the event receivers to complete because after adding all the files at once 90+% of the time the process was spending in switching between the few hundred threads and only up to 10% were the actual threads running the event receivers.

The next interesting question is what is the lifespan for the SPItemEventProperties that are passed to all item event receivers (synchronous and asynchronous). Well looking at the SPEventManager class code it turns out that the SPItemEventProperties are created once per batch of events receivers and are shared among all of them. So this means that all synchronous event receivers will share the same properties and all asynchronous event receivers will share different instance of the properties. However the unmanaged SPRequest class will keep the changes to the properties done by the synchronous event receivers and will pass them to the asynchronous event receivers.  This means that it is generally possible to store information in the SPEventProperties from some event receivers that could be passed to other event receivers executed later on.

This could only be done from an ItemAdding or ItemUpdating event receiver. You could add anything you want to the AfterProperties collection and the information you have added will be availabe as AfterProperties in the ItemAdded and ItemUpdated event receivers.

A question that have been bothering me for a while is should the web retrieved by SPItemEventProperties.OpenWeb() be disposed by the event receiver that have opened it. The answer as always lies in the implementation details of the class (in out case SPItemEventProperties) that can be retrieved using Reflector.

public sealed class SPItemEventProperties : SPEventPropertiesBase, IDisposable

{

   

 

    private SPSite OpenSite()

    {

        if (((this.m_site == null) && (this.WebUrl != null)) && (this.m_site == null))

        {

            if (this.m_userToken == null)

            {

                this.m_site = new SPSite(this.WebUrl);

            }

            else

            {

                this.m_site = new SPSite(this.WebUrl, this.m_userToken);

            }

            this.m_siteCreatedByThis = true;

        }

        return this.m_site;

    }

 

   

 

    public SPWeb OpenWeb()

    {

        this.OpenSite();

        if (this.m_site == null)

        {

            return null;

        }

        return this.m_site.OpenWeb(this.RelativeWebUrl);

    }

 

   

 

    public void Dispose()

    {

        if (this.m_site != null)

        {

            while (this.m_siteCreatedByThis)

            {

                this.m_site.Dispose();

                this.m_site = null;

                this.m_siteCreatedByThis = false;

                break;

            }

        }

    }

}

So the SPItemEventProperties class actually implements IDisposable and disposes the allocated site or web. The SPEventManager class makes sure to call Dispose() after the batch of event receivers have executed.

Waiting for ItemAdded to Complete Before Showing EditForm.aspx

There is an issue with document libraries and the Upload.aspx page as descibed in my previous blog entry. The problem will be encountered if you have an ItemAdded event receiver attached to the document library that updates fields of the list item/document after it has been created. Because the ItemAdded event receiver runs asynchronously it may update the list item after the EditForm.aspx page has been displayed. This would result either in the EditForm.aspx form to not show the updated fields or in an exception to be generated when you click OK.

In order to solve the problem we need some code to wait for the event receiver to complete before loading the EditForm.aspx page. One way to do this is to customize the EditForm.aspx page either on a Content Type level or a List level or just using SharePoint designer and to insert a web control that will wait for the ItemAdded event receiver to complete. We also need a special ItemAdded event receiver which the web control will be able to communicate with.

And the good news for you is that I did the hard work or creating both the base class for the event receiver and the web control. They can be donwloaded here:

SharePointInternals.SynchronousItemAdded.dll

SharePointInternals.SynchronousItemAdded – Source Code

To use them you need to create your event receiver class to inherit from the SPSynchronousReceiver class and override the ItemAddedSynchronously method:

public class SPTestReceiver : SPSynchronousReceiver

{

          

    protected override void ItemAddedSynchronously(SPItemEventProperties properties)

    {

        // Your code goes here

    }

 

 

    // Your other item receiver overrides go here   

 

}

Then you need to insert the WaitForItemAdded web control in the EditPage.aspx. The best place to do this is as a first control in the PlaceHolderMain.

<%@Register TagPrefix=”SharePointInternals”

            Assembly=”SharePointInternals.SynchronousItemAdded, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d7dbdc19a16aed51″

            namespace=”SharePointInternals.WebControls”%>

 

 

 

<asp:Content ContentPlaceHolderId=”PlaceHolderMain” runat=”server”>

    <SharePointInternals:WaitForItemAdded ID=”waitForItemAdded1″ runat=”server”/>

 

   

   

</asp:Content>

Unfortunately this is the only way to integrate the WaitForItemAdded control. If we add the web control to the ListFormTemplate used by the ListFormWebPart from EditForm.aspx, then it will wait for the event receivers to complete, but at this stage the ListItem would have been already loaded in the SPContext and the EditForm.aspx will show the field values of the list item before the ItemAdded event receivers have completed. If any of them have updated the list item then pressing OK on the EditForm.aspx will actually cause an exception:

The file … has been modified by … on …

In order to get around the problem that the ListItem from the SPContext have been initialized we should put the WaitForItemAdded web control somewhere in the EditForm.aspx before the ListFormWebPart and before any other control that may be using SPContext.Current.ListItem. Accessing this property will load the list item which may happen before the asynchronous event receivers have completed.

You can also wait for the ItemAdded event receivers to complete when adding a file using code. This could be handy when doing migration of documents. In order to wait for the event receiver to complete just call SPSynchronousReceiver.WaitForItemAddedReceivers().

using (SPSite site = new SPSite(http://server/sites/test&#8221;))

using (SPWeb web = site.OpenWeb())

{

    SPList list = web.Lists[“Shared Documents”];

 

    SPFile file = list.RootFolder.Files.Add(fileName, fileBytes);

 

    SPSynchronousReceiver.WaitForItemAddedReceivers(list, file.Item.ID);

}

Of course this will only wait for those event receivers that inherit from the SPSynchronousReceiver class. If you need to do the same for another asycnhronous event receiver, this could be easily done by changing the code of the base class.

And for completeness I also want to mention that the ListItemFileConverted event receivers execution is initiated from managed code when SPFile.Convert() is called. This is the only exception and the execution of all other event receivers is initialized from unmanaged code. The ListItemFileConverted event receivers run asynchronously.

Finally to recap our findings. The execution of all event receivers with the exception of the ListItemFileConverted will be initiated from unmanaged code. This happens when the SharePoint object model calls the corresponding method from the SPRequest unmanaged object to perform the operation that will trigger the event receivers. Then the unmanaged WSS world will use callbacks on the ISPEventManager COM interface implemented by the Microsoft.SharePoint.SPEventManager class to start the event receivers execution on batches of synchronous and asynchronous event receivers. You don’t have to dispose the SPWeb returned by properties.OpenWeb() in your event receiver code. Sometimes it may be a better idea to create a fresh SPSite and SPWeb instead of using properties.OpenWeb(). It is hard to say which one is good for you as it all depends on the parallel processes that may be running and the other event receivers being executed. All asynchronous event receivers for the given object (list item, list of web) will be executed in a separate thread created on the ThreadPool. SharePoint will queue a new thread for every object. This could potentially cause the server or the application to slow down dramatically as it struggles to switch between the different threads.  The asynchonous event receivers run in the same process in which the object was changed that lead to the event receivers being executed. If you stop the application or reset IIS all running event receivers will be terminated and all scheduled event receivers will not be executed at all. If you do iisreset /noforce this will wait for the event receivers to complete, but they may not be able to update the SharePoint objects they are working with. When you upload a file to a document library there is a nasty problem that you will be redirected to the EditForm.aspx page before the ItemAdded event receivers have completed. If you are doing any changes to the item in the event receivers these changes will not show up when EditForm.aspx page loads. Even more if you press OK in the EditForm.aspx page after the event receivers have modified the item you will get an exception. There is workaround if you use the SharePointInternals.SynchronousItemAdded.dll as described in this blogpost.

15 Comments »

  1. […] Understanding SharePoint: Event Receivers […]

    Pingback by Links (2/26/2009) « Steve Pietrek - Everything SharePoint — 27 February, 2009 @ 12:42 am

  2. Since each call to properties.OpenWeb() returns m_site.OpenWeb(), I’m inclined to think that should be manually disposed since the reference will not be reused. Or a better option might be to use properties.ListItem.Web: the SPListItem is reused and its Web property always returns the same object.

    The dispose story is less clear for SPListEventProperties and SPWebEventProperties, especially since they don’t implement IDisposable. Can you find anywhere in the framework that suggests their internal web and site reference are disposed properly?

    Cheers ~
    Keith

    Comment by Keith Dahlby — 28 February, 2009 @ 9:10 am

  3. Hi Keith,

    Yes what you are saying is correct. properties.OpenWeb() will open a new SPWeb which is IDisposable. However when the properties.m_site gets disposed automatically by the SPEventManager, this will also dispose all opened webs created by properties.OpenWeb().

    The reason to dispose SPSite or SPWeb objects is that they may contain a reference to an instance of the SPRequest unmanaged class. While every SPSite has a reference to an SPRequest this is not the case with the SPWebs. When you call SPSite.OpenWeb(relativeUrl) as in the case here, there is actually no SPRequest associated with the returned open web. So not disposing it is not something really bad i.e. there are no leaked resources. To see that there is no SPRequest created with the opened web have a look at the SPWeb(SPSite site, string url, bool bExactWebUrl) constructor that actually calls SPWeb.SPWebConstructor(site, url, bExactWebUrl, null).

    I’ll probably do a special post on IDisposable implementation of SPSite and SPWeb and tackle those interesting cases.

    Hristo.

    Comment by hristopavlov — 1 March, 2009 @ 10:27 am

  4. If the request passed to SPWebConstructor is null, m_RequestOwnedByThisWeb will be true and Request will create its own via EnsureSPRequest that is cleaned up in Invalidate. Whether or not an SPRequest is allocated depends entirely on what is done with the object (though most interesting operations require one). The SPRequest-accepting SPWeb constructor is only used by SPSiteCollection.Add, SPSite.CreateWeb and SPWeb.CreateWeb, and in each the resulting SPWeb seems to be closed and reopened as a new SPWeb that owns its request.

    So in the context of SPItemEventProperties, we know that an SPWeb is allocated internally by ListItem which will not be disposed until the SPEventManager cleans up m_site. The question is if you want memory allocated for the other requests created with properties.OpenWeb() to stay open as well. If memory pressure is a problem, the answer is probably no, in which case they should either be proactively disposed or replaced with ListItem.Web.

    Or am I missing something?

    Comment by Keith Dahlby — 1 March, 2009 @ 10:20 pm

  5. You are right about the SPRequest being created Keith. I didn’t spend much time looking into it yesterday before sending my reply, which is a shame.

    So as you say an SPRequest may be created with each SPWeb depending on what you are doing with this web. In many cases such as if you are using lists, list items, content types – there will be an SPRequest created.

    On the other hand not dosposing the SPWebs in this particular case will not cause unmanaged memory leaks and is not really a mistake. Those SPWebs will be dispoased by the SPEventManager when all event receivers have completed. But I agree that proactively disposing those webs will free some extra memory for the duration of when the event receivers are being executed.

    Comment by hristopavlov — 2 March, 2009 @ 12:23 am

  6. Hi,

    Please, examine the following log. It seems that some memory leak exists…

    An SPRequest object was reclaimed by the garbage collector instead of being explicitly freed.
    To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.
    Allocation Id: {20357A0E-10A9-4670-B429-FA3C926086DB}
    This SPRequest was allocated at
    at Microsoft.SharePoint.Library.SPRequest..ctor()
    at Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous)
    at Microsoft.SharePoint.SPWeb.InitializeSPRequest()
    at Microsoft.SharePoint.SPWeb.EnsureSPRequest()
    at Microsoft.SharePoint.SPWeb.get_Request()
    at Microsoft.SharePoint.SPListCollection.EnsureListsData(Guid webId, String strListName)
    at Microsoft.SharePoint.SPListCollection.EnsureListsData(String strListName)
    at Microsoft.SharePoint.SPListCollection.ItemByInternalName(String strInternalName, Boolean bThrowException)
    at Microsoft.SharePoint.SPListCollection.GetListById(Guid uniqueID, Boolean bThrowException)
    at Microsoft.SharePoint.SPListCollection.get_Item(Guid uniqueID)
    at Microsoft.SharePoint.SPItemEventProperties.get_ListItem()
    at [package name].ReceiversItemEventReceiver.ItemCheckingOut(SPItemEventProperties properties)
    at Microsoft.SharePoint.SPEventManager.RunItemEventReceiver(SPItemEventReceiver receiver, SPItemEventProperties properties, SPEventContext context, String receiverData)
    at Microsoft.SharePoint.SPEventManager.RunItemEventReceiverHelper(Object receiver, Object properties, SPEventContext context, String receiverData)

    Comment by Zsolt Borcsok — 3 August, 2009 @ 9:04 am

  7. Hi ,

    I can get the session object from inside ItemAdding Event if the user try to upload one document, but the problem is the httpcontext.current is always null when the user upload multiple documents using the document libarary option ( upload multiple document )

    Comment by Mohammed barakat — 25 December, 2009 @ 11:05 am

  8. The HttpContext.Current Is Always Null When uploading multiple docs at the same time .

    I faced the same issue when I was tring to update some custom fields of my document library when uploading new documents, the field was ( ProjectID ) which I put it inside a session in my webpart ( the step before uploading the document).

    What I did is : I put the projectID into the cache ( per user ) inside the custom webpart which acts as a session as follows :

    if (Request.QueryString[“ProjectID”] != null)
    {
    HttpRuntime.Cache.Remove(SPContext.Current.Web.CurrentUser.LoginName);
    HttpRuntime.Cache.Add(SPContext.Current.Web.CurrentUser.LoginName, ProjectID, null, DateTime.UtcNow.AddMinutes(60), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
    }

    then I implemented the ItemAdded event and I get the value of the cached projectId through :

    public override void ItemAdded(SPItemEventProperties properties)
    {
    try
    {

    string ProjID = “”;

    string CreatedBy = null;
    if (properties.ListItem[“Created By”] != null)
    CreatedBy = properties.ListItem[“Created By”].ToString().Split(‘;’)[1].Replace(“#”,””);

    if (HttpRuntime.Cache[CreatedBy] != null)
    {
    //SPContext.Current.Web.CurrentUser.LoginName;
    ProjID = HttpRuntime.Cache[CreatedBy].ToString();

    if (properties.ListItem[“Project”] == null)
    {
    properties.ListItem[“Project”] = new SPFieldLookupValue(ProjID);
    properties.ListItem.SystemUpdate();
    }

    base.ItemAdded(properties);

    }

    }
    catch (Exception ex)
    { }

    }

    Comment by Mohammed barakat — 27 December, 2009 @ 10:11 am

  9. Hi Hrito,

    I was trying to make use of “SharePoitntenals.SynhronusItemAdded” dll to write code inorder to resize a picture while uploading to a SharePoint Image Library. I am hving hard time registering the event handler as in the rgular speventreciver we use the line – [imagelibrary.EventRecievers.Add(SPEventReceiverType.ItemAdded,assembly,eventclas) to rgister the event

    so similarly how can i make use of the metods or properties in this dll to get to the above line of code while rgistring the event.

    much appriciate your help,
    Thanks,
    Naresh

    Comment by Naresh — 2 March, 2010 @ 1:12 am

  10. I was getting the error “The file … has been modified by … on … ”
    Your comment below solved my problem.

    “This could only be done from an ItemAdding or ItemUpdating event receiver. You could add anything you want to the AfterProperties collection and the information you have added will be availabe as AfterProperties in the ItemAdded and ItemUpdated event receivers.”

    I wanted to populate “Control Number” (string) when uploading a new item to document library. Based on your comment I used ItemAdding instead of ItemAdded and inserted this:

    properties.AfterProperties[“Control Number”] = mystring; // 04/30/10

    Sure enough, when I uploaded a new document, the “Control Number” was in the EditForm and in the list.

    Thank-you for your excellent article.
    Gordon

    Comment by Gordon Laing — 30 April, 2010 @ 10:39 pm

  11. Hi,

    could u explain how I can use your ItemAddedSynchronously in my own Event Receiver? I am always getting the following error

    Requested value ‘ItemAddedSynchronously’ was not found. at System.Enum.Parse(Type enumType, String value, Boolean ignoreCase)
    at System.Enum.Parse(Type enumType, String value)
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.ConvertStringToEventReceiverTypeAsInt(String stringType)
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.GetOneEventReceiverFromXml(Object[,] receivers, Int32 i, XmlNode receiverXml)
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.GetEventReceiversFromXml(XmlNodeList receiversXml, SPEventHostType hostType, Int32& count, Object& receivers)
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.GetFeatureEventReceivers(Int32& count, Object& receivers)
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.EnsureEventReceivers()
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.get_Count()
    at Microsoft.SharePoint.SPEventReceiverDefinitionCollection.GetSqlToAddEventReceiversToList(SPList list, Byte[] sourceId, SPEventReceiverSource sourceType)
    at Microsoft.SharePoint.SPEventElement.AddEventReceiversForOneList(SPWeb web, SPList list)
    at Microsoft.SharePoint.SPListCollection.FixEventReceivers(Guid lid)
    at Microsoft.SharePoint.SPListCollection.CreateListFromRpc(NameValueCollection queryString, Uri& nextUrl)
    at Microsoft.SharePoint.ApplicationPages.NewListPage.BtnOk_Click(Object sender, EventArgs args)
    at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
    at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
    at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
    at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
    at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
    at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

    How can I register your EventReceiver correct?
    best regards Alex

    Comment by Alex — 24 November, 2010 @ 5:40 pm

  12. Excellent post; really good detective work there on the threading model for the event receivers as this is something I’ve been mulling over (but for different reasons).

    Comment by Charles Chen — 8 March, 2011 @ 4:07 am

  13. […] mine, but came across an excellent post by Hristo Pavlov (who unfortunately, no longer works in the SharePoint […]

    Pingback by Deep Insight on SharePoint Event Receivers « <CharlieDigital /> — 8 March, 2011 @ 4:14 am

  14. Great. Finally with this “Wait for Item added” I was able to resize the uploaded images without getting the error “The file … has been modified by … on …”

    Thanks man also for the source code.. I will definitivelly try to create the samekind of thing for the ItemUpdated handler.

    Comment by patrick imboden — 16 June, 2011 @ 9:20 am

  15. Is there any throttling on the number of threads servicing event receivers? It seems that at peak times the system could get bogged down.

    Comment by perranman — 20 October, 2011 @ 3:00 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.