Sunday, 15 June 2008

Disposing SharePoint objects - what they don't tell you

If you're a SharePoint developer and find yourself regularly writing code to work with SPListItem objects, here's some info which might help you factor your code in a slightly neater way in some scenarios.  Interestingly this doesn't get mentioned in any of the key papers/articles - I can think of a good reason for this (which we'll look at), but in my mind this is a safe technique. If you have other ideas however, I'd love to hear from you.

Most developers are now fairly well-versed in the techniques needed to write efficient SharePoint code (key resources are listed at the end of the article), specifically in terms of disposing of any SPSite/SPWeb objects your code creates. The thing is, having to always be mindful of disposing these objects can sometimes restrict us in the way we want to write our code - consider the following simplified example:

public void DoSomething()
{
SPList list = getList();

// do something with list..
foreach (SPListItem item in list.Items)
{
processItem(item);
}

// ** PROBLEM - how do we now dispose of the SPSite/SPWeb objects we created earlier? **
}

private SPList getList()
{
// can't dispose of these objects here if we're returning a list - we'll be attempting to use
// objects which have already been disposed of..
SPSite site = new SPSite("http://cob.blog.dev");
SPWeb web = site.OpenWeb("/MyWeb");
SPList list = web.Lists["MyList"];

return list;
}


The problem here is we created our SPSite/SPWeb objects in a different method to where we need to dispose them - this can happen a lot when writing library code. To extend this example, here's a pattern I regularly use to ensure I'm getting my objects the most efficient way - using SPContext if it's present, but instantiating them myself if it isn't (i.e. the code is called from an event receiver, console app etc.):


public void DoSomething()
{
bool bDisposalsRequired = false;

// get list from SPContext if we have one..
SPList list = getListFromContext();
if (list == null)
{
// otherwise get list from objects we create..
list = getInstantiatedList();
bDisposalsRequired = true;
}

// do something with list..
foreach (SPListItem item in list.Items)
{
processItem(item);
}

if (bDisposalsRequired)
{
// ** PROBLEM - how do we now dispose of the SPSite/SPWeb objects we created earlier? **
}
}

private SPList getInstantiatedList()
{
// can't dispose of these objects here if we're returning a list - we'll be attempting to use
// objects which have already been disposed of..
SPSite site = new SPSite("http://cob.blog.dev");
SPWeb web = site.OpenWeb("/MyWeb");
SPList list = web.Lists["MyList"];

return list;
}

private SPList getListFromContext()
{
SPContext currentContext = SPContext.Current;
SPList list = null;

if (currentContext != null)
{
list = currentContext.Site.AllWebs["MyWeb"].Lists["MyList"];
}

return list;
}


When the code starts to become more complex like this, we start to really want a simple way of disposing the objects we created elsewhere. I'd probably caution against this, but what happens if you created the SPSite/SPWeb objects not only in another method, but another class? All of a sudden disposing of objects correctly is a lot more complex than the examples given in the white papers. 

Now, the savvy SharePoint developer might think "Hold on, the SPList has a ParentWeb object (and SPListItem has a ParentList object!), what happens if I go up the hierarchy and dispose that way?" - code to this would look like:


if (bDisposalsRequired)
{
list.ParentWeb.Dispose();
list.ParentWeb.Site.Dispose();
}


Now how would we know the correct objects have been disposed? Are these objects the same objects we were using earlier? Or has SharePoint populated these properties with references to different objects, meaning we've left our original objects alive and could run into scalability problems?

Well, the good news is these are the same objects, so disposing in this way will dispose of the objects you created. We can tell this by comparing the objects using System.Object's GetHashCode() method:


private void compareObjects()
{
SPSite site = new SPSite("http://cob.blog.dev");
SPWeb web = site.OpenWeb("/MyWeb");
SPList list = web.Lists["MyList"];

SPWeb parentWeb = list.ParentWeb;



// no message displayed..
Debug.Assert(parentWeb.GetHashCode() != web.GetHashCode(), "Objects are not same!");


// create a *new* SPWeb object and compare it to our other reference..
web = site.OpenWeb("/MyWeb");

// message IS displayed..
Debug.Assert(parentWeb.GetHashCode() != web.GetHashCode(), "Objects are not same!");
}


What we see from this is that the SPWeb object stored in the list.ParentWeb property has the same hash code as the original SPWeb object we created, but if we create a new instance of the same web, it has a different hash code. This indicates the objects in the first comparison are the same - so disposing of them through the ParentWeb reference will indeed dispose of the objects we created. So happy days - we've found we can factor our code in a way which may be more logical and readable.


The caveat

