Skip to main content

Blog

Go Search
Home
Blog
Wiki
  

Other Blogs
There are no items in this list.
SharePointNick.com > Blog > Posts > Business Data Columns : updating programmatically
Business Data Columns : updating programmatically

When you add a Business Data Column and additional data fields to a list or library, setting the values of this column actually stores the data in the list, rather than a pointer to the BDC data. This means the BDC data is not refreshed automatically when a users views a document or the entire library, but actually relies on them clicking the little refresh button in the list. Of course getting the BDC data to update everytime the list/library is displayed may not be ideal as it could create a huge burden on your LOB System, but a setting would be nice so users could choose which way it would work.

Anyway, this blog post originates due to the fact that I've seen this problem enquired about, and also a number of people have asked how do you programmatically set BDC additional data fields in code. SharePoint must be able to do this as it does this exact functionality when you click on the little refresh button to refresh all your fields, hopefully this blog post might give you a little insight into how to use reflector and other tools incase you need to investigate how SharePoint does stuff, so you can use it or change it for your own solution.

Out of the box to refresh your Business Data Columns you need to click the little refresh button in the document library

image

When you click the refresh button you are taken to the page _layouts/BusinessDataSynchronizer.aspx where you are asked this operation may take some time, are you sure you want to continue. As we hopefully all know all the pages that are displayed from the _layouts directory come from the 12\TEMPLATE\LAYOUTS directory on the file system. We can find our BusinessDataSynchronizer.aspx page in the LAYOUTS directory and open it up in notepad. Right at the top of the code you'll find the controls and assemblies registered that are going to be used in the page. The one that should stick out at us is:

Microsoft.SharePoint.Portal.WebControls.BusinessDataSynchronizerPage

Now we know the namespace of the code we are looking for we can open Reflector (an application that should be in every SharePoint developers toolbox) and open the Microsoft.SharePoint.Portal dll and drill down to the namespace we require. The event that actually fires the synchronization is the btnSave click event so if we select that we'll see the code disassembled that SharePoint uses.

image

So the BusinessDataSynchronizerJob looks like the class we'd want to create and then run it's start method. Clicking on the BusinessDataSynchronizer link will take us to the code that is in that class...

But what do we see? They have marked the BusinessDataSynchronizer class as Internal? Why? Why leave it un-obfuscated but mark it as Internal? Grrrrrr (Adam I feel your pain! :-))

