Tuesday, October 21, 2008

Programmatically set a BDC column

********** UPDATE **********

I've created a new post as a follow up to this article. It contains C# code for a class I created to work with business data in SharePoint 2007. There is a public method in there called "SetFieldByID" which will set the value of a BDC column including all auxiliary fields.

Click here to view the post.

*****************************


Finally!

I spent way too much time playing around with this one... There is no documentation, and very little discussion about it in the forums. All I wanted to do was be able to set the value(s) of a Business Data Catalog field programatically through the SharePoint object model.

Sounds simple enough right?

Not quite....

The first thing to note is that you can't just set the value of the column like you would do with other fields like so:

mySPListItem["myBDCField"] = 123;

If you do this, you won't get an error, but the value will not be set...

This reference was a useful start: http://www.portalsolutions.net/Blog/Lists/Posts/Post.aspx?ID=18. However, I still encountered some challenges in setting a BDC column value.

First of all, the EntityInstanceIdEncoder class is found in the following namespace:
Microsoft.Office.Server.ApplicationRegistry.Infrastructure;
The referenced dll is found in the ISAPI folder on the SharePoint server:
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI

Now for some code...

To check if a field is a BDC field:

if(spItem.Fields["SomeField"].TypeAsString == "BusinessData")


To get the BDC field:
SPField bdcField = spItem.Fields[sBDCColumnName];

To get the BDC column entity internal name (this is key):
XmlDocument xmlData = new XmlDocument();
xmlData.LoadXml(bdcField.SchemaXml);
String sEntityName = xmlData.FirstChild.Attributes["RelatedFieldWssStaticName"].Value;

Set the primary key of the BDC field:
spItem[sEntityName] = EntityInstanceIdEncoder.EncodeEntityInstanceId(new object[] { Value }); - Here you may want to add some logic to cast the the passed value to the proper type.

Set the display value of the column:
spItem[sBDCColumnName] = Value;

You also need to explicitly set the values of any of the additional related fields. These values can be set like any other column:

spItem[ BDCFieldName + ": " + BDCDisplayFieldName ] = "SomeValue";

// ex: spItem["Products: Name"] = "XYZ";

Hope this saves somebody the trouble that I went through to figure this out!




19 comments:

Anonymous said...

Hello,
Thanks for the useful article.

However, I have one problem: I can update existing BDC fields from a document library but not when new items are added (where BDC value does not exist).
I have o Document Library and try to upload a document and set the BDC field. I get a "(Blank)" value in BDC column and when I click the Sharepoint's update button everything works fine.

Any ideas??
Thank You in advance

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

Hi Loomy,

Are you explicitly setting the descriptive property for the key? For example, lets say I have a BDC field called customerID. I have found that you need to assign the value to a field labelled "customerID: customerID". SharePoint uses a separate placeholder for the key, and the display value. So, you are getting a value of (Blank) because the BDC field has a key, but those display fields were not given a value. Hope that helps...

Loomy said...

Thank You Wade for your quick response. I managed to solve the problem. I first upload the Document, get the ID (counter) of the uploaded document (CAML against the filename) and then I update the metadata of the doc.

I also set the Key value of the BDC field and then use a class which updates all related fields. It iterates through the ListItems and makes an automatic refresh: http://old.sharepointnick.com/blog/Lists/Posts/Post.aspx?ID=59

Dimitris.

Anonymous said...

Does this also apply to the Lists Web Service? I tried adding a list item with a BDC Column and ran into issues. The Non-BDC fields get added just fine and the BDC field remains empty.

Here is the meat of the problem

BDC.BDCFieldsResolver resolver = new SharepointListTest.BDC.BDCFieldsResolver();

resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

BDC.ResolveResult result = resolver.Resolve("LOBSYSTEM", "Entity", "60343234388", "macaddress");

batchElement.InnerXml = "[Method ID='1' Cmd='New']" +
"[Field Name='Title']Hello World[/Field]" +
"[Field Name='bdc']" + result.Identifier.Value + "[/Field][/Method]";

I replace the <> with [] since HTML is not allowed to be posted

I see Hello World as the title and the bdc column remains blank.

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

Shane,

I have yet to figure out how to set a BDC field using the SharePoint web services... I'm not sure it is possible. It seems silly that you can resolve a BDC value, but can't set it...