So why might this not be mentioned in any of the published guidance? Well, before using this technique throughout your code you should consider that in writing code in this way we are effectively relying on an internal implementation detail of the current SharePoint API. If Microsoft were to change this in a service pack (or the next version of SharePoint), our code may no longer dispose of objects correctly. I'd recommend giving this your own consideration (not least because you might conclude differently to me), but I'd suggest this change would be fairly unlikely. The reason for this is the SharePoint team themselves need to eliminate any unnecessary SPSite/SPWeb objects, so of course it makes sense that the the same SPWeb instance used to obtain the SPList is used to populate the ParentWeb variable. Creating an extra instance would make the API less efficient. On that basis, I personally am happy to write code in this way. If you think I've got this wrong (I can't guarantee I haven't), of course I'd love to hear from you.

In an upcoming article, I'll also discuss the idea of going beyond the MSDN examples, and trying to write SharePoint data access code in a more 'enterprise' or 'patterns' kind of way. Stay tuned for more on this.


Disposing SharePoint objects - required reading

Sunday, 1 June 2008

Code/Feature samples for common SharePoint development tasks

There are lots of tools available to help developers, but I always think the single most useful thing is actually seeing a good example of the thing I'm trying to develop. This is why SharePoint blogs are so popular right? As well as checking my favorite blogs or past articles, I'm always going back to previous work - perhaps to see what values I used in a certain SharePoint file or to grab a chunk of code I wrote which I can amend to fit my current requirement. So today I want to point you in the direction of some useful samples which could be good reference material if you're a SharePoint developer - in actual fact I'm not releasing anything new here, but highlighting that my recent 'Config Store' solution provides a great "one-stop shop" of downloadable examples of several common SharePoint development tasks. Examples of the following dev tasks can all be found in the solution:

  • Deploy site columns, content types as Feature
  • Deploy a list (with default list items) as a Feature
  • Using Feature receivers
  • Using SPWebConfigModification to make web.config changes in code/as part of a Feature
  • Using SPQuery to efficiently find items in a list
  • Build a Solution package (.wsp) without WSP Builder
  • Write a Solution deployment script to retract Solution, deactivate Feature, redeploy Solution, activate Feature etc.
  • Using event receivers
  • As a .Net bonus, how to use the HttpRuntime class to work with ASP.Net caching outside of a web context

The entire set of code/Feature files can be downloaded from Codeplex, making it a great reference project containing samples of all the tasks listed above. If this is useful to you, the list below details the file to open to find the code sample, and some notes which might help you to get to grips with the task:

Deploy site columns, content types as Feature:

File

Notes

ConfigStoreElements.xml          

This is accomplished by use of the Field/ContentType Feature elements. Note these should be before any ListTemplate/ListInstance items in the same elements file - if using both sets of elements in different files, ensure the field/content type file is listed first in manifest.xml.



Deploy a list (with default list items) as a Feature:

File

Notes

ConfigStoreElements.xml          

This is accomplished by use of the ListTemplate/ListInstance Feature elements.



Feature receivers:

File

Notes

ConfigStoreFeatureReceiver.cs

In my case, the Feature receiver makes modifications to web.config and programmatically adds list event receivers to a list.



web.config modifications

File

Notes

ConfigStoreFeatureReceiver.cs

This is accomplished by use of the SPWebConfigModification class. I add three entries to the web.config to support my solution.

 

Using SPQuery to find items in a list:

File

Notes

ConfigStore.cs                                 

I have two methods in the ConfigStore class which perform queries in this way (also known as a CAML query).

 

Build a Solution package (.wsp) without WSPBuilder:

File

Notes

makecab.ddf/                                  
manifest.xml                          

Whilst WSPBuilder offers a great automated way to build Solutions, occasionally there is a need to step outside the tool to build the package 'manually'. In my case, I had a problem where WSPBuilder was adding elements in the 'wrong' order to the generated manifest.xml file, and this was causing the Solution deployment to fail.

 

Solution deployment script to retract Solution, deactivate Feature etc:

File

Notes

COB.SharePoint.Utilities.ConfigStore_Install.bat      

This is the script I've often used to deploy my Solutions. It has error checking at each stage so I can easily tell at what point the install fails for simpler problem diagnosis.

 

Using event receivers:

File

Notes

ConfigStoreListEventReceiver.cs

In the Config Store solution I use a list event receiver to add items to the cache when the list contents change. This code shows using the ItemAdded and ItemUpdated events on the list.

 

Using the HttpRuntime class to work with ASP.Net caching outside of ASP.Net:

File

Notes

ConfigStoreListEventReceiver.cs

Something to remember with list event receivers is that we actually don't have access to HttpContext.Current or SPContext.Current - both are null. If it wasn't for the HttpRuntime class, this would be a problem for my Config Store code since I wanted to add/remove items from the cache in the list event receiver (i.e. manage the cache as soon as list items are changed). Fortunately, HttpRuntime allows us access to the ASP.Net framework from any code location (e.g. console app).

 

The link to download all this stuff is at http://www.codeplex.com/SPConfigStore. Hope you find the samples useful, feel free to leave a comment if anything doesn't make sense.

Monday, 19 May 2008

