Skip to main content

Click Mice, Unclick Mice

Go Search
Home
Wikin
Windows Live ID Authentication
  

Click Mice, Unclick Mice > Categories
Microsoft Office 2007  Development Environments pt. 2
A new version of the whitepaper has been released.  The major updates are around Visual Studio 2008.  There has been a section added to include more detailed information around setting up Visual Studio 2008.  There are also new project samples including a sample on a web part, list definition and a document level addin (for VS 2008).
CKS: Windows Live Authentication
I know it has been a little while since I have posted something.  I have been working on the Community Kit for SharePoint in the last few weeks.  I have been working on the Enhanced Blog Edition, but probably the bigger contribution was incorporating my Windows Live Authentication implementation in to the CKS.  I took what I had from my 1.0 release and rolled it in to the CKS solution.  I am a member of the Puget Sound SharePoint User Group and they decided to use the new CKS: WLA module which showed something was missing, so I added profile syncronization to my initial release.  What this really means is that going forward I will be doing my work in the CKS project on CodePlex so feel free to get involved as well!
 
Lawrence Liu has some screenshots and additional information posted on the SharePoint Team Blog here.
Windows Live ID Authentication for SharePoint Released
Sorry to those people I had promised an install to last week.  I got caught up in a few other projects, but I have completed the testing and have a SharePoint solution prepared for download.  I have created a site for any issues or feedback on the authentication provider, so feel free to go grab a copy and take a look.  You can find it on the Windows Live ID Authentication blog.  The solution has been tested on  a few different systems and hopefully the installation and instructions are easy for everyone to follow.  Big thanks to Todd Klindt for helping me nail down the some installation usability issues and for his feedback on the documentation.
 
Later this week I will start a series of posts about the implementation to give everyone an idea of how it works.
SharePoint List Performance
Anyone who has dealt with development in SharePoint has likely had to deal with SPList, SPListItem and SPListItemCollection.  Recently I have been working on some projects that were experiencing some performance issues.  Looking at the code, I started to realize that while I had heard a lot of things from Microsoft (specifically the SharePoint product team) about how much the SharePoint list performance had been increased, I had not actually seen a lot of publications around where, how and some good ways to take advantage of them.  Certainly there are articles about Indexed columns, and many about views only up to 1000-2000 items.  I decided to dig a little deeper, and I think you might be as surprised as I was about what I found.
 
Test Data / New Item Performance
For part of the test, I decided I should combine the testing with creating the test data.  One of the performance items I wanted to test was how SharePoint handled new items.  Specifically, I wanted to know how quickly new items could be added, and I wanted a list in the end with 100,000 list items.
 
What I found was that SharePoint could really tear in when it came to creating a new item.  Now I had a very simple list, it consisted of:
  • SingleText - A single text column that I used to put GUID's in to
  • Number - A number column I used to put a counter in to
  • Choice - A choice column I used to put a text choice in to
  • Bool - A boolean column I put either true or false in too (what choice did I have right?)

On my test hardware I could push about 50 items per second.  I was actually pretty happy with that performance.  It means I could populate 10,000 items in around 3.5 minutes.  So how did I come by these numbers?  I setup a list as above; I just used the Custom List template, and I used the following code:

  DateTime start1 = DateTime.Now;
  for (int i = 0; i < numItems; i++)
  {
    cn = rand.Next(0,3);
    SPListItem item = list.Items.Add();
    item["SingleText"] = Guid.NewGuid().ToString();
    item["Number"] = itemCount + i;
    item["Choice"] = choice[cn];
    item["Bool"] = (cn <= 1);
    item.Update();
  }
  DateTime end1 = DateTime.Now;
  TimeSpan timer1 = new TimeSpan(end1.Ticks - start1.Ticks);

As you can see this code sets a time before and after the for loop which we can use to figure out how many seconds total were spent in the loop.  I tested this by adding 1, 10, 100, 1,000, 10,000 and finally around 85,000 items to the list, and in general the timing of around 50 list items per second seemed to hold true for my hardware. 

Updating Items Performance
There are really two ways to update items in a SharePoint list.  Either you use the SPListItem.Update() method or you use the Lists web service UpdateListItems() web method.  I decided to try them both, and what I found really surprised me.  As I looked in to it more, I found some things I really hadn't expected. 
 
