Export (0) Print
Expand All

How to: Create a handler for the update event in app for SharePoint 2013

apps for SharePoint

Learn how to create and use a handler for the update event of an app for SharePoint.

Be familiar with How to: Update apps for SharePoint and the prerequisites and core concepts listed in it.

Note Note

Version numbering system: For consistency, this topic assumes that the app version numbers are 1.0.0.0, 2.0.0.0, 3.0.0.0, and so on. However, the logic and guidance applies no matter what your numbering system is.

For custom update logic, you can create a SharePoint remote event receiver that handles the app updated event. You should be conservative in using this technique. Use it only for updating steps that can’t be done any other way. Also, the things that should be avoided in an InstalledEventEndpoint should also be avoided in an UpgradedEventEndpoint. For example, adding a column to a list on the host web is dangerous if the app depends on the presence of the column, because users can delete such columns.

One scenario in which an UpgradedEventEndpoint handler is useful is when a computed field is added to a remote database. Suppose a City column is added, and its value is computed from an already existing Postal Code column. Your code in an UpgradedEventEndpoint handler could iterate through existing items in the database and fill in the value for the new City field based on the value of the Postal Code field and some external data source that maps postal codes onto cities. The change to the database schema itself is best done outside the UpgradedEventEndpoint handler by using the best practices of the database platform. For example, in an autohosted app, you could update the Microsoft Azure SQL Database with a data script. For more information, see Update remote components in an autohosted app.

For more details about how to create a handler for the app event, see Handling events in apps for SharePoint and How to: Create an app event receiver. The following procedure assumes that you have added the app event receiver item to your app for SharePoint project in Visual Studio.

To handle the app updated event the first time

  1. Open the AppEventReceiver.svc.cs file and add a conditional structure to the ProcessEvent method that tests whether the event that invoked the handler is the updated event. Your updating code goes inside this structure. If your code needs to access SharePoint, you can use the SharePoint managed code client object model (CSOM) or Representational State Transfer (REST) interface. Where the conditional structure is located in the method depends on how you have structured the other code in the method. Typically, it is a peer of similar conditional structures that test for the app installed and app uninstalling events. It may be within a conditional structure that tests certain properties or subproperties of the SPRemoteEventProperties object that is passed to ProcessEvent for null values or other invalid values. It may also be within a try block. The following is an example of the structure. The properties is an SPRemoteEventProperties object.

    if (properties.EventType == SPRemoteEventType.AppUpgraded)
    {
    }
    
    
  2. To use CSOM in the handler, add (within the conditional block) a using block that gets a ClientContext object by calling the TokenHelper.CreateAppEventClientContext method. Specify true for the second parameter to access the app web. Specify false to access the host web. If you need to access both, you will need two different client context objects.

  3. If your handler needs to access non-SharePoint components, put that code outside any client context blocks. Your code should be structured similarly to the following:

    if (properties.EventType == SPRemoteEventType.AppUpgraded)
    {
        using (ClientContext cc = TokenHelper.CreateAppEventClientContext(properties, true))
        {
            // CSOM code that accesses the app web
        }
        using (ClientContext cc = TokenHelper.CreateAppEventClientContext(properties, false))
        {
            // CSOM code that accesses the host web
        }
        // Other update code
    }
    
    
  4. To use the REST interface, your code uses other methods in the TokenHelper class to get an access token, which is then included in the requests it makes to SharePoint. For more information, see How to: Complete basic operations using SharePoint 2013 REST endpoints. Your code should be structured similarly to the following.

    if (properties.EventType == SPRemoteEventType.AppUpgraded)
    {
        TokenHelper.TrustAllCertificates();
        string contextTokenString = TokenHelper.GetContextTokenFromRequest(Request);
        if (contextTokenString != null)
        {
            contextToken = TokenHelper.ReadAndValidateContextToken(contextTokenString, Request.Url.Authority);
            accessToken = TokenHelper.GetAccessToken(contextToken, sharepointUrl.Authority).AccessToken;
    
           // REST code that accesses SharePoint
        }  
        // Other update code
    }
    
    
  5. To access SharePoint, your REST code also needs to know the URL of the host web or app web or both. These URLs are both subproperties of the SPRemoteEventProperties object that is passed to the ProcessEvent method. The following code shows how to get them.

    Uri hostWebURL = properties.AppEventProperties.HostWebFullUrl;
    Uri appWebURL = properties.AppEventProperties.AppWebFullUrl;
    

When you update an app for the second (or third, and so on) time, you may need to ensure that some update logic does not run multiple times on the same app instance. The following procedure shows you how.