Config Store FAQs

So last week I introduced my Config Store 'framework' on Codeplex, which provides a complete solution (including source code!) for storing config values in a SharePoint list, but skipped over some of the details, including the optimizations which I think make it so much better than a simple implementation of this concept. This post aims to shed a bit more light on what the Config Store is about, and hopefully provides some key information to help you work out whether it's something which would be useful in your projects.


What kind of configuration values can I put in the Config Store?
In short, any value you wish to avoid hardcoding into your SharePoint application's C#/VB.Net code. Values are stored in the list as strings, but if they are actually integers, booleans, DateTime values etc., you would just cast them as appropriate after retrieval. Note also that we can also store complex data as XML, and either deserialize into a class or otherwise process with standard XML methods.


How do I retrieve config values in code?
For a single value it's as simple as:

string sAdminEmail = ConfigStore.GetValue("MyApplication", "AdminEmail");


To take advantage of the optimization (described later) for retrieving multiple values in a single control/page/web part/whatever, use the ConfigStore.GetMultipleValues() method - see the Config Store introduction post or Codeplex site for details.


What are the main components?
The main pieces are:

  • Infrastructure - the list, site columns, and content type

  • Event receivers - responsible for managing the config cache

  • Feature and Feature receiver - responsible for provisioning the infrastructure (Feature) and also adding web.config entries and hooking up event receivers (Feature receiver)

  • Code - the main class is the ConfigStore class, this has the GetValue() and GetMultipleValues() methods.


How do I install it?
Simply run the install script which will do the work of installing the Solution (.wsp) to your site. Once that's complete, there are a couple of things to check got installed correctly - a checklist is included in the readme.txt in the download.


Performance is important on our site - how is the Config Store optimized?
There are several levels of optimization - taking these one at a time:

  • all values are cached in memory, so in general retrieval is lightning fast and doesn't require any kind of database lookup.

  • even the first ever request for a particular value will be lightning fast, since the value will already be in the cache. This is possible because of the event receiver on the list - when the admin added the config value to the list, we executed some code to proactively add the item to the memory cache at this point. The caveat here of course, is that IISResets/app pool recycles will still clear down the cache (since it is the ASP.Net memory cache), so this only holds for values added since the last reset/recycle. All that said, you could certainly add code to the global.asax file to cache all values automatically after a reset/recycle if you want ultimate performance.

  • if a query is required, the retrieval code uses the current SPContext if one is available - this avoids creating an SPSite/SPWeb object unless absolutely necessary. Needless to say, any objects which are created are also disposed.

  • the query is performed using a CAML query (SPQuery) as opposed to iterating through the whole config list.

  • the GetMultipleValues() method allows the developer to do just that whilst ensuring there is only one underlying query, rather than one for each value retrieved. 


Developers familiar with caching will recognise that this alone provides a huge boost to performance.


Where can I retrieve values from in code?
It doesn't have to be a SharePoint page, control or web part. The framework is designed to work in other scenarios even when there is no SPContext available - examples are event receivers, InfoPath managed code or even outside the SharePoint web app (e.g. console app, custom STSADM command).


I work in a large SharePoint deployment and my admin doesn't want to deploy assemblies to the GAC or run under Full Trust. Can I still use the Config Store?
By default the Config Store Solution installs the assembly to the GAC. This provides access to the functionality from outside the SharePoint web app as described above. However, it is possible to run the assembly from your site's bin directory, though remember you will then need to define CAS policy in your web.config code as appropriate.


I've installed the Config Store using the supplied .wsp and install script, how can I quickly tell it's working?
The quickest way is to add the supplied 'ConfigTestControl' to one of your pages in SharePoint Designer. You'll need the Register directive to link the assembly, and also the control declaration:



<%@ Register Tagprefix="COB" Namespace="COB.SharePoint.Utilities" Assembly="COB.SharePoint.Utilities.ConfigStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" %>
. <!-- normal page markup here -->
.
.
<COB:ConfigTestControl runat="server"></COB:ConfigTestControl>


How does the Config Store work in terms of permissions? Do I need to apply any permissions to the list to make it work?
No specific permissions need to be configured, unless you choose to lock down write access to the config list beyond the default - this is for the list to inherit the permissions of the root web of your site. This means  anyone with 'Contribute' permission in this web (for example) will also be able to work with items in the config list. If you do wish to restrict who can modify config values, simply add whatever security you require to the config list - the Config Store framework will still continue to work since the code retrieves values under an admin context, via use of SPSecurity.RunWithElevatedPrivileges().


Our site is an internet site, will the Config Store work with anonymous users?
Yes. Again, since values are actually retrieved under an admin context, it doesn't matter that your anonymous users do not have direct permissions to read from the list.


Anything else?
It's worth remembering that we can also take advantage of services offered by the list infrastructure when using the Config Store. Some ideas could be auditing ("we need to log any changes to our app's config"), alerts ("if something changes, I want to know about it!"), version history ("let's see what the setting was when the site was having problems") and workflow ("I want all changes to go through me for change management").