SPListItem.Update()
The first thing I stumbled on to was SPListItemCollection's Count property.  Apparently, using it forces some enumeration of the SPListItemCollection.  In my initial code to update items, I was using the Count property in a for loop.  The performance of that trial was just awful.  I looked in to it more and I found that by moving the SPListItemCollection Count property access outside of the for loop in to an integer variable, I was able to increase performance dramatically.  When I was testing this I had around 5,000 items in my list, and every call to the Count property was costing me around 2.5 seconds.  So an important note to anyone that uses the Count property of a SPListItemCollection - Make sure you only call it once and then put it in to a variable you control so you can refer to that instead of calling it again. 
 
After I tracked down the Count snafu I was well on to my way to getting numbers for the Update performance.  What I found is that I was only able to update 1 list item every 2.8 seconds.  Surprised?  I was.  Now as I put that together with what I found with the Count property, I had to wonder if some list item collection isn't being enumerated.  It seems quicker to delete an item and create  a new copy.  Here is the code I used to test this update:
 
  DateTime start2 = DateTime.Now;
  for (int i = 0; i < numItems; i++)
  {
    cn = rand.Next(0,3);
    list.Items[i]["SingleText"] = Guid.NewGuid().ToString();
    list.Items[i]["Number"] = itemCount+ i;
    list.Items[i]["Choice"] = choice[cn];
    list.Items[i]["Bool"] = (cn <= 1);
    list.Items[i].Update();
  }
  DateTime end2 = DateTime.Now;
  TimeSpan timer2 = new TimeSpan(end2.Ticks - start2.Ticks);
 
I am doing the same thing here with the time being set before and after the for loop.  I am afraid I only had the patience to test this with 1, 10, 100 and 1,000 items.  It was just taking too long.
 
Web Service UpdateListItems() Performance
I came to this part of the testing a little biased.  I had just witnessed the poor performance of the SPListItem.Update() and, as I stated earlier, I fully expected the web service to perform even worse.  But I was pretty much as wrong as a person can be.  UpdateListItems() allows you to submit items in batch using a XML item update definition.  This allows you to submit multiple items at once if you have them all to submit (I did find that 400 or so is the max you want to submit at once).  I hit my test button fully expecting that I would have to go make a sandwich while I waited, but as I was starting to stand up it came back as completed.  I blinked a little, thinking it was broken, but it had updated the 100 items I told it to.  As surprising as it sounds, the web service was pushing updates at the comparitively blazing speed of 50 items per second.  It was matching the creation speed of new items.  If you are as puzzled as I am about why well I am thinking of starting a support group.  As a performance tip in your code: 
Do - use the web service to update list items if you need to update them yourself. 
Don't - use SPListItem.Update() in your code if you have to update more than one item at a time. 
 
Here is the code I used for testing:
 
  DateTime start1 = DateTime.Now; 
  Lists listService = new Lists();
  listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
  listService.Url = "
http://server/site/_vti_bin/Lists.asmx";
  System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
  System.Xml.XmlElement batchElement = doc.CreateElement("Batch");
  batchElement.SetAttribute("OnError", "Continue");
  batchElement.SetAttribute("ListVersion", "1");
  batchElement.SetAttribute("ViewName", "{F15B3020-EF3F-4458-B8A2-FD7F10725F82}");
  for (int i = 1; i < numItems + 1; i++)
  {
    cn = rand.Next(0, 3);
    xml.AppendFormat("<Method ID='{0}' Cmd='Update'><Field Name='ID'>{0}</Field><Field Name='SingleText'>{1}</Field><Field Name='Number'>{2}</Field><Field Name='Choice'>{3}</Field><Field Name='Bool'>{4}</Field></Method>",i, Guid.NewGuid(), itemCount+i, choice[cn], (cn <= 1));
  }
  batchElement.InnerXml = xml.ToString();
  listService.UpdateListItems("TestDB", batchElement);
  DateTime end1 = DateTime.Now;
  TimeSpan timer1 = new TimeSpan(end1.Ticks - start1.Ticks);
 
I have so much more code quoted here because I was timing the additional cost of having to setup the web service connection, and it still was over 100 times faster than using SPListItem.Update().
 
