Introduction
Remote event receivers are a cool new feature of SharePoint 2013 and usually are quite straightforward to use. That is, until you try to register one programmatically…
One of my clients wanted to attach a remote event receiver to a list using an App. The list is hosted on the App’s host web. This series of posts will detail all the steps you need to take in order to achieve just that. As the documentation for SharePoint 2013 is a bit lacking (and in some cases completely wrong), I hope this documentation will save you all the head banging, tears and long office hours i had to put in, in order successfully get my remote event receiver to register and run on an App’s host web.
This series of posts will consist of the following parts:
- Part 1 (this post) will deal with registering a remote event receiver to an App’s host web using the managed code (C#) client object model.
- Part 2 will deal with working with RestSharp to perform calls to SharePoint’s REST services from a remote event receiver.
- Part 3 will demonstrate an event receiver i get asked about a lot – how to create a post on a user/site’s newsfeed when a document is uploaded to a document library.
Set the stage
To get started, open Visual Studio 2012 (or the new Visual Studio 2013 preview) and create a new App for SharePoint 2013 project. Name the project however you like and set its type to SharePoint Hosted. Your popup should look similar to the following:
Now that we have an app ready, let’s add a remote event receiver: Right click on the project name and choose Add and New Item. Click on Remote Event Receiver and name it DocLibraryRER. Once you click on Finish the Choose Event Receiver Settings popup appears. These settings means nothing to us as we are planning on registering the remote event receiver to the host web, and these settings only affect the app web. Choose whatever settings you wish and click Finish. I used the following:
Pay attention to the Solution Explorer windows:
When we added the remote event receiver we set a chain of changes to our solution:
- Our App project changed from SharePoint hosted to Auto-hosted.
- A new web project was added to our solution in order to host the new remote event receiver.
- The new remote event receiver (DocLibraryRER.svc) was created under the Services folder of the new web project.
Let’s recap what we done so far: We created a SharePoint hosted app and added a remote event receiver that will register itself to whatever event and list we chose in its settings.
In order register this new event receiver programmatically to a list on the host web site, we will have to handle the App Installed event. This event fires as soon as the App is installed by the admin, making it the perfect place to handle such registration tasks. There are three App life cycle events:
- Installed – an event which fires just before the app is fully installed.
- Uninstalling – an event which fires while the app is uninstalling
- Upgraded – an event which fires just before the app is upgraded.
To handle any of these event highlight the App project name in the solution explorer and change the value of the life cycle event you wish to handle from False to True:
As we wish to handle the App Installed event, change its value from False
to
True
as seen in the screenshot above.
As soon as we make the change, a new web service will get added to our web project. The new service is called AppEventReceiver.svc and it’s hosted under the Services folder. This service will handle all the App life cycle events, so even if you handle all three life cycle events, you will still use this one file.
Handling the App Installed Event
When handling App life events, we use the ProcessEvent
public method as the entry point.
In order to make sure the code we write will only execute for the App Installed event, let’s add a check at the top of the
ProcessEvent
method for the type of event its currently handling. Change the
ProcessEvent
method as follows:
public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
{
SPRemoteEventResult result = new SPRemoteEventResult();
if (properties.EventType == SPRemoteEventType.AppInstalled)
{
using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
{
if (clientContext != null)
{
clientContext.Load(clientContext.Web);
clientContext.ExecuteQuery();
}
}
}
return result;
}
Pay special attention to the CreateAppEventClientContext
method call. The second parameter specifies whether we wish to target the App web (the web which the app runs at) or the host web (the host web of the App). As we are planning to attach the remote event receiver to a list on the host web, keep the second argument as false.
The list we are going to attach our remote event receiver to, is the Documents
list which SharePoint creates by default for almost every site template. To load the list change the code inside the using call as follows:
using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
{
if (clientContext != null)
{
var documentsList = clientContext.Web.Lists.GetByTitle("Documents");
clientContext.Load(documentsList);
clientContext.ExecuteQuery();
}
}
In order to debug a remote event receiver you have to create a service bus within Azure and set your Visual Studio to use it. For a detailed tutorial on that subject check out Alexander Vanwynsberghe’s excellent post - Debugging SharePoint 2013 Remote Events Using The Windows Azure Service Bus.
If you take a look in the Elements.xml file that Visual Studio created for the remote event receiver we created earlier, you’ll notice the following element:
<Receiver>
<Name>DocLibraryRERItemAdding</Name>
<Type>ItemAdding</Type>
<SequenceNumber>10000</SequenceNumber>
<Url>~remoteAppUrl/Services/DocLibraryRER.svc</Url>
</Receiver>
If you look closely at the URL element you’ll notice that it uses a variable: ~remoteAppUrl. As the URLs of the remote event receiver differ between the debugging environment and production one, Visual Studio automatically replaces the variable with the correct URL as we build or package the App. Now you may think – ‘Cool, then let’s use it in our code as well!’ well…
Sadly we can’t use this awesome variable when we programmatically attach the remote event receiver, as thus we are faced with the first Gotcha Moment: how can I find out the right URL for debugging (and later publishing) my remote event receiver?? Well, the answer is: ask the
OperationContext
object!
The OperationContext
, as MSDN explains it, “Provides access to the execution context of a service method”. In our case, when we wish to debug, we will set the remote event receiver URL as follows:
string opContext = OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.Substring(
0, OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.LastIndexOf("/"));
string remoteUrl = string.Format("{0}/DocLibraryRER.svc", opContext);
and for the deployment environment:
string remoteUrl = string.Format("https://{0}/DocLibraryRER.svc",
OperationContext.Current.Channel.LocalAddress.Uri.DnsSafeHost + "/services");
For now, comment out the deployment URL as we are going to debug the remote event receiver to make sure it registers.
Next up we create the EventReceiverDefinitionCreationInformation
object which contains all the information about the registered remote event receiver. The object defines (at minimum) the remote event receiver’s type, name, URL, and sequence number.
Our remote event receiver creation object will look as follows:
EventReceiverDefinitionCreationInformation newEventReceiver = new EventReceiverDefinitionCreationInformation()
{
EventType = EventReceiverType.ItemAdding,
ReceiverName = "DocLibraryRER",
ReceiverUrl = remoteUrl,
SequenceNumber = 1000
};
The only thing left for us to do is attach the new remote event receiver to the list we defined earlier. Use the following snippet:
documentsList.EventReceivers.Add(newEventReceiver);
clientContext.ExecuteQuery();
We add the new EventReceiverDefinitionCreationInformation
we created earlier to the
EventReceivers
collection of the list using the Add
method. Since we are using the client object model we have to execute our commands to the server using the
ExecuteQuery
method of the client context object.
If you made it this far, add a breakpoint in the beginning of DocLibraryRER.svc.cs and hit the ol’ F5 key and check your new, shiny and pure awesome remote event receiver!
Well, that didn’t work, did it?
Setting the right permissions
This nifty little error brings us to the next Gotcha Moment: when working with Apps always remember to set its permissions. An App without permissions is like eating Oreos without milk – it simply wont work.
To set our App’s permissions, expend the App project in the solution explorer, and double click the AppManifest.xml file. The Manifest Designer will pop right up:
As you probably know by now, our App registers the remote event receiver to a list on the host web, thus, we need to ask the user’s permission to manage a specific list or web. For simplicity purposes we will use the web scope in this example.
Click on the Permissions tab. Under Scope choose Web and under Permissions choose Manage. Your permissions manager should look as follows:
Hit the ol’ F5 key again. This time a new popup will pop:
This popup is directly related to the permissions we added in the Manifest Editor earlier. It asks the installing user for permissions to manage the current site. Click on Trust It and wait untill the App finishes installing. Once installed the App will run its default page file in your browser. Do not close this window! Closing it will detach the debug process.
Open another tab and head over to the site you installed the App to (if you need a refresher check out the App’s properties for the Site URL property). Open the Documents folder and try to upload a file. If everything went smooth, your breakpoint should be hit!
What's next?
Congratulations! You have successfully deployed an App which register a remote event receiver to a host web and managed to debug it. If you are going to deploy it to an App Catalog or even the Office Store don’t forget to change the
remoteUrl
variable to the deployment one.
If you wish to download the complete solution – click here.
In the next part of the series we will discuss how to work with RestSharp to call SharePoint 2013′s REST services from a remote event receiver.