Specifying Reader Quotas for WCF Services in SharePoint 2010

Creating custom WCF services for SharePoint 2010 is very easy. If you read the article here, Creating a Custom WCF Service in SharePoint Foundation, you’ll see how easy. Most of the article deals with the sample service, but only two steps deal with the actual service hosting. All you need to do for most scenarios is deploy an .svc file to the ISAPI folder in the SharePoint root and use the MultipleBaseAddressBasicHttpBindingServiceHostFactory class as your service host factory like so:

<%@ServiceHost
language="c#"


Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory,
Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"


Service="MyAssembly.MyService, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2f6e123a83b3b3e5"

%>

 

That’s it! MultipleBaseAddressBasicHttpBindingServiceHostFactory does all the work required to set up the end points and bindings. You don’t need to do create a web.config file or write any WCF service code.

But, you might see some errors when you use the service like:

The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.

–OR–
The remote server returned an unexpected response: (400) Bad Request.

 

Both of these errors are caused by the message size. The first happens when a single parameter exceeds the default length of 8k and the second happens when the overall message size exceeds the default of 65k.

You can change these limits as follows:

//This code makes it possible to pass large messages (up to 10MB)
//To the outward facing service (not the service app)
//i.e. ISAPIMySolutionMyService.svc
//Note:10MB is not the upper limit, it's just what I'm using in this example


private void ConfigureWebService()
{
SPWebService contentService = SPWebService.ContentService;

SPWcfServiceSettings wcfServiceSettings = new SPWcfServiceSettings();
wcfServiceSettings.ReaderQuotasMaxStringContentLength = 10485760;
wcfServiceSettings.ReaderQuotasMaxArrayLength = 2097152;
wcfServiceSettings.ReaderQuotasMaxBytesPerRead = 10485760;
wcfServiceSettings.MaxReceivedMessageSize = 10485760;
contentService.WcfServiceSettings["MyService.svc"] = wcfServiceSettings;

contentService.Update();
}

Author: Doug Ware

Passing Values from a List Custom Action to Server Code

In my previous post I showed how to create a custom action for a specific list with code. Usually when I need to create a custom button on a list I also need to do some processing of the selected item(s) on the server. To that end, I’ve developed a reusable pattern that is the subject of this post.

Enabling and Disabling the Ribbon Button

The code in the previous post shows the EnabledScript attribute of the CommandUIHandler element.

This attribute takes a JavaScript function. If the function returns true, SharePoint enables the button. The script for this is simple:

function EnableDoAction() {
var ctx = SP.ClientContext.get_current();
//Enable the button if list items are selected
return (SP.ListOperation.Selection.getSelectedItems(ctx).length > 0);
}

 

Passing Values from the Parent Form to the Dialog

The ribbon button executes another JavaScript function on click. This function opens a confirmation page using the dialog framework and passes the current list ID and the selected items via the SP dialog options as so:

function DoAction() {
//Get the List ID and the selected items
var ctx = SP.ClientContext.get_current();
this.web = ctx.get_web();
var listId = SP.ListOperation.Selection.getSelectedList();
var items = SP.ListOperation.Selection.getSelectedItems(ctx);

//Store the id and items in a variable
var args = {
listId: listId,
items: items
};

//Open the confirmation dialog
var options = {
url: '/_layouts/MySolution/MyConfirmationPage.aspx',
title: 'Confirm Action',
allowMaximize: false,
showClose: true,
width: 400,
args: args
};
SP.UI.ModalDialog.showModalDialog(options);
}

 

Passing Values from the Dialog to the Server

The trick is passing arguments from the parent to the dialog and from the dialog to the server. I like to use JSON serialization for this via a two step process. First I need to use script on the dialog to get the args and populate a hidden input field with a JSON representation.

<input id="args" type="hidden" runat="server" />
<script type='text/javascript'>
//Populate input field
childDialog = window.top.g_childDialog;
args = childDialog.get_args();
var input = document.getElementById('<%= args.ClientID %>');
input.value = JSON.stringify(args);
</script>

 

(Most current browsers support JSON natively, for older browsers like IE7 include json2.js from http://www.json.org)

On the server side, deserialize the JSON into a class as follows.

//JSON Deserialization classes for args
public class Args
{
public string ListId { get; set; }
public List<Item> Items { get; set; }
}

public class
Item
{
public int Id { get; set; }
}

Use the following code in the appropriate event handler in your page or control to turn the JSON contained in the hidden input field into a .NET objects and then use the information to do whatever you require to the selected list items or documents.

//Deserialization code
var js = new JavaScriptSerializer();
string json = args.Value;
var SelectedValues = js.Deserialize<Args>(json);

Author: Doug Ware

Create a Custom Action with Code

A few months ago when I was writing the SharePoint 2010 for Developers course for AppDev I noticed an important difference in the behavior of CustomAction between sandbox and farm deployment – something I personally think is a bug. When you deploy a CustomAction to the sandbox, you can target a specific list instance by using a token in the RegistrationId attribute as so:

RegistrationId="{$ListId:Lists/MyList;}"

SharePoint replaces the token at activation time with the list’s actual ID. This doesn’t work if you deploy the feature as part of a farm solution. In SharePoint 2010 you can use the same token syntax for the List attribute in a Field feature element to create a Lookup field with a feature without writing code in both farm or sandbox mode.

Because of this inconsistency, if you need to create a ribbon button that applies to a specific list in a farm deployment scenario you must write some code in a feature event receiver to create an SPUserCustomAction class as so:

SPList list = web.Lists["SomeList"];
SPUserCustomAction action = list.UserCustomActions.Add();
action.Title = "Do Something";
action.Description = "Sample ribbon button";
action.ImageUrl = "/_layouts/images/centraladmin_backupandrestore_granularbackup_32x32.png";
action.Location = "CommandUI.Ribbon.ListView";
action.Sequence = 0;
action.Url = "javascript:DoAction();";
action.CommandUIExtension =
@"<CommandUIExtension xmlns='http://schemas.microsoft.com/sharepoint/'>
<CommandUIDefinitions>
<CommandUIDefinition Location='Ribbon.Documents.Manage.Controls._children'>
<Button Id='Sample.DoSomething.Button'
Command='Sample.DoSomething'
Image32by32='/_layouts/images/centraladmin_backupandrestore_granularbackup_32x32.png'
Image16by16='/_layouts/images/centraladmin_backupandrestore_granularbackup_32x32.png'
Sequence='0'
LabelText='Do Something'
Description='Sample ribbon button'
TemplateAlias='o1' />
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler Command='Sample.DoSomething'
CommandAction='javascript:DoAction();'
EnabledScript='javascript:EnableDoAction();'/>
</CommandUIHandlers>
</CommandUIExtension>"
;

action.Update();

 

The association with the list isn’t done by setting the RegistrationId or RegistrationType properties, but rather by adding the custom action to the SPList’s UserCustomActions collection and you’ll get an error if you try to set these properties in the context of an action for SPList. However, both SPSite and SPWeb expose UserCustomActions collection properties and you can use these to programmatically create other custom actions where RegistrationId and RegistrationType apply.

Author: Doug Ware