Wednesday, June 11, 2014

Remote Event Receivers in SharePoint 2013

 

A new concept of remote event receivers have been introduced in SharePoint 2013. 

These are similar to event receivers in 2010 except that they run remotely and not actually on server.  Hence we user CSOM in the handlers.

There can be two type of event receivers

1. App event receivers

2. Remote event receivers.

Now we need to be clear that app event receivers and Remote event receivers work in Cloud Hosted App i.e. auto hosted or provider hosted.  They do not work with SharePoint Hosted apps.  I have seen some examples on net having remote event receivers on SharePoint Hosted app but event receivers are not meant for SharePoint Hosted App.  There are some methods of these receivers to get client context and they dont work with SharePoint Hosted app.  I had a tough time struggling with an event receiver on SharePoint Hosted app and then I stumbled on a line in msdn

Excerpt from MSDN

Note

Remote event receivers and app event receivers work only with cloud apps for SharePoint (that is, provider-hosted apps). These receivers don't work with SharePoint-hosted apps for SharePoint.

Source - http://msdn.microsoft.com/en-us/library/office/jj220048(v=office.15).aspx

How Does it work

Web services are used in remote event receiver. 

When we add a remote event receiver to an app, a web service also gets added which handles these events.

Open Visual studio, Create a new project (App For SharePoint).  I am going to select AutoHosted.

Now Add New List.  Name the list “ListInApp”.

Now click add new item, Select Remote Event Receiver.  It would ask for list name to be associated with.  Select list just created ‘ListInApp’

image

Name it as AppListReceiver. 

Next select “List Item Event” in type of event receiver.  Select the list ListinApp that we just created. (Notice there are ItemAdding and ItemAdded, ItemDeleting and ItemDeleted and so forth)

image

I am going to select ItemDeleting and ItemAdded.

Notice AnnouncementReceiver.svc gets added in the solution explorer in the App Web project.

image

In the .svc file we notice there are 2 methods. ProcessEvent and ProcessOneWayEvent.

Now before we get into code let us look at the events

We can handle Before and After events.  Now the Before events i.e. ItemAdding, ItemDeleting, etc are always synchronous while the after events i.e. ItemAdded, ItemDeleted etc can be synchronous as well as asynchronous

ProcessEvent handles the synchronous methods while ProcessOneWayEvent are one way, i.e. asynchronous.  They just fired and are forgotten. 

Notice I selected ItemDeleting and ItemAdded.

I am going to handle ItemDeleting to stop allowing to delete any items from my list.

public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
       {
           SPRemoteEventResult result = new SPRemoteEventResult();

           if (properties.EventType == SPRemoteEventType.ItemDeleting)
           {
               result.ErrorMessage = "Deleting items from this list is not allowed";
               result.Status = SPRemoteEventServiceStatus.CancelWithError;
           }

           return result;

}

 

Similarly we can write code in ItemAdded even in the method ProcessOneWayEvent

Getting ClientContext in Remote Event Receivers.

Now to get client context there is a method available TokenHelper.CreateRemoteEventReceiverClientContext(properties)

Now a point to note here is the client context we get here is that of app web.  That is because the event receiver is to handle events on a list on app web.  If the event receiver is written to handle events of list on host web, the context returned is that of host web.

This brings the question how do we create remote event receiver to a list of host web.  We do not get that option in Visual studio.  This has to be done programmatically.  Let us see how

Remote event receivers for list on host web

To do this I am going to write an app event receiver.  My plan is to code an app.  When this app gets installed, it add an event receiver to a list of host web. 

Create a new app project for sharepoint.  Select AutoHosted App. 

Go to properties of the app and select the property “HandleAppInstalled”  Set it to True

image

On doing so we will find AppEventReceiver.svc added to solution explorer in the appweb project

image

This would have method ProcessEvent

public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)

Further Below code can be self explanatory