The Config Store framework can be downloaded from www.codeplex.com/SPConfigStore.

Sunday, 11 May 2008

Introducing the SharePoint Config Store for developers

Today I want to introduce something I've been working on recently which could be of use to you if you're a SharePoint developer. Often when developing SharePoint solutions which require coding, the developer faces a decision about what to do with values he/she doesn't want to 'hardcode' into the C# or VB.Net code. Example values for a SharePoint site/application/control could be:

'AdministratorEmail' - 'bob@somewhere.com'
'SendWorkflowEmails' - 'true'

Generally we avoid hardcoding such values, since if the value needs to be changed we have to recompile the code, test and redeploy. So, alternatives to hardcoding which folks might consider could be:

  • store values in appSettings section of web.config
  • store values in different places, e.g. Feature properties, event handler registration data, etc.
  • store values in a custom SQL table
  • store values in a SharePoint list

Personally, although I like the facility to store complex custom config sections in web.config, I'm not a big fan of appSettings. If I need to change a value, I have to connect to the server using Remote Desktop and open and modify the file - if I'm in a server farm with multiple front-ends, I need to repeat this for each, and I'm also causing the next request to be slow because the app domain will unload to refresh the config. Going through the other options, the second isn't great because we'd need to be reactivating Features/event receiver registrations every time (even more hassle), though the third (using SQL) is probably fine, unless I want a front-end to make modifying the values easier, in which case we'd have some work to do.

So storing config values in a SharePoint list could be nice, and is the basis for my solution. Now I'd bet lots of people are doing this already - it's pretty quick to write some simple code to fetch the value, and this means we avoid the hardcoding problem. However, the Config Store 'framework' goes further than this - for one thing it's highly-optimized so you can be sure there is no negative performance impact, but there are also some bonuses in terms of where it can used from and the ease of deployment. So what I hope to show is that the Config Store takes things quite a bit further than just a simple implementation of 'retrieving config values from a list'.

Details (reproduced from the Codeplex site)