Well even though it is marked as Internal at least we can see what it does. And the answer is nothing that clever. It gets the data from the LOB System and then sets new values, iterating through the BDC Data Columns aditional fields also setting the values when it matches. Reflector does a good job of decompiliing the code in the BusinessDataSyncronizer class, but it does come up with a lot of GOTO and LABEL statements. Hopefully the BDC team didn't really include code like that and this is just Reflectors interpretation of 'foreach' and 'if' statements. But from this code we can see what they are doing, and we can piece it all together to perform the same work. The original question I had was how do you programatically set additional BDC Data Column fields, which this code will do, but I'm sure you'll be able to take it and fit it to whatever scenario you want to use it for. The code here will actually refresh every list item and the respective BDC data column although I'm sure you can see how to do it for one individual item if you needed. Also note I have hard coded the name of the SSP...

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using Microsoft.SharePoint; using Microsoft.Office.Server.ApplicationRegistry.Infrastructure; using Microsoft.SharePoint.Portal.WebControls; using Microsoft.Office.Server.ApplicationRegistry.MetadataModel;
namespace SetBDCData { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
 private void Form1_Load(object sender, EventArgs e) { SPSite siteCollection = new SPSite("http://localhost");
 SPWeb site = siteCollection.AllWebs["bdc"];
 SPList list = site.Lists["Tasks"];
 RefreshBDCFields refreshF = new RefreshBDCFields(); refreshF.List = list; refreshF.ColumnName = "Product";
 refreshF.DoWork(); 
 } }
 public class RefreshBDCFields { public SPList List = null; public string ColumnName = "";
 LobSystemInstance sysinst = null; Entity entity = null; Microsoft.Office.Server.ApplicationRegistry.MetadataModel.View specificFinderView = null; SPListItemCollection items = null;
 public RefreshBDCFields(SPList list, string columnName) { this.List = list; this.ColumnName = columnName; }
 public RefreshBDCFields() { }
 public void DoWork() { SPField fieldByInternalName = List.Fields.GetFieldByInternalName(this.ColumnName); if (!(fieldByInternalName is BusinessDataField)) { throw new BusinessDataListConfigurationException("The field " + this.ColumnName + " is not a business data field"); } BusinessDataField bizDataField = (BusinessDataField)fieldByInternalName;
 string[] secondaryFieldsNames = bizDataField.GetSecondaryFieldsNames(); string[] secondaryWssFieldNames = new string[0]; string property = bizDataField.GetProperty("SecondaryFieldWssNames");
 secondaryWssFieldNames = property.Split(new char[] { ':' });
 SqlSessionProvider.Instance().SetSharedResourceProviderToUse("SharedServices1");
 sysinst = ApplicationRegistry.GetLobSystemInstanceByName(bizDataField.SystemInstanceName); entity = sysinst.GetEntities()[bizDataField.EntityName]; specificFinderView = entity.GetSpecificFinderView(); items = List.Items;
 foreach (SPListItem item in items) { UpdateListItem(item, bizDataField, sysinst, entity, specificFinderView, secondaryFieldsNames, secondaryWssFieldNames); item.Update(); }
 }
 private void UpdateListItem(SPListItem item, BusinessDataField bizDataField, LobSystemInstance sysinst, Entity entity, Microsoft.Office.Server.ApplicationRegistry.MetadataModel.View view, string[] secondaryBdcFieldNames, string[] secondaryWssFieldNames) { string bdcFieldName = bizDataField.BdcFieldName; string encodedId = null; 
 object[] objArray; IList<object> identifierValues = null; object[] objArray2 = null; 
 List<Field>.Enumerator enumerator; encodedId = (string)item[bizDataField.RelatedField]; if (encodedId != null) { objArray = EntityInstanceIdEncoder.DecodeEntityInstanceId(encodedId); identifierValues = entity.FindSpecific(objArray, sysinst).GetIdentifierValues(); objArray2 = new object[identifierValues.Count]; }
 for (int i = 0; i < identifierValues.Count; i++) { objArray2[i] = identifierValues[i]; } 
 item[bizDataField.RelatedField] = EntityInstanceIdEncoder.EncodeEntityInstanceId(objArray2); enumerator = view.Fields.GetEnumerator();
 Microsoft.Office.Server.ApplicationRegistry.Runtime.IEntityInstance instance = entity.FindSpecific(objArray2, sysinst);
 Field field; string name;
 while (enumerator.MoveNext()) { field = enumerator.Current; name = field.Name; if (name == bdcFieldName) { item[bizDataField.InternalName] = Convert.ToString(instance.GetFormatted(field)); }
 for (int i = 0; i < secondaryBdcFieldNames.Length; i++) { if (secondaryBdcFieldNames[i] == field.Name) { item[secondaryWssFieldNames[i]] = Convert.ToString(instance.GetFormatted(field)); } }
 item.Update(); 
 }
 } }
}

Sure the code could do with a few comments and a bit more error checking but hopefully you get the jist.

So what could you do with this code? Well if you wanted to update Business Data Columns nightly or hourly why not wrap it up in site a Timer job? I'm still thinking of how it can be used to get LOB data every time a document library is viewed. Unfortunately there is nothing simple such as a View event, and of course updating the doc lib every time it was viewed may be hitting your LOB system quite a bit.

We're working on an administration Feature so you can setup a timer job to update these BDC fields at set times. What this space for more info on that soon.

Comments

John Deary

It looks like the page only updates BDC columns that are attached to documents in a document library. Folders are excluded from the syncronization. My documents library happens to have BDC columns on the folders.

System Account at 10/17/2007 9:54 PM

John Deary

It turns out that this is easily remedied with the following code block. It can be added right after the original for each block:

            items = List.Folders;
            foreach (SPListItem item in items)
            {
                    UpdateListItem(item, bizDataField, sysinst, entity, specificFinderView, secondaryFieldsNames, secondaryWssFieldNames);
                    item.Update();
            }
System Account at 10/18/2007 4:31 PM

Patrick

Thank you for the excellent article. We would be interested in any work you are doing on an administration feature to update the BDC fields at set times. Let me know how it is going.
System Account at 1/16/2008 8:45 PM

Yosry

Hello,

Thanks for sharing this piece of code. I encountered one issue: when I ran the code using a Windows Forms application, it worked great; however, when I ran the code in an event handler, an exception was thrown saying "Access denied by BDC". The exception is thrown on this line: identifierValues = entity.FindSpecific(objArray, sysinst).GetIdentifierValues();

Seems to me like a permissions issue, but how can I go about fixing it?

Thanks.
System Account at 3/11/2008 11:39 AM

Eric

This has saved my life, thank you so much!  I added some error handling to this and used it to fix a problem where I couldn't even refresh the list using the OOTB functionality.  I'll be blogging about this and linking back to this post soon.  Thanks!
System Account at 8/12/2008 11:56 PM

Eric

I blogged here about a solution I created based on your post. 

http://www.esotericdelights.com/post.aspx?id=1290e814-f058-4e4e-955e-c888d213bc28

Thanks again!
System Account at 8/13/2008 4:04 AM

Add Comment

Title


Body *


CommentUrl


Attachments
Subscribe in a reader