My suggestion would be to create your own custom SharePoint web service, and use the method described here to set the BDC value (using the SharePoint Object Model).

Best of luck...

Anonymous said...

Thanks for the response Wade. Here is a forum post on MSDN where I am trying to get this worked out. I feel I am really close to getting this working. If the forums don't produce, I am calling Microsoft and getting it from the horses mouth.

http://social.msdn.microsoft.com/Forums/en-US/sharepointbdc/thread/4985ab17-5167-418e-a200-37f4b1c61ccd/


Shane

Chirag Patel said...

how do i had a reference to Microsoft.Office.Server.ApplicationRegistry.Infrastructure? I do added a reference to 'Microsoft.Office.Server' however it does not list ApplicationRegistry as an option when adding the 'using' line.

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

Hi Chirag,

I guess I fogot to mention that part.

You need to add a reference to the microsoft.sharepoint.portal.dll which can be found in the ISAPI folder of the SharePoint hive (ex: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI). After adding this reference to your project, you can import the Microsoft.Office.Server.ApplicationRegistry.Infrastructure namespace. Good luck!

Cody Nutt said...

Be sure to take Wade's comment about converting to the right format before adding to the object array to heart. It's the type of database column you want. You will still receive errors if you don't.

Philip said...

Hi Wade this article is a really good explanation. I'm just confused as to the use of "Value" and your mention of casting it to the correct type. Could you explain?

Philip said...

I should have said. I'm trying to read a BDC value from a column in a list, and then apply that value to an item in a document library, which is also a BDC column...

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

Hi Philip,

The EntityInstanceIdEncoder method will return a different value depending on the object type that is passed.

For example:

EntityInstanceIdEncoder.EncodeEntityInstanceId(new object[] {1}) will return a different value than EntityInstanceIdEncoder.EncodeEntityInstanceId(new object[] {"1"}).

This is why it is important to make sure you cast the value to the type specified in the BDC definition for that column, otherwise you may not get a correct identifier value.

Hope that clarifies things.

Brad King said...

Worked great was able to set the ID in he BDC column to link the new file with the record. But I am running into the problem now of the rest of the fields not refreshing until I refresh the BDC in document library.

Loomy posted a link on how to loop through and refresh the fields but I was unable to make sense of it.

Any ideas ?

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

Hi Brad,

As mentioned, you need to explicitly set the values of the other fields. For example, if you have a BDC column titled "Customer" with additional fields for "Name" and "Phone", you would need to set the "Name" and "Phone" values as follows:

spItem["Customer: Name"] = "Wade Hunter";
spItem["Customer: Phone"] = "555-5555";

And of course, don't forget to call Update() on the item that you're updating.

I realize that this is not ideal as you want to be able to have the additional fields populate automatically based on the selected BDC item. I wrote this article 2 years ago and never really kept it up to date, but I have since come up with a much more generic, fully functional apporach to set BDC values. I haven't used it in a while, but I will try to dig it up and make a post about it. Check back soon!

Brad King said...

Thanks Wade .. I will check back for sure. I found some routines to auto-refresh the whole library but that doesn't seem practical since a large library could take quite some time and add quite a bit of overheard.

Wade Hunter - B.Sc Computer Science, MCP, MCTS said...

I've created a new post as a follow up to this article. It contains C# code for a class I created to work with business data in SharePoint 2007. There is a public method in there called "SetFieldByID" which will set the value of a BDC column including all auxiliary fields.

http://wadehuntersblog.blogspot.com/2010/10/working-with-sharepoint-2007-business.html

أحمد بن عمر باجابر said...

Thanks
In fact,that was more helpful for me than the new post :-)

dotNetFollower said...

Hello!
Nice article, but, frankly speaking, I wonder why you parse bdcField.SchemaXml to get "RelatedFieldWssStaticName". I think it can be obtained like bdcField.GetProperty("RelatedFieldWssStaticName"). I described how to get main properties of BdcField in my article here - SharePoint: Understanding BusinessData Column (BDC Field)

Unknown said...

Hi,

I couldn't find the Microsoft.Office.Server.ApplicationRegistry.Infrastructure dll in the specified location..

Please help what to do?