Sunday, 22 August 2010

How to create a BCS lookup field in a content type

Hellloooooo fellow frustrated SharePoint devs!
(Well I assume you are, otherwise you wouldn't be interested in this ...)

I'm currently working on a little project/proof-of-concept which involves heavy usage of the new SharePoint 2010 BCS functionalities. I've already come across a few bumps in the road for which I couldn't immediately find some useful info, so I guess this will be the first of many posts on this subject.

Today I'll start with the following issue: how to use a BCS lookup field in a content type.
Normally this shouldn't be to hard to do:
  1. Create a site column "A"
  2. Create a content type (on site collection level) "CT"
  3. Add a reference to column "A" in content type "CT"
However, no matter how hard I looked, I just couldn't find an option to create a BCS lookup field as site column. Fyi: a BCS lookup field is called External Data field in the UI.
Sure, you can easily create such field on a list level, but not on a site level.


Adding a list BCS lookup. Notice the External Data type


Trying to add a site BCS lookup. No external data type here ... :(



So after googling around without any result, I found it was time to fumble around with my trusty Powershell and see how these list fields are defined.
Apparently the field's SchemaXml is:
<field
type="BusinessData"
displayname="bcs_field"
required="FALSE"
enforceuniquevalues="FALSE"
id="{GUID}"
sourceid="{GUID}"
staticname="bcs_field"
baserenderingtype="Text"
name="bcs_field"
colname="nvarchar11"
rowordinal="0"
version="3"
group=""
systeminstance="SPSDEV1"
entitynamespace="SPSDEV1.BCS"
entityname="dbo_Persons"
bdcfield="Names"
profile=""
hasactions="True"
addfieldoption="AddToAllContentTypes, AddFieldToDefaultView">
</field>
Notable properties are:
  • SystemInstanceName: this is the BCS instance name.
    You can find this value via Central Admin > Application Management > Manage Service Applications > Business Data Connectivity Services. Then select External Systems and select the system's name. This should result in an URL like http://centraladmin/_admin/BDC/ViewBDCLobSystemInstances.aspx?AppId=...

  • EntityNamespace: BCS entity's namespace
  • EntityName: BCS entity you're referencing to
  • BdcFieldName: the BCS field you want to display in the column
Those last three properties' values can be found in the External Content Types overview of Central Admin > Application Management > Manage Service Applications > Business Data Connectivity Services





Armed with this info, I cooked up this piece of code which will create a BCS lookup site column. Of course, you can then reuse this column in any content type.
private string CreateBCSLookupField(SPWeb targetWeb,
string lookupFieldName, string groupName,
string systemInstanceName, string entityNamespace,
string entityName, string entityFieldName, bool hasActions)
{
SPBusinessDataField lookupField =
targetWeb.Fields.CreateNewField("BusinessData", lookupFieldName) as SPBusinessDataField;
lookupField.Group = groupName;
lookupField.SystemInstanceName = systemInstanceName;
lookupField.EntityNamespace = entityNamespace;
lookupField.EntityName = entityName;
lookupField.HasActions = hasActions;
lookupField.BdcFieldName = entityFieldName;
return targetWeb.Fields.Add(lookupField);
} 

*UPDATE*
Okay, apparently there's still something fishy going on behind the scene. I've been using this code for a while now and it seems there's an inconsistency when you create multiple BCS site columns in a row.
Lets say you create 3 columns (A, B, C) with this code.
  • First test:
    A was ok, B too, but C resulted in an error about an improperly configured lookup.
  • Second test (same code, same setup):
    A was ok, B impropertly configged and C was ok
  • Third test (again same setup):
    A failed, B was ok and C failed too
To be honest, I really don't know why it sometimes fails.  It's the same frikking code, but somehow SharePoint sometimes decides to funk things up.
Funking things up apparently comes down to bad property values.  I compared the schema xml's of properly created BCS fields and those that weren't: the correct fields had the same XML as I posted before, while the erronous fields had this xml
<Field  
Type="BusinessData"  
DisplayName="bcs_field"  
Required="FALSE"  
EnforceUniqueValues="FALSE"  
Group="BCS Test"  
ID="{GUID}"  
SourceID="{GUID}"  
StaticName="bcs_field"  
Name="bcs_field"  
BaseRenderingType="Text"  
ColName="nvarchar11"  
RowOrdinal="0" />
As you can see, this xml doesn't have the BCS related properties ... but why?!
Currently I only have one "workaround":
  1. Create BCS field
  2. Check if the resulting fields schema xml contains a reference to SystemInstance (or another BCS related property)
  3. If it doesn't, remove the field and try again.  Usually it'll then be created properly
  4. Rince and repeat if it still doesn't have a correct schema xml (of course, limit the number of repeats to prevent endless loops)
I know, it's definately not kosher ... but I just don't have a clue at the moment on what's causing these missing properties.
Any ideas are welcome :)

Wednesday, 11 August 2010

Querying SharePoint 2010 SPSite from within console application results in FileNotFoundException

Tried to connect to a SharePoint 2010 sitecollection via a console application today, but it kept failing with an FileNotFoundException.
Code:

using(SPSite site = new SPSite("http://sps2010"){
//FileNotFoundException
}

Just for future reference:
  1. make sure your application is using the .NET 3.5 framework
  2. ensure it's compiled as 64 bit

As you'll have guessed by now: my app was using the visual studio 2010 default settings of .NET framework 4.0, compiled as x86 ... which obviously conflicts with the Microsoft.SharePoint assemblies (that are built on 3.5 and as 64bit.) :)

Monday, 28 June 2010

Removing SSL from SharePoint Central Administration

Encountered yet another SharePoint 2007 inconsistency today.

Situation:
One of my customers wanted to test out an SSL-secured central admin page to allow for SSL-secured content deployments. His wish is my command (he's paying the bills after all), so I proceeded with executing the following stsadm command
stsadm -o setadminport -port 443 -ssl

As expected, after clicking on the Sharepoint Central Admin link, we were greeted by a nice https://server****/ url in our browser's address bar. Huzzah!
As can be read on many other blogs covering the setup of SSL in central admin, I then proceeded with installing the SSL certificates to finalize the setup. 'T was a true miracle, but the SSL central admin actually worked.

However, due to some problems with the SSL certificate itself (not the setup), the content deployment tests failed. So we ended up in removing the SSL from the central admin again.
Should be easy stuff, right?

WRONG! Remember, we're dealing with SharePoint after all ;)
My first guess was: "let's run the stsadm again but without the -ssl parameter"
stsadm -o setadminport -port 10000

This resulted in a nice "Operation completed succesfully" message, but when we clicked on the central admin link ... we were greeted by a less nice https://server***:10000. So, looks like the command succesfully changed the portnumber, but unfortunately it kept the https part.

Tried some other things, but they all failed. The portnumber changed, but the https part didn't move an inch.
So it was time to bring out the big guns:
  • .NET Reflector (awesome tool!)
  • Powershell (even more awesome for these situations!)
Ran a reflector on the stsadm.exe command, after some digging I found the code responsible for the setadminport operation:
Microsoft.SharePoint.Administration.SPGlobalAdmin.SetAdminPort()

(not going to post the code here, don't wanna risk lawsuits ... if you're after the code: disassemble it yourself ;))


In general, this method will open the SPAdministrationWebApplication.Local object, change its ServerBindings or SecureBindings properties (based on the -ssl flag), modify the AlternateUrls SPAlternateUrlCollection and finally create a webapp provisioning job.

Armed with that information, I started fiddling around in Powershell. Remember kids, only fiddle around in a VM with a recent snapshot ... don't do this on production servers! You'll find out why in a couple of paragraphs :)

First thing I noticed: after executing the "stsadm -o setadminport -port 10000", I ended up with 2 alternate url's in the AlternateUrls property for the webapp's default zone. Looks like we're getting a bit warmer:
  • one with https://server*:10000 as incoming url
  • one with http://server*:10000 as incoming url
Attempt 1:
Tried to manually delete the https alternate url by calling AlternateUrls.Delete(0). However, calling Update() on the AlternateUrls property will then result in dependency errors and a failing central admin.
There was no way to fix this, so I ended up in restoring a VM snapshot :)


