tisdag 10 juni 2008

Linq for SharePoint

Do you love Linq? Well, now you can have it with sharepoint with the use of LINQ4SP.

måndag 2 juni 2008

Basic SPQuery problems

I'm new to caml and SPQuery and I've build this simple search webpart for the Case Management project I'm on. And the frustration starts. I've used the U2U CAML Query Builder which is a really nice tool. Somehow all of the queries I've copied from that tool has returned all rows when using SPQuery and GetItems() on the list I want to select from. Turns out you have to remove the <QUERY>-tags.

Basic query with SPQuery

List<SPListItem> cases = new List<SPListItem>();

SPSite site = Helper.GetDefaultSite();

SPWeb web = site.OpenWeb();

SPList list = web.Lists["myCaseList"];

SPQuery query = new SPQuery();

query.Query = "<Where><Eq><FieldRef Name='ProblemType'/><Value Type='Text'>6</Value></Eq></Where>";

SPListItemCollection foundItems = list.GetItems(query);

if (foundItems.Count > 0)

foreach (SPListItem item in foundItems)

cases.Add(item);

caseList.Cases = cases;

Annoying WSS errors

It feels like I've gone through all of the classic errors that someone new to WSS will encounter. I will fill up this post as I encounter more and unfortunatly I can't remember all of the problems that I've gone through. =) .. Somehow it feels like the SharePoint-team is sponsored by the mental institutions and have signed an agreement to bring them a certain amount of new patients that needs therapy every year. I know that I soon might need it. Anyways...

Value does not fall within the expected range
This means the same as "index outside of range" or "the key is not present in the dictionary". Or make it simple, you have referenced something in a dictionary where the key isn't present. Like Trying to get a list from the SPWeb-object that doesn't exists. This troubled me for quite some time.

Cannot complete this action
Is most commonly due to user rights, or, the lack of. WSS need you to impersonate before doing certain kinds of tasks. Read this post. Another fantastic description of the problem. Not vague at all.

One or more field types are not installed properly. Go to the list settings page to delete these fields.
When you get this error, it probably is due to a reference to a field with special character in it's name. For instance, if you have a field namned "Problem type" it has to be referenced as "ProblemType" (WSS 3.0). To avoid this, and you really should, never name a field with a non-alphanumeric character. Use only numbers and letters.

Exception occurred. (Exception from HRESULT: 0x80020009 (Part 1)(DISP_E_EXCEPTION))
From what I've seen, this can come up on alot of different instances. This guy, David, has a solution for it in his blog.

To sum up his post:
stsadm.exe -o export -url http://yourweb/ -filename c:\yourweb.cab
stsadm.exe -o deletesite -url http://yourweb/
stsadm.exe -o import -url http://yourweb/ -filename c:\yourweb.cab

Worked fine for me, and resolved a problem with cached list templates that refused to take on new fields even though i retracted and deleted the solution where they originated from.

Exception occurred. (Exception from HRESULT: 0x80020009 (DISP_E_EXCEPTION)) (Part 2)
The problem resurfaced when I tried to use a SPQuery to search for a SPUser. All other searches works fine. Another blog suggested that it could be due to a disposed SPSite. I notices that I had typed "type" instead of "Type" in the <Value>-tag. I changed it to a capital T and everything worked fine.

Exception from HRESULT: 0x81070215
Go to your schema.xml for the list you are trying to create and change the Name attribute of the element in your ElementManifest-file to the same name as the directory for the list (where you have put your schema.xml).

Creating webparts outside of SharePoint

After going through a painfull process of creating a webpart, installing it, testing it, putting all debug information out in labels and then iterate through all of it again I got tired. I worked on a VM in a cramped session-window.

That's when I evolved a simple Helper-class for get default sites and such so I could test the webpart outside of WSS and just put it in when needed.

Now i just use Helper.GetDefaultSite() and specifying an application setting ("CM.Debug") with a value of true if I'm of the bars of WSS. Just remeber not to dispose the SPSite or SPWeb if you're within WSS context.

The thing is, when you try to do some actions, such as updating an SPListItem, you will get an error unless you impersonate as yourself. Inside of the GetDefaultSite() I've included a call to GetUserToken() which gets the SPUserToken for you. If you havn't impersonated as yourself, you will get the wonderful error message "Cannot complete this action". Thank you SharePoint for being so precise in your error messages =). Feels like all of their is wrapped inside a try-catch and always return something like that, hehe.

Helper class

public static class Helper

{

public static bool Debug

{

get { return bool.Parse(GetSetting("CM.Debug")); }

}

public static string DebugUrl

{

get { return GetSetting("CM.DebugUrl"); } //http://WSS/sites/yoursite

}


public static SPUserToken GetUserToken()

{

SPSite site = new SPSite(debugUrl);

SPWeb web = site.OpenWeb();

SPUserToken token = null;

if (Debug)

token = web.AllUsers[WindowsIdentity.GetCurrent().Name].UserToken;

else

token = web.CurrentUser.UserToken;

web.Dispose();

site.Dispose();

return token;

}



public static SPSite GetDefaultSite()

{

if (Debug)

return new SPSite(debugUrl, GetUserToken());

else

return SPSite(SPContext.Current.Site.ID);

}

public static string GetSetting(string setting)

{

return System.Web.Configuration.WebConfigurationManager.AppSettings[setting];

}


Example code using Helper


public static Case Load(int id)

{

SPSite site = Helper.GetDefaultSite();

SPWeb web = site.OpenWeb();

SPList list = web.Lists["myCaseList"];

Case loadedCase = new Case();

loadedCase.Loaded = true;

if (list == null)

return null;

foreach (SPListItem item in list.Items)

{

... some code

}

if (Helper.Debug)

{

web.Dispose();

site.Dispose();

}

return loadedCase;

}


Another way to impersonate


System.Security.Principal.WindowsImpersonationContext wic = null;

wic = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate();

SPSite site = new SPSite(...);

SPWeb web = site.OpenWeb(...);

....

wic.Undo();