To handle the app updated event on subsequent updates

  1. Open the AppEventReceiver.svc.cs file, and find the code that implemented update actions in the first update (from 1.0.0.0. to 2.0.0.0). Let's call this the previous update code. (This code is generally located after the authorization code that gets client context or access tokens.) Now that you are updating again (from 2.0.0.0 to 3.0.0.0), there will typically be actions in the previous update code here that you don't want to run again on the same app instance, but you do want it to run on a app instance that was never updated to 2.0.0.0 (in which case, the instance is now being updated all the way from 1.0.0.0. to 3.0.0.0).

  2. Wrap the previous update code in a conditional structure that tests for the previous version of the app instance and executes only if the code in the structure has not run before on this app instance. The previous version of the instance is stored in the PreviousVersion subproperty of the SPRemoteEventProperties object.

  3. Add your new update logic (for the update from 2.0.0.0 to 3.0.0.0) below this structure. The following is an example.

    Version ver2OOO = new Version("2.0.0.0");
    if (properties.AppEventProperties.PreviousVersion < ver2OOO)
    {
        // Code to update from 1.0.0.0 to 2.0.0.0 (previous update code) is here.
    }
    // Code to update from 2.0.0.0 to 3.0.0.0 is here.
    
    
  4. For each subsequent update, repeat these steps. For the update from 3.0.0.0 to 4.0.0.0, your code should have the following structure.

    Version ver2OOO = new Version("2.0.0.0");
    if (properties.AppEventProperties.PreviousVersion < ver2OOO)
    {
        // Code to update from 1.0.0.0 to 2.0.0.0 is here.
    }
    
    Version ver3OOO = new Version("3.0.0.0");
    if (properties.AppEventProperties.PreviousVersion < ver3OOO)
    {
        // Code to update from 2.0.0.0 to 3.0.0.0 (previous update code) is here.
    }
    // Code to update from 3.0.0.0 to 4.0.0.0 is here.
    
    
Important note Important
  • If you add a component to an app in an UpgradedEventEndpoint handler, be sure to add the same code to an InstalledEventEndpoint handler because you want that component included in the app on a brand new installation as well. Also, you should add an UninstallingEventEndpoint (or revise it) for the app to remove the component. Anything that was added or changed by the InstalledEventEndpoint should be reversed or deleted by the UninstallingEventEndpoint. One exception is that data that will remain useful after the uninstallation of the app should not be deleted. (Websites, other than the app web, that are created by the app should be considered data.)

  • For an autohosted app, your UpgradedEventEndpoint web service is included in the app itself. It becomes part of the Web Deploy package. The autohosting infrastructure installs it in Azure Web Sites relatively early in the update process. This ensures that the web service is ready to be invoked at the end of the update process, which is when the updated event that your receiver is handling occurs.

If an error occurs when an app is being updated, the SharePoint 2013 infrastructure stops the update process and rolls back the app instance and all its components to the previous version of the app instance. But the infrastructure has no way of knowing what your UpgradedEventEndpoint handler logic does, so it can't always undo it. An unhandled exception thrown while your UpgradedEventEndpoint handler is executing almost certainly indicates that the entire app update process should be rolled back, but it won't be because the infrastructure doesn't know how to undo some things your UpgradedEventEndpoint code might have done. For this reason, the UpgradedEventEndpoint code must:

  1. Catch all exceptions.

  2. Branch to custom rollback code to undo what it has been done to that point.

  3. Signal to the SharePoint 2013 infrastructure that the entire app update should be rolled back.

  4. Pass on an error message to the infrastructure.

Not everything your UpgradedEventEndpoint does needs to be explicitly reversed inside the handler. The app web is going to be rolled back by the infrastructure, so your code does not have to undo changes to the app web. In an autohosted app, both the Azure Web Site and Microsoft Azure SQL Database components are reverted by the infrastructure, so your code does not have to undo changes it may have made to either of them. But the UpgradedEventEndpoint handler does have to reverse changes it has made to the host web or other components that are external to the app for SharePoint.

On the second (or third, and so on) update, your exception handing logic has to take account of the fact that the app instance that is being updated is not necessarily the immediately previous version. If it isn't, there may be two or more blocks of update code that need to be reversed. For this reason, you need conditional logic that is based on the number of the previous version. The following is an example of the rollback logic for an update to version 4.0.0.0.

catch (Exception e)
{ 
    // Rollback of the 3.0.0.0 to 4.0.0.0 update logic goes here.

    Version ver3OOO = new Version("3.0.0.0");
    if (properties.AppEventProperties.PreviousVersion < ver3OOO)
    {
        // Rollback of the 2.0.0.0 to 3.0.0.0 update logic goes here.
    }

    Version ver2OOO = new Version("2.0.0.0");
    if (properties.AppEventProperties.PreviousVersion < ver2OOO)
    {
        // Rollback of the 1.0.0.0 to 2.0.0.0 update logic goes here.
    }
}

The last things your error handling should do is assign an error message and a cancel status to the SPRemoteEventResult object that the ProcessEvent method returns to the SharePoint 2013 update infrastructure. The following is an example. In this code, result is an SPRemoteEventResult object that has been declared earlier in the ProcessEvent method.

catch (Exception e)
{ 
    // Rollback logic is here. 

    result.ErrorMessage = "custom message " + e.Message;
    result.Status = SPRemoteEventServiceStatus.CancelWithError;
}
Important noteImportant

Assigning SPRemoteEventServiceStatus.CancelWithError (or SPRemoteEventServiceStatus.CancelNoError) to the Status property is crucial. This property is what signals the infrastructure to roll back the update.

Return to Major steps in updating an app, or go directly to one of the following articles to learn how to update the next major component of your app for SharePoint.

Show:
© 2014 Microsoft