The list used to store config items looks like this (N.B. the items shown are my examples, you'd add your own):

ConfigStoreList.jpg
There is a special content type associated with the list, so adding a new item is easy:

ConfigItemContentType.jpg

..and the content type is defined so that all the required fields are mandatory:

AddNewConfigItem.jpg

Retrieving values


Once a value has been added to the Config Store, it can be retrieved in code as follows (you'll also need to add a reference to the Config Store assembly and 'using' statement of course):

string sAdminEmail = ConfigStore.GetValue("MyApplication", "AdminEmail");


Note that there is also a method to retrieve multiple values with a single query. This avoids the need to perform multiple queries, so should be used for performance reasons if your control/page will retrieve many items from the Config Store - think of it as a best practise. The code is slightly more involved, but should make sense when you think it through. We create a generic List of 'ConfigIdentifiers' (a ConfigIdentifier specifies the category and name of the item e.g. 'MyApplication', 'AdminEmail') and pass it to the 'GetMultipleItems()' method:


List<ConfigIdentifier> configIds = new List<ConfigIdentifier>();

ConfigIdentifier adminEmail = new ConfigIdentifier("MyApplication", "AdminEmail");
ConfigIdentifier sendMails = new ConfigIdentifier("MyApplication", "SendWorkflowEmails");
configIds.Add(adminEmail);
configIds.Add(sendMails);
Dictionary<ConfigIdentifier, string> configItems = ConfigStore.GetMultipleValues(configIds);
string sAdminEmail = configItems[adminEmail];
string sSendMails = configItems[sendMails];

..the method returns a generic Dictionary containing the values, and we retrieve each one by passing the respective ConfigIdentifier we created earlier to the indexer.


Other notes
  • All items are wrapped up in a Solution/Feature so there is no need to manually create site columns/content types/the Config Store list etc. There is also an install script for you to easily install the Solution.

  • Config items are cached in memory, so where possible there won't even be a database lookup!

  • The Config Store is also designed to operate where no SPContext is present e.g. a list event receiver. In this scenario, it will look for values in your SharePoint web application's web.config file to establish the URL for the site containing the Config Store (N.B. these web.config keys get automatically added when the Config Store is installed to your site). This also means it can be used outside your SharePoint application, e.g. a console app.

  • The Config Store can be moved from it's default location of the root web for your site. For example sites I create usually have a hidden 'config' web, so I put the Config Store in here, along with other items. (To do this, create a new list (in whatever child web you want) from the 'Configuration Store list' template (added during the install), and modify the 'ConfigWebName'/'ConfigListName' keys which were added to your web.config to point to the new location. As an alternative if you already added 100 items which you don't want to recreate, you could use my other tool, the SharePoint Content Deployment Wizard at http://www.codeplex.com/SPDeploymentWizard to move the list.)

  • All source code and Solution/Feature files are included, so if you want to change anything, you can

  • Installation instructions are in the readme.txt in the download


Summary

Hopefully you might agree that the Config Store goes beyond a simple implementation of 'storing config values in a list'. For me, the key aspects are the caching and the fact that the entire solution is 'joined up', so it's easy to deploy quickly and reliably as a piece of functionality. Many organizations are probably at the stage where their SharePoint codebase is becoming mature and perhaps based on a foundation of a 'core API' - the Config Store could provide a useful piece in such a toolbox.


You can download the Config Store framework and all the source code from www.codeplex.com/SPConfigStore.

Tuesday, 29 April 2008

Considerations when referencing assemblies in your page layouts

This one came up in the office last week, and I thought it worthy of discussion. On one project we have, the page layouts use the ASP.Net Register directive to reference assemblies containing our controls. So we have several directives in the code similar to:

<%@ Register TagPrefix="psw" Namespace="OurCompany.OurClient.SharePoint.WebControls" Assembly="Parity.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00000000000000" %>


These serve the purpose of telling the page layout about our controls assembly, and ensuring the designer can supply us with Intellisense for the controls and their members.


The question which then arises is "what happens when we update the shared assembly and we want to increment the version number? Surely we don't have to update and republish all our page layouts?" There are a couple of answers to this:



  • we could use assembly redirects in application config to avoid changing the page layouts. These entries would then tell .Net to load (for example) version 2.0.0.0 of our assembly whenever version 1.0.0.0 is requested. This would work, but would mean that we don't get Intellisense for any new/changed members added in subsequent assembly versions.

  • we could also avoid referencing common assemblies in this way completely, and centralize the reference in web.config instead


So instead of having an entry in each page layout, the 'Pages' section of web.config (in .Net 2.0 and upwards) allows us to reference assemblies containing controls in one central place, meaning any changes to the assembly name are simple to implement:



<pages>
<controls>
<add tagPrefix="psw" namespace="OurCompany.OurClient.SharePoint.WebControls"
assembly="OurCompany.OurClient.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00000000000000" />
<!-- also works with short assembly name -->
<!--
<add tagPrefix="psw" namespace="OurCompany.OurClient.SharePoint.WebControls"
assembly="OurCompany.OurClient.SharePoint.WebControls" />
-->
</controls>
</pages>


[Side note] - as I specify in the comment, it is possible to reference the assembly with the short name, but shops with a defined versioning strategy will want to avoid this since it's then not possible to have side-by-side versions of assemblies, one of the fundamental forward steps introduced with .Net.


However, it's not all clear cut. Unfortunately SPD isn't clever enough to resolve the assembly reference in web.config to provide Intellisense. I initially thought it was, but alas closing and re-opening shows that in fact something was cached. Visual Studio is sufficiently aware (if we were in a pure .Net scenario), but not SPD.


So it's a trade-off as far as I can see - either have the @Register directive in your page layout (with version numbers in it), or reference in web.config but lose Intellisense.


I wondered if it was possible to use the short assembly name in the @Register directive, but supply a 4 part assembly name in web.config for use at runtime. Unfortunately this doesn't work since .Net sees it as an ambiguous registration. So if you want to keep assembly version numbers out of your page layouts but keep Intellisense, one strategy could be to remove the @Register directives as part of your release process.


Or as an alternative final thought - which is more important, Intellisense during development or minimising running into this problem at release time? For my money, losing the inconvenience of Intellisense in SPD for controls in is a minor hassle, so referencing assemblies in web.config is a better approach.

Sunday, 20 April 2008

5 things you didn't know about me

So a full year after it started, this tagging thing is still going on - my buddy Robin Meuré has tagged me to write about things you didn't know about me. I figure since I've not done this yet and I rarely write about anything personal, I'd make an exception and do it - so here goes. I think I'm actually supposed to write 8 facts, but 5 seems plenty to me :-)

  1. I'm completely addicted to breakfast cereal!

    I love muesli in particular, and for the last 10 years have got through at least two big bowls a day when not away from home. Those who have me on Messenger might know this from the Chris O'Brien : ICerealizable tag (developer's joke :-)) Bizarrely, my girlfriend Suzanne is a buyer for an organic muesli company, and so is able to bring lots of free stuff home, woohoo! Shortly after we met a couple of years ago, I remember in the pub my friends asking what she did as a job - when I told them, they didn't believe me ;-)

  2. I used to be a competitive cyclist.

    In my teens my life was dominated by cycling. I won lots of races when I was at my peak at around 18, and wasn't too far off getting into the GB team. Unfortunately my sporting career was cut short due to the combination of a couple of bad crashes in races and discovering girls and pubs. Guess I probably didn't have the dedication required to make it at the top level.

  3. I got into computers by accident.

    I studied business (and French!) rather than a classic Computer Science degree. The third year was a 'year in industry', and I'd talked my way into a prestigious placement in the IT department of large blue-chip. I hated it. I didn't understand the technology (IBM AS/400) and everyone spoke a language I didn't understand. After 5 months of unhappiness, it dawned on me that if I threw myself at it I'd probably understand it, and if I understood it I'd probably like it. I studied OS/400, Query 400, JD Edwards etc every evening, and 2 or 3 months later I absolutely loved it. I didn't want to return to university at the end of the year and was tempted when my employer said there'd always be a job for me there.

  4. I love travelling.

    My backpacking days are probably over, but I've been lucky enough to travel around South America, Australasia and Asia. The most recent trip was a solo trip to Nepal just over a year ago, and I got to trek to Annapurna base camp which is used for climbing expeditions in the area. At the higher altitudes it was getting down to -16 °C at night, and of course the simple lodges used for shelter at night have no heat! Those nighttime trips in the howling wind to get to the toilet were pretty memorable.   

  5. I'm a big Manchester City fan.

    Although I've lived in London for many years, I'll always support the 'other' team from Manchester and for better or worse, this was handed down from my father. If you speak to many people from Manchester, they'll always tell you real Mancunians support City. Unfortunately it's frequently a despondent hobby, and Colin Schindler had it right when he wrote his book 'Manchester United ruined my life'. However, I'm encouraged to read there is apparently an entire village of Manchester City fans somewhere in Sierra Leone!