public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
       {
           SPRemoteEventResult result = new SPRemoteEventResult();

           switch (properties.EventType)
           {
               case SPRemoteEventType.AppInstalled: HandleAppInstalled(properties);
                   break;

                case SPRemoteEventType.AppUnInsstalled: HandleAppUninstalled(properties); break;
            }

           return result;

 

In HandleAppInstalled we will try to attach remote event receiver to host web’s list.  Now to do this we need client context to host web. 

SP gives us a magical method TokenHelper.CreateAppEventClientContext(properties,false)

If the second parameter of this method is false, it returns us the client context to host web else it returns us the client context to app web.

Note that this method is available only with App event receiver and when we try to use it with remote event receiver it returns null.

Below is the code

private void HandleAppInstalled(SPRemoteEventProperties properties)
      {

using (ClientContext clientContext =TokenHelper.CreateAppEventClientContext(properties,false))
          {
              if (clientContext != null)
              {
                  bool rerExists = false;
                  List myList = clientContext.Web.Lists.GetByTitle("MyAnnouncements");
                  clientContext.Load(myList, p => p.EventReceivers);
                  clientContext.ExecuteQuery();

                  foreach (var rer in myList.EventReceivers)
                  {
                      if (rer.ReceiverName == "ItemAddedEvent")
                      {
                          rerExists = true;
                      }
                  }

                  if (!rerExists)
                  {
                      EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
                      receiver.EventType = EventReceiverType.ItemAdded;
                      receiver.ReceiverName = "ItemAddedEvent";
                      OperationContext op = OperationContext.Current;
                      receiver.ReceiverUrl = op.RequestContext.RequestMessage.Headers.To.ToString();
                      receiver.Synchronization = EventReceiverSynchronization.Synchronous;
                      myList.EventReceivers.Add(receiver);
                      clientContext.ExecuteQuery();
                  }

} }  }

 

The above code attaches event receiver to the MyAnnouncements list of host web.  Now you must be wondering where do we write the event handler for this.  If you closely observe the code we have given the Receiver url of the same class, hence we can write the event handler here itself.

The process event function of AppEventReceiver.svc gets changed to this

public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
       {
           SPRemoteEventResult result = new SPRemoteEventResult();

           switch (properties.EventType)
           {
               case SPRemoteEventType.AppInstalled: HandleAppInstalled(properties);
                   break;

                case SPRemoteEventType.AppUnInsstalled: HandleAppUninstalled(properties); break;

                case SPRemoteEventType.ItemAdded: HandleItemAdded(properties); break;
            }

           return result;

}

We add a function HandleItemAdded and that would have the code what needss to be done when we an item is added to MyAnnouncements list.

Debugging

Last but not the least, how do I debug this app?  To do this we can use Windows Azure service bus.

Steps from msdn (Source - http://msdn.microsoft.com/en-us/library/office/jj220047(v=office.15).aspx#DebugRER )  I tend to copy imp things on my blog as you never know which link breaks and goes down ;-)

Debugging remote events


To debug remote event receivers and app event receivers in an app for SharePoint, perform the following steps.

  1. If you're behind a firewall, you may need to install a proxy client (such as the Forefront Threat Management Gateway (TMG) Client), depending on your company's network topology.

  2. Register for a Microsoft Azure account if you haven't already, and then sign into that account.

    For information about how to register for an Azure account, see Microsoft Azure.

  3. Create an Azure Service Bus namespace, which you can use to debug remote events.

    For more information about the Azure Service Bus, see Messaging and Managing Service Bus Service Namespaces.

    Note Note

    Remote event debugging uses the Relay Service component of the Azure Service Bus, so you'll be charged for using the Service Bus. See Service Bus Pricing FAQ. You get free access to Azure each month that you subscribe to Visual Studio Professional with MSDN, Visual Studio Premium with MSDN, or Visual Studio Ultimate with MSDN. With this access, you can use the Service Bus relay for 1,500, 3,000, or 3,000 hours, depending on your MSDN subscription. See Get some amount of Microsoft Azure Services each month at no additional charge.

  4. In Azure, choose your service namespace, choose the Access Key link, and then copy the text in the Connection String box.

  5. On the properties page of your app for SharePoint project, choose the SharePoint tab, and then select the Enable debugging via Microsoft Azure Service Bus check box.

    You must enable this feature to debug remote events in cloud apps for SharePoint. This property applies to all of your SharePoint projects in Visual Studio. Visual Studio automatically turns off remote event debugging if you package your app for distribution on the Office store.

  6. In the Microsoft Azure Service Bus connection string box, paste the connection string that you copied.

  7. If you don't enable remote debugging and don't want to receive a notification whenever your project contains a remote event receiver or an app event receiver, clear the Notify me if Microsoft Azure Service Bus debugging is not configured check box.

After you enable remote event debugging and provide a valid connection string for the Azure Service Bus, you can debug remote events.

Note Note

If the remote event receiver doesn't hit a breakpoint immediately, the event might be asynchronous. Events that contain the word "being", such as "An item is being added" or "An item is being deleted", are synchronous and complete faster. Events that contain the word "was", such as "An item was added" or "An item was deleted", are asynchronous and take slightly longer to complete.

Request Management in SharePoint 2013

 

Why Request Management

SharePoint 2010 had request throttling.  That is let us say been enhanced and in SharePoint 2013 we have Request Management.

Drawbacks of SP2010 Req Throttling

It was not possible to route specific requests to specific server i.e. that kind of control was not with the admin.  For example if a particular WFE is down, the client gets server busy messages from the pool health WFE while other WFEs still were available.

Through RM we can plan to send specific requests to specific servers.  E.g. all requests of specific type like search can be sent to specific machines.

RM can help maintain farm by sending heavy requests to powerful machines.

Through RM we can identify harmful requests

RM mainly includes following 3 main components

1. Request Routing

We have something called as Routing rules to route certain requests to certain WFEs.  For e.g. an admin might decide to route all requests for .pdfs to WFE2.  Routing rules can be written to serve this kind of request.

2. Request throttling and request prioritization

If a particular WFE is heavily utilized from one particular application e.g. Outlook sync is producing lot of sync requests.  Now each machine has a health score from 0 to 10.  0 being the healthiest.  An admin might decide to stop all requests from outlook sync when the health score of WFE reaches 8.  Through RM, one can design a throttling rule which says to stop all requests from useragent *microsoftoutlook.* when the health score of the machine is 8.

3. Request load balancing

Select a WFE server to route the request based on the weighting scheme decided.

Now we need to be familiar with certain terms before understanding RM

1. Health score – We already saw this.  Every WFE can get a health score between 0 and 8.  The policy engine health rule updates the health weights and these cannot be changed by admin

2. Static weights – Each machine can be given a static weight by the admin.  These again can vary from 1 to 10, 1 being the highest.  Admin sets the static weights of the machines so that certain ones are always preferred

3. Routing weights – Routing weights uses health weights and static weights.

4. Routing Rules – Routing rules are configured by admin to route the requests of certain type to certain servers etc.

5. Throttling rules – Throttling rules are configured by admin to throttle certain kind of requests.

Routing and Throttling rules can match

Matching Parameters Methods
Url Starts With
URL Referrer Ends With
User Agent Equals
Host IP RegEx
Http Method  
Soap Action  
Custom Header  

One thing to note is Request Management is applied per web app.

How Does it Work

RM has execution groups. Execution groups contain routing rules. There are 4 execution groups.  When a request comes, rules in execution group 0 are run.  If any of the rule matches, the rest of the rules are not run.  If none in Execution group 0 matches rules of the next execution groups are run.  Each rule points to a machine pool.  Each machine pool contains few servers.  There are cmdlets to create machine pools and add servers to these.  Each machines can be given static weights by admin.  Again there are cmdlets for the same.

We need to start Request Management Service instance.  This can be done from central admin by going to services of this server.

Powershell command ‘Start-SPServiceInstance’ can also be used for the same

1. SPRequestManagementSettings

Get-SPRequestManagementSettings – gets request management settings for a particular web application

Set-SPRequestManagementSettings – sets the settings for a web app

Example

$webApp = Get-SPWebApplication http://sharepoint01/

$rmSettings = $webapp | Get-SPRequestmangementsettings

image

Set-SPRequestManagement Settings can be used to set few configurable properties like ThrottlingEnabled.

Example -

$webapp | Set-SPRequestManagementSettings -ThrottlingEnabled $True

The RequestManagementSettings object got through Get-SPRequestManagementSettings is passed to other cmdlets of RM

2. SPRoutingMachinePool

Cmdlets – Add-SPRoutingMachinePoolallows to add machine servers to a pool and also create a machine pool

     Get-SPRoutingMachinePoolallows to get a machine pool configuration.

Example -

Add-SPRoutingMachinePool -Name `PrimaryMachinePool' -RequestManagementSettings $rmSettings -MachineTargets "SPTaleSpin2013"

Get-SPRoutingMachinePool –RequestManagementSettings $rmSettings

3. SPRoutingMachineInfo

CmdLets – Get-SPRoutingMachineInfo gets machine info

                 Set-SPRoutingMachineInfo – allows to set properties like static weight for a particular machine.

Example

$machineInfo = Get-SPRoutingMachineInfo –Name “SP02” –RequestManagementSetting $rmSettings

Set-SPRoutingMachineInfo –Identity $machineInfo –StaticWeight 4

4. Add-SPRoutingRule and Add-SPThrottlingRule

As the name suggests these cmdlets are used to add routing and throttling rule.

Each of the routing or throttling rule needs a criteria. 

New-SPRequestManagementRuleCriteria – cmdlet lets us define a criteria.  This cmdlet has 3 params

a)Property – It can be Url, Url Referrer, UserAgent (all the parameters we saw in the table above)

b)MatchType – It can be Equal, StartsWith, Regex (all the methods we saw in the table above)

c)Value – The value of property to be matched with

Example

$criteria1 = New-SPRequestManagementRuleCriteria –Property Host –MatchType Equals –Value “www.abc.com”

Hence criteria1 is all request for host www.abc.com

Now lets create a routing rule

$webApp = Get-SPWebApplication ‘http://sp1”

$rmSettings = Get-SPRequestManagementSettings $webApp

$mp1 = Get-SPRoutingMachinePool –RequestManagementSettings $rmSettings –Name “primary machine pool”

$criteria1 = New-SPRequestManagementRuleCriteria –Property Host –MatchType Equals –Value “www.abc.com”

$rule1= Add-SPRoutingRule –RequestManagementSettings $rmSettings –Name ‘Rule for abc.com’ –Criteria $criteria1 –ExecutionGroup 0 –MachinePool $mp1

Similarly we can define throttling rule as

$criteria2 = New-SPRequestManagementRuleCriteria –Property UserAgent –MatchType Regex –Value ‘*.microsoft.outlook.*’

Add-SPThrottlingRule –Criteria criteria2 –Threshhold 8

Monday, June 9, 2014

Caml Queries

 

Everytime I start writing CAML I fall back to U2U.

Wanted to keep few queries handy so here we go

No Filter

<View><ViewFields><FieldRef Name="Title" /><FieldRef Name="Column2" /></ViewFields></View>

With Row Limit

<View><RowLimit>10</RowLimit><ViewFields><FieldRef Name="Title" /><FieldRef Name="Column2" /></ViewFields></View>

Above two can  be used with CamlQuery object as view xml.

example

CamlQuery camlquery = new CamlQuery();
               camlquery.ViewXml = "<View><RowLimit>10</RowLimit><ViewFields><FieldRef Name='Title'/><FieldRef Name='FileRef'/><FieldRef Name='BasePath'/></ViewFields></View>";

   List sSCConfigList = clientContext.Web.Lists.GetByTitle("SSCConfig");
               clientContext.Load(sSCConfigList);
               clientContext.ExecuteQuery();

               var listItems = sSCConfigList.GetItems(camlquery);

Single Select

Example
<Query><Where><Eq><FieldRef Name="Title" /><Value Type="Text">ABC VALUE</Value></Eq></Where></Query>

Multiple Rows

Example
<Query><Where><Contains><FieldRef Name="Title" /><Value Type="Text">Some Text</Value></Contains></Where></Query>

In case of Rich Text field, you can use <![CDATA[]]> around the value to prevent parsing errors when passing HTML into the query. Or you can replace < with &lt;, > with &gt; and “ with &quot; and so on.

Other operators are <Geq/> for greater than and <Leq/> for less than

LookUps

Here we use LookupId=”true”.  By using this we can specify the id value in the value tag
Look up on Id

example

<Query><Where><Eq><FieldRef Name="SomeLookupcolumn" LookupId="TRUE" /><Value Type="Lookup">4</Value></Eq></Where></Query>

Here It will filter on ‘SomeLookupColumn’ column here based on look up id and not value.  This ensures unique value.

This can be used to get person or group also

example

Filter on Current User

Example
<Query><Where><Eq><FieldRef Name="Author" LookupId="TRUE" /><Value Type="Integer"><UserID /></Value></Eq></Where></Query>

Notice here Valuetype is integer.

Using <UserID /> as the value, the query will filter based on the current user. You can also pass the ID of a specific user in place of <UserID /> (e.g. <Value Type="Integer">283</Value>) if you don’t want to filter by the current user.

Lookup on text

Example
<Query><Where><Eq><FieldRef Name="SomeLookupColumn" /><Value Type="Lookup">GujaratState</Value></Eq></Where></Query>

This will look for items with “GujaratState” in the look up column field. If there are more than one items having same display name, it will return all those items.

Date & Time

Example
<Query><Where><Eq><FieldRef Name="Modified" /><Value Type="DateTime"><Today /></Value></Eq></Where></Query>

A date can also be used instead in Value tag.  We can also use something like this <Today OffsetDays="-4" />