Attempt 2:
Instead of removing the urls, I tried calling the SetResponseUrl method on the AlternateUrls property & passed the http://server*:10000 as parameter.
Guess what: this method replaced both alternate url definitions with a single alternate url (which I had supplied in the method parameters). Result!

For all you lazy arses out there, I used the following Powershell script to solve it. Maybe it's easier to read than my ranting :)
//Load the SharePoint assembly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

//Get the central admin webapp
$cAdmApp = [Microsoft.SharePoint.Administration.SPAdministrationWebApplication]
::Local

//Create a new alternate url
$altUrl = New-Object "Microsoft.SharePoint.Administration.SPAlternateUrl"
-ArgumentList "http://server:10000", "Default"

//Set this alternate url as response url
$cAdmApp.AlternateUrls.SetResponseUrl( $altUrl )
$cAdmApp.AlternateUrls.Update()

//Get the alternate urls definition, this should only list our
//http://server:10000 url
$cAdmApp.AlternateUrls
... and there was much rejoicing.

Thursday, 24 June 2010

Importing SharePoint 2007 list templates (STP) into SharePoint 2010

Damn, only my second post and I'm already covering *shiver* SharePoint.

Anyway, I've been working on migrating an existing WSS 3.0 customisation to a new SPF 2010 environment today. Most of the stuff was pretty straightforward (some webparts, webservices, a few event handlers, ...), but there was one thing that didn't work out as planned.