Reading / Querying Item Performance
Once I had my 100,000 list items, I was not expecting spectacular performance on my list queries.  I was pleasantly surprised by what I found.  While the queries could take some time, there were certainly ways to get around those issues.  I was able to query the list with various different CAML queries with an average speed of around 2,000 items per second.  This speed held true when querying through the Lists web service GetItems() method as well.  This meant when I queried my 100,000 item list and I returned 89,000 items in the result set, it took around 44 seconds to completed the query.  I found that presorting the items in the query using <OrderBy> didn't affect the performance noticably at all.  I did these two queries:
 
  <Where><Contains><FieldRef='SingleText'/><Value Type='Text'>8</Value></Contains></Where>
  <OrderBy><FieldRef Name='Number'Ascending='FALSE'/></OrderBy><Where><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains></Where>
 

There was less than a tenth of a second difference in the average time to process the query.  So a performance hint:

Do - use the RowLimit property in the SPQuery class if you don't need to return all the results, and make the limit as small as you can while using <OrderBy> to make sure you get the most relevant results.

 
Queries I tested:
  <Where><Contains><FieldRef='SingleText'/><Value Type='Text'>8</Value></Contains></Where> (search a GUID for a single character of text)
  <OrderBy><FieldRef Name='Number'Ascending='FALSE'/></OrderBy><Where><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains></Where> (search a GUID for a single character of text and return a sorted list)
  <Where><And><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains><Contains><FieldRef Name='SingleText'/><Value Type='Text'>a</Value></Contains></And></Where> (search a GUID for a the character '8' and the character 'a')
  <Where><And><Lt><FieldRef Name='Number'/><Value Type='Text'>95000</Value></Lt><Gt><FieldRef Name='Number'/><Value Type='Text'>10000</Gt></Contains></And></Where> (search for all values in the Number field between 10,000 and 95,000)
 
Manipulating Data
As part of this performance data, I decided it was probably worth the time to check how well different methods of using the data were performing.  One of the things I see a lot is the results of a query being loaded in to a DataSet or DataTable to be combined with content from a backend system or SQL table.  I had found a new method available in the SPListItemCollection that I wanted to test.  GetDataTable() is the method and it does exactly what I thought it did.  It returns a data table populated with all the data in the SPListItemCollection.  Once you have a DataTable there are several time saving tricks to make SharePoint lists more attractive as a data source.  I found that, in general, it took more time to use the GetDataTable() method than it took to do a foreach loop and copy the data in to a new DataTable.  This seemed really odd to me, but I thought perhaps I was running in to the same performance issue I was seeing in the SPListItemCollection Count property.  It was able to process 1,400 items per second.  So my results that were around 85,000 items took about one minute to generate with GetDataTable(), and about 48 seconds with the foreach loop (putting the performance there at about 1800 items per second).
 
Wrap Up
I will summarize what I have found.
 
Action
Performance
Add New Items
50 items/sec
SPListItem.Update()
2.8 secs/item
Web Service UpdateListItems()
50 items/sec
SPQuery
2,000 items/sec
SPListItemCollection.GetDataTable()
1,400 items/sec
Manually Create DataTable

1,800 items/sec

 
Keep in mind that this is of course on my hardware, which is a 64-bit dual core machine with 4GB RAM.  Your mileage may vary. 
 
Another important discovery worthy of summarizing is the realization that you should limit calls to the SPListItemCollection.Count property. 
Windows Live ID Authentication in SharePoint
If you have been keeping track of Windows Live ID (formerly known as Passport) at all, you should have seen the announcements about the Windows Live ID Web Authentication SDK being released.  I was pretty happy to see this myself.  I was a member of their beta program because I was interested in how this authentication would work with SharePoint.  So I gave myself a weekend project.  I decided I would write a SharePoint Windows Live ID Membership Provider.  While I was in the middle of developing the Membership Provider, I found I needed to also create a Role Provider to ensure that I met all of my requirements.
 
I am in the process of creating a SharePoint solution to allow others to make use of the providers, however as I finish that up you might be wondering how it all works.  Well, the easy answer is to click on the Sign In link in the upper right hand corner of this page.  It will step you through the login process.
 
Click On The Sign In Link
The Membership and Role providers are linked in to the SharePoint Forms Based Authentication (FBA) architecture.  This means that the Sign in and Sign Out mechanisms are integrated right in to SharePoint automagically.
 
Get Redirected To Live's Login Site
When you click the Sign In link you will be redirected to the Windows Live ID login site.  This site validates the users credentials, either by requiring them to login with their Live ID credentials or by validating that they have an active Windows Live session.
 