So that's me. Some other people I'd like to tag who I don't think have done this are:

Monday, 14 April 2008

Accelerate your InfoPath managed code development

Like most developers, I'm always looking for ways to speed up the code/test/get feedback cycle. There are many ways to do this in SharePoint development, and I guess a typical example would be to do application pool recycles rather than full IISResets. Along similar lines, anyone who has spent time doing Visual Studio/WF workflow development in SharePoint would hopefully have discovered the DEPLOY QUICK parameter on the build script MS supply - this will simply GAC the updated workflow assembly rather than perform a full deployment of the workflow Solution/Feature. This alone will probably shave many hours off a significant workflow project.

When working with InfoPath forms which have managed code, we need to deploy as an administrator-approved form rather than publish directly to a list or content type. The process is:

  • publish the form to the filesystem
  • upload the form via 'Manage form templates' in Central Admin

In the background, the last step actually wraps the form .xsn and associated assembly in a Solution and Feature, and deploys throughout your farm. Understandably this is S-L-O-W for development, so I started looking at ways to speed this up. Interestingly the assembly doesn't get deployed to the GAC or web application bin directory, but resides solely in the folder for the Feature (click image to enlarge):



Nevertheless, instead of publishing the form we can do the following:

  • compile VSTA/VSTO project
  • copy .dll (and .pdb if we're in debug mode) over the ones in the Feature folder
  • recycle app pool

This will then load the updated code. Note because we've only deployed the updated code to the local machine, only form sessions on the local machine will be affected (I'm assuming you're not using a load-balanced URL in dev) - this has a nice side-effect of isolating your changes from other developers in the farm until you're ready to release them.

I'm interested to know how InfoPath accomplishes the trick of loading assemblies from this location at run-time -there are no custom 'probing' entries in my web.config (instructions to tell .Net to probe custom locations for assemblies) so I'm assuming it's done via reflection.

If you have your own tips for speeding up SharePoint development which aren't commonly-known, I'd be interested to hear...

Sunday, 6 April 2008

Recipe for successful use of Content Deployment Wizard

So my tool, the SharePoint Content Deployment Wizard has been available for some time now and I've been monitoring the feedback and issues people have raised closely. The current version is labelled 'beta 2' but I'm happy with the stability of the current codebase, so will probably re-label it as 'release 1.0' soon (following some feedback on the psychological aspect of the beta label :-)).

Only a small number of people have raised issues, and any problems have almost exclusively been related to the underlying Microsoft code used by the tool rather than the Wizard itself. I should probably be happy about this, but in reality if some people get errors from the tool it doesn't really matter why it happens. The good news is that it seems Microsoft are finally getting some issues with the Content Deployment API sorted at their end. This is a key point in my list of guidance I'd give to anybody running into any errors from the Wizard. Note that the first two apply to use of standard Content Deployment using Central Admin also:

Tip 1 - Service Pack 1 and hotfixes matter

Service Pack 1 fixed many issues with Content Deployment. Unfortunately it also broke some things which had previously been fixed with pre-SP1 hotfixes. It took me a while to realize this, but it's definitely the case. Probably the most common issue in this area is the 'Violation of Primary Key' error. There are reports of being able to work around this by modifying versioning settings on certain libraries, but MS have now released a hotfix very recently which seems to solve the problem for good on SP1 environments. At the moment this is by special request only - the KB to ask for is KB950279. This forum thread discusses this, and it worked for us. Interestingly I spoke to Tyler Butler (Program Manager for Content Deployment) at SPC2008, and he indicated Content Deployment in SharePoint is likely to get "significantly more stable in the next 30-60 days". I'm guessing this hotfix is what he was referring to, or at least part of it.