My predecessor relied on list templates stored in STP files to deploy the custom list templates and create some lists instances based on them.

(This deployment involved manual uploading & creating of lists, maybe I'll do a quick overview on how you can automate this in the coming days... pretty easy stuff tho).


Now, while STP site templates are no longer supported, STP list templates
should still work in SharePoint 2010. Unfortunately things were a bit trickier than I expected them to be.

As a test I started with uploading one of the original STP's to the template catalog (
_catalogs/lt). No problem so far, but when I tried to create a list based off that template I got the following error:
Error
Microsoft SharePoint Foundation version 3 templates are not supported in this version of the product
Correlation ID: {random guid}
Date and Time: ....

Dang, that didn't look to good. Googling around didn't help either, as they all suggested on installing the original solution in a 2007 environment, upgrading the environment to 2010 & then exporting the list again as a template.

As this wasn't an option, I decided to delve deeper into the STP file. As you might know, these STP files are just renamed CAB files. This means you can safely rename them again to .CAB and extract their contents ... which I did.

Doing so revealed that my STP/CAB file only contained a single file: manifest.xml. This file contains the definition of your list: its fields, contenttypes, views, ...
To have some data to compare to, I created a new, custom list in SPF2010 and exported it as an STP template. Once again I renamed the file to .CAB and viewed its contents ... and there was much rejoicing.

It turned out that the SPF2010 STP had the same content as the WSS 3.0 STP: just a single manifest.xml. A quick diff between the two
manifest.xml files revealed that they're both quite similar and that there's a ProductVersion element in both files.
In the WSS 3.0 version, this element has a value of 3 ... but in the SPF2010 version, it has a value of 4. Could it be solved this easily?

Guess it was. I changed the value of the
ProductVersion element in the original manifest.xml to 4 and repackaged it into a CAB. Renamed the CAB to STP and uploaded it again in SPF2010 and behold ... no more errors when creating lists from that template!

Summary
So if you need to migrate list templates STP's from SharePoint 2007 to 2010, you could try the following:

  1. Rename the original .STP to .CAB
  2. Extract its manifest.xml to a local folder (lets call it {workingfolder})
  3. Search for the ProductVersion element. This should have a value of 3
  4. Change its value to 4
  5. Repackage the manifest.xml into a .CAB. I've done this by using makecab.exe in the C:\Windows\System32 folder
    Syntax
    : makecab.exe {workingfolder}\manifest.xml {workingfolder}\{template-name}.cab
  6. Change the generated cabinet's extension from .CAB back to .STP and upload it into the _catalogs/lt
Update
For those looking to migrate a 2007 doc lib with content: apparently a fellow SharePoint-victim has written a powershell script for this - http://www.heyweb.net/2011/06/converting-sharepoint-2007-document-library-templates-for-sharepoint-2010/
Kudos to Peter! :)

Hell must be freezing over ...

After all these years of avoiding all kinds of 'social' web 2.0 stuff, I succumbed and created a blog.
Although, I guess it doesn't really count as 'social' medium if I don't reply on any comment ... which I'm planning to do. Hah, dodged a bullet there!

So why did I start this blog?
Actually, I don't really know. Chances are that you'll never hear from me again after a few posts, but I promised some mates that I would at least try it out ... so here we are.

For now I'd like to think of this blog as my personal notebook.
Should I come across some stuff that I find interesting, then I'll write it down here. I used to write such things down on a piece of paper or a post-it note (best invention ever!), but recent events have taught me that I'm not too good at 'archiving' these bits of paper. :)
Lets hope this piece of blogging software does a better job.

Topics you can except to pop up:
  • .NET related topics (I'm a .NET developer after all)
  • Quite a few angry posts on SharePoint 2007/2010 problems (unfortunately I'm supposed to be a SharePoint consultant too)
    Who knows, maybe I'll be able to post some solutions too!
  • Tips on bonsaï tree trimming! Never done it, but I'm sure I'll cover it one day ...
  • ... and much more useless stuff!

But first things first: time to figure out how to put a better styling on this site.

By the way, feel free to slap me if I ever mention web 2.0 again, damn the guy/girl/demon spawned from the arse of beelzebub who invented that term.