Get Redirected Back to SharePoint
Once the Windows Live session is validated, the Live Login system will redirect you back to SharePoint.  The Windows Live system requires an Application Administrator to register a single URL per application ID to return users to once they have been validated.  The Windows Live request returns a signed authentication token that the SharePoint server verifies, and then uses to identify the user by their unique user token.  This token is unique to each Application that is registered to the Windows Live system.  What this means is if I register an application for blog.solanite.com and one for moreblogs.solanite.com I will get a different ID's for the same Windows Live ID on each system.  These unique user tokens only provide the SharePoint server with validation that Windows Live has authenticated this user.  It does not provide access to the Windows Live associated email, or really any information about the user.
 
User Token Is Processed
When the Windows Live system responds to SharePoint, the user token is checked against the profile store.  In the case of what I have written, I decided to test Microsoft's statement that with the performance tweaking abilities added to SharePoint, you can technically use a SharePoint list instead of using a SQL table for simple applications.  I just did a few web searches, and can't find any published versions of this statement, but it is something I and others I know have heard several times.  I have set the user token and email columns of the profile list as indexed to help with searching against them, and I am hopeful that I'll see good results as the number of profiles increases. 
If this user token is new, the user is prompted to enter an email identifier.  This is done to help with adding users to SharePoint sites.  Remembering a long user token is difficult to do, however typing in an email address is much easier.  The profile store itself is not accessible, unless you have specific access to the list so the data is protected.
 
Returned To The Sign In Page
Once either the new user has submitted an email address or the pre-existing user check is completed, you are redirected back in to the SharePoint page you initially signed in from. 
 
Roles
As I was working with the Membership provider I realized I was going to want to be able to add the equivalent of NT_AUTHORITY\Authenticated Users to a SharePoint site.  The answer was to create a Role provider that implemented one role, Authenticated Live Users.  This solved my primary issue, and allows someone to invite all Live users to a site.
 
Drawbacks
This is not a perfect system, however it does accomplish what it is should.  Some of the major issues come from the Windows Live ID system itself.  It does not share the email address of the Windows Live ID with the application, so in order to get a human readable identifier for the user they must be asked to provide an email (which is less than ideal). 
Another issue is how the Membership provider works.  When using WSS (not MOSS) users added from a Membership provider get a display name that is the same as their UserName in the provider.  There isn't a way to acquire a Display Name automatically, which leads to the next problem.  Users can't change their profile information in SharePoint unless they have contributor access to the site they are currently in (by default at least).   I understand the reason behind it, but it is still annoying in this particular case; however, the solution is pretty simple by adding the Edit Personal User Information  permission in to the Read permission level.  Since we have the Authenticated Live Users role then all Live users can be added with Read access.
 
Wrap Up
My intention is to finish up the solution sometime yet today or tomorrow, and let a few people I know test it out to make sure it installs smoothly.  Once it seems to be working well on their systems, I'll put it up for download assuming there is interest.  If you are interested and want to help out, feel free to give the Live integration a shake down on this site.  Please leave any comments and suggestions here.  I am happy to hear about the things you liked and the things you didn't. 
Debugging obtuse SharePoint errors
  • An unknown error has occurred.
  • An unexpected error has occurred.
If you have ever done any custom web parts or other custom code in a SharePoint installation you have probably seen this message.  Lately I have also found this set of steps useful for tracking down other odd things like the GroupBoard Workspace issue.  There are lots of reasons this can happen, but I think in all the debugging I have done the most prevalent reason for it has been someone closing an SPSite or an SPWeb that they pulled from the SPContext (GetContextWeb/Site in v2).  So if you see one of these messages, or any terribly vague SharePoint message, I would ask yourself if you are closing an SPSite or SPWeb and if so double check if you should.  If you have done that, well read on and hopefully this helps.
 