Tip 2 - always start from a blank site template an empty site created from STSADM -o createsite on the destination

The official guidance currently states that Content Deployment requires that the target site has been created from the 'blank' site template - this is detailed in KB article 923592. However, a better way detailed by Stefan in the comments below is to create an empty site using the STSADM -o createsite command. This is not the same as a site created from the blank template, and is the safest way to create sites which will use Content Deployment or the Wizard. What this means is that even if you're creating a site based on say, the publishing site template in development, any other environments which you wish to deploy content to should be created in this way. Notably, for publishing sites the publishing Feature should also not be enabled for the first deployment - this will be taken care of for you when the first deployment happens. You'll receive the same 'object already exists' error otherwise.

Tip 3 - pay attention to the 'retain object IDs' option

Generally the right option here is to select that you do want to retain the object IDs, and this should be done from the very first deployment - the only exception is when moving webs/lists to a different part of the site structure (reparenting). However, it's important to note that mixing use of Content Deployment or the Wizard with STSADM export/import is likely to cause problems as noted by Stefan in his recommended 'Content Deployment and Migration API - avoiding common problems' post.

A more comprehensive write-up of options available with the Wizard is available at 'Using the SharePoint Content Deployment Wizard'. Also note that's not it as far as the tool goes - in addition to extra functionality such as item-level reparenting and incremental deployment, I hope to refactor the code so that the Wizard would be scriptable from the command-line.

And special thanks go to my colleague Nigel Price for working through the hotfix situation, much appreciated :-)

Monday, 31 March 2008

This blog is changing URL..

..but I'm hoping you won't notice! The new URL is the somewhat simpler www.sharepointnutsandbolts.com, and if everything goes to plan everything will be redirected from old to new, so any bookmarks should continue to work. Or that's the theory promised to me by my blog host, but I thought I should say something just in case :-) Certainly all subscribers to the feed won't need to do anything - the Feedburner URL will stay the same.

Changeover will be later this week - if anybody notices a problem I'm keen to hear, please drop me a comment to let me know!

P.S. I'm likely to switch over to using the CKS : EBE framework at some point soon. A few people have commented that since we moved into 2008, they found it harder to find articles since the navigation on the right buries them under the '2007' node. I've tried to mitigate this by adding the 'Most popular articles' list (as indicated by Feedburner), but I know navigation isn't ideal. Rest assured I'll improve it when I have more control over the site!

Sunday, 16 March 2008

Great controls to be aware of when building SharePoint sites

Something I've been meaning to do for a while is discuss some of the controls I've found useful when putting together SharePoint sites. Obviously before building a custom control for a specific behaviour, it's a good idea to check if SharePoint comes with anything that will do the job. It sometimes surprising what you find! Certainly those from a Content Management Server background will be familiar with the idea of having lots of reusable controls (which used the CMS API) within the team, but in MOSS some of the equivalent controls come for free. The most obvious is the Content by Query web part (though we won't mention that the output HTML isn't accessible [WCAG AA-compliant] without extra work), but some of the smaller controls deserve some attention too. So here's a rundown of some handy items for the toolbox:

SPSecurityTrimmedControl

This control can be used to selectively display content or controls depending on the current user's SharePoint permissions. Whatever is the inner content of the control will therefore not be shown if the user doesn't have the specified permissions. An example would be:

   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" Permissions="ManageWeb">
   2:     This is only visible to users who can manage current web..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

In addition to the Permissions property shown above (enum), the PermissionsString property can be used to specify a comma-separated list of required permissions. This can be used in conjunction with the PermissionsMode property which takes 'All' or 'Any', and also the PermissionContext property which specifies what SharePoint object (e.g. 'CurrentList', 'CurrentFolder', 'CurrentItem', 'CurrentSite', 'RootSite') the specified permission applies to, so it's flexible. Examples of the permissions which can be specified include 'ManageLists', 'AddListItems', 'ViewPages', 'ManagePermissions' and so on. These are members of the SPBasePermission enum and Zac Smith has a full list. Notably many of these permissions are probably more useful in a collab scenario rather than WCM. [UPDATE: Text in this section updated following comment below/further testing:] For WCM folks the control does look like it does something useful in being able to filter on authentication type - unfortunately this doesn't work as advertised. For reference (in case MS fix it), the usage is:


   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
   2:     This *should* be visible only to anonymous users but it doesn't work..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

As you'd expect, valid values here are 'AnonymousUsersOnly', 'AuthenticatedUsersOnly' and 'AllUsers' - however as noted in the comments below 'AuthenticatedUsersOnly' is the only flag which seems to work properly.

There are some other interesting properties too:

  • PagesMode - valid options are 'Design', 'All' and 'Normal'

  • QueryStringParametersToInclude

  • RequiredFeatures

These look like extra properties to filter on, but alas I couldn't get these to work either. My investigative powers let me down here as firing up Reflector onto both SPSecurityTrimmedControl and the related RightsSensitiveVisibilityHelper class didn't give anything away in terms of usage, nor did Google or searching the 12 hive. Certainly it looks like specifying values is all that would be needed for latter two parameters, but I had no joy. A final consideration for SPSecurityTrimmedControl however, is the idea of further possibilities opened by deriving from this control in the same way SPLinkButton does.

EditModePanel

Where the previous control examined the user's permissions to establish whether content should be shown, the EditModePanel looks at whether the current page is in display or edit mode. This can be incredibly useful in the WCM world for displaying help messages or other content to users as they edit a page. However there are other uses - hiding navigation, adding inline CSS override classes to use different formatting (particularly useful) and emitting debug information in the HTML output are all examples. The declaration for this control would look like:

   1: <PublishingWebControls:EditModePanel SuppressTag="false" GroupingText="Title help" 
   2:         PageDisplayMode="Edit" runat="server" id="EditModePanel1">
   3:     Page titles should be concise
   4: </PublishingWebControls:EditModePanel>

At run time in edit mode, this would look like:


EditModePanel

A prettier usage might be to include the field controls within each respective EditModelPanel, meaning the input control is shown within the borders of the box. To do this we'd also need an extra EditModePanel set to PageDisplayMode="Display" which also contained the field control, since the original will display in edit mode only. Note this control will output a surrounding <div> unless the SuppressTag property is set to 'True'.

ListItemProperty

One control you might use in conjunction with the previous control is the ListItemProperty control. This simple little fella allows you to write out a particular field's value for a list item. The field which is rendered is specified with the Property attribute, where we specify the field's internal name. By default, the current list item (i.e. for the page we're viewing) is used, but this can be overridden by specifying the List property - to do this we specify the list GUID with braces:



   1: <SharePointWebControls:ListItemProperty runat="server" id="ListItemProperty1" 
   2:     List="{C110296D-2BE9-4818-80EE-A06BD018DE2F}" ListItemID="1" Property="Title"/>


I think of this control as doing a .ToString() on the contents of the chosen field. This can sometimes be useful in WCM where you want the value to appear in a different location onthe page to where it is edited - for example within a <title> tag. Note we can also specify the version of the list item we want to retrieve the value for with the ListItemVersion property.

ListProperty

Much the same as ListItemProperty except on a list itself. These two may well be used together as the code in a list's DispForm.aspx does:



   1: <SharePoint:ListProperty Property="LinkTitle" runat="server" id="ID_LinkTitle"/>
   2: : 
   3: <SharePoint:ListItemProperty id="ID_ItemProperty" MaxLength="40" runat="server"/>

This will show the current list's title, followed by a colon, followed by the current list item's title.


ProjectProperty


Slightly confusing name, but this control allows us to write out some properties of the current site/web. The MSDN documentation shows the valid list to be:

  • BlogCategoryTitle - Category of the current post item

  • BlogPostTitle - Title of the current post item

  • Description - Description of the current Web site

  • RecycleBinEnabled - 1 if the recycle bin is enabled; otherwise, 0

  • SiteOwnerName - User name of the owner for the current site collection

  • SiteUrl - Full URL of the current site collection

  • Title - Title of the current Web site

  • Url - Full URL of the current Web site
So the following would write out the title of the current web:



   1: <SharePointWebControls:ProjectProperty runat="server" id="ProjectProperty1" 
   2:     Property="Title" />

DelegateControl

This control provides an architecture where an 'outer' control is added to the page layout/master page, but the 'inner' control is determined by a Feature, thus allowing the control to be swapped out without having to modify the original template. This is used widely in Microsoft's master pages. This is a great control, I discuss it more fully in my Using the DelegateControl post.

AssetUrlSelector

To finish on something slightly different, this control can be invaluable when developing custom web parts and field controls. It provides the 'file picker' experience authors are used to when working with SharePoint, and fits well when you have a control which requires a file to be selected. So in several of my controls I allow the user to override the XSL file used for formatting, and the AssetUrlSelector is added to the user control I use for the edit view of the control. This provides me with the 'browse' button which will launch the picker when clicked:


AssetBrowseButton 
Clicking the button then shows:

AssetUrlSelector 
The AssetUrlSelector provides a fairly good user experience, including opening the picker in the location of the currently-selected file if one exists, or defaulting to a specified default location if not. Along similar lines if you need to provide a slick experience to select an image is the RichImageSelector - this is the picker used by the RichImageField control.

Summary

So that's some of the controls I've found useful. Obviously there are stacks more and the ones I did highlight can be used in lots of interesting ways which aren't discussed here. But hopefully this does serve to remind you to check what's in the box before spending time writing your own!