Changing the web.config
Well if you have double checked your work and are still seeing the error then there is only one choice.  Part the fog of SharePoint's error and take a look at the stack trace for the error.  There are two things you will need to do to make this happen. 
  1. Turn off Custom Errors: This tag basically controls what error pages your web site will show when an error occurs.  In most cases you would not want to show a full stack trace to the world when an error occurs so I would opt for setting this to On for most of the time and RemoteOnly when you want to debug something.  If it is a dev only system though knock yourself out and set it to Off.  You can set this tag in the <system.web> tag though it should already exist for you to modify.  More info on the tag here.
    • On: This will always show a simple error has occured page and will not show details.
    • RemoteOnly: This will show a simple error has occured page to remote clients and a full stack trace to a browser from the local server.
    • Off: This shows a full stack trace to everyone when an error occurs.
    • Ex: <customErrors mode="RemoteOnly" />
  2. Set the SafeMode CallStack: This is my favorite setting for SharePoint, but it comes with a price.  You can set the CallStack to true, and it will do wonders for tracking down many errors that otherwise result in the "An unknown error" type messages, however it has a dark side.  It can also hide other errors.  I have used this method to trace issues in code in SharePoint v2 and found that a fully reproducable case was "fixed" by setting the CallStack to true.  Hardly what I was looking for when I did it, but worth noting that this is not a silver bullet for all unknown error messages.  Setting this attribute can mask some poor coding standards by making SharePoint slightly more lenient in its memory cleanup and allocation.  You can find the <SafeMode> tag in the <SharePoint> tag in the web.config.  CallStack in an attribute inside that tag.  By default it is set to false.  To enable printing of the stack trace just set it to true.
    • Ex: <SafeMode MaxControls="50" CallStack="true"/>
Reparenting a Site in SharePoint
WSS v3 and MOSS 2007 have given us new capabilities from an administrative point of view with at least 3 different ways to reparent a site within a site collection without having to resort to messy SQL changes or many backups and restores.
 
It is important to point out here that you can only use these methods to rename webs inside the same site collection and that the new name's full path must exist.  I.e. don't rename a web to test/bar if there is no web named test. 

STSADM

This is by far the easiest method to use and has all the functionality available.  Though I may be a little partial since I love a good command line application.  You will find STSADM in your SharePoint bin directory usually located in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin and it has a handy renameweb operation built in to it.

Examples:

stsadm -o renameweb -url http://server/sampleweb -newname example (this will rename the web to a URL of http://server/example)

stsadm -o renameweb -url http://server/sites/foo/test/bar -newname bar (this will rename the web to a URL of http://server/sites/foo/bar)

stsadm -o renameweb -url http://server/sites/foo/bar -newname test/bar (this will rename the web to a URL of http://server/sites/foo/test/bar)

Should also mention if it isn't obvious from the examples that the newname variable is site collection relative.

SiteManager web UI

First you will need to bring up the UI.  The URL to the Site Manager is only available in MOSS and you can find it at http://server/_layouts/SiteManager.aspx.  Now you can change that to any site url and add the _layouts/SiteManager.aspx, but keep in mind that if you are moving things around you probably want to hit the top-level of the site collection to do this. 

Once you have the Site Manager page up it is pretty simple to use.

  1. Expand the tree on the right hand side to find the site you want to move
  2. Click on the parent of that site in the right hand tree to load the left hand panel
  3. Check the checkbox next to the site you want to move
  4. Click on the Actions -> Move menu option
  5. Expand the tree in the popup Move dialog to find the new parent site
  6. Click on the parent site in the tree and click the Ok button

This method has the benefit of being more visual, however it does lack so of the power of STSADM since you can't rename the URL with it.  You have to go in to Site Actions -> Site Settings -> Title, description, and icon to change the URL name.  This mean a two step process to reparent and rename a web through the Web UI as opposed to the one step with STSADM.

SharePoint API

The last method for reparenting a site is using the SharePoint API.  Microsoft has made it much easier to manage this now and actually went so far as to make the ServerRelativeUrl property read/write.  So the easiest way to explain how to use the API is probably to give a sample.

string fullUrl = "http://server/site/subsite";

string newRelativeUrl = "/site2";

using (SPSite site = new SPSite(fullUrl))

using (SPWeb web = site.OpenWeb())

{

     web.ServerRelativeUrl = newRelativeUrl;

     web.Update();

}

This would rename http://server/site/subsite to http://server/site2.  Changing newRelativeUrl to:

string newRelativeUrl = "site2";

Would rename the http://server/site/subsite to http://server/site/site2.  Just to give you an idea of the difference between how to rename the Url in the parent web vs. renaming the Url in the site collection.

Microsoft Office 2007  Development Environments
Not exactly breaking news since it has been published for a couple of months, but this whitepaper does cover in a pretty step-by-step manner how to install and configure an Office client and Office server development environment.  There are some sample projects that are available for download with the whitepaper on MSDN also.  Basically two office client samples (a Word add-in that covers adding a custom ribbon extension and a Word add-in that covers adding a custom task pane) and two office server samples (a workflow sample and an event handler sample).