Tuesday, January 25, 2011

BusinessDataField API: How to add a secondary field to an existing BDC field

   I wondering if people usually change the field structure on the existing list through the schema change. If you are the one who does it - welcome to the club! (But actually it's not supported by Microsoft. To read further about the  upgradability the existing schema refer to Sharepoint : how to survive after the first deployment)
   But as you know the "Here is ideal and here is real" rule shows up in the every change in the sharepoint site.
 Recently I had a task to add one more Secondary Field to BDC field (BusinessDataField ) to the existing list instance list. One may say - it' easy - just do it through UI. But in my case I have a lots of business logic incorporated code behind, and I  need to have control over the related WSS field to use in my code.
   To have control over the field, I prefer not to rely on the display name of the field, but on a static name which is unchangeable.
   My task is:
     1.add the new Secondary Field to existing BDC field on the list;
     2. make possible to manipulate with new secondary field through the code.
  Solutions:
     1. First approach  -  to change <Field Type="BusinessData" > the schema failed. The existing list didn't see the changes in the attributes like SecondaryFieldBdcNames,SecondaryFieldWssNames, SecondaryFieldsWssStaticNames.
         The second approach  did the job and created the secondary field, but here is a tricky part - the only method I can use doesn't give me power to control the static name of the field that it creates. But I will discuss in the part 2 of the solution how I can make do with an unpredictable static name of the secondary field of the business data field.
         Let me first  show  how you can set an additional secondary field to the existing business field on the list:

      SqlSessionProvider.Instance().SetSharedResourceProviderToUse(
                                                   CommonConstants.SharedServices(oWebsite.Site));
                                             
                                               string[] flds = field.GetSecondaryFieldsNames();

                                               List<string> lstFlds= new List<string>(flds);
                                               lstFlds.Add("New_Secondary_Field_Name");

                                               string[] newFlds = lstFlds.ToArray();
//I have noticed that people tend to reapply the existing properties of BussinesDataField to make sure there will not be lost after BussinesField.Update
                                               field.SystemInstanceName = "InstanceName";

                                               field.SetSecondaryFieldsNames(newFlds);

                                               field.EntityName = field.EntityName;
                                               field.BdcFieldName = field.BdcFieldName;
                                               field.HasActions = field.HasActions;
                                               field.Update();
 I want to emphasize that method BusinessDataField.SetSecondaryFieldsNames is not intended to be used directly from your code as Microsoft says.

2. Now I have a new secondary field  in the list.  I need to use it in my code. My last task - is to identify this added secondary field without knowing its static name. I wrote the method which searches through schema fields with an attribute "BdcField" and than trying to find the field with the passed SecondaryBDCValue (BDCFieldValue).

   public static string getWssFieldInternalNameByBDCFied (SPList list,BusinessDataField BDCField,  string BDCFieldValue )
        {
           
            string[] secFldNames;
            XmlDocument schemaBdc = new XmlDocument();
            schemaBdc.LoadXml(BDCField. SchemaXml);
            XmlNode bdc = schemaBdc.DocumentElement;

            if(bdc==null)
            {
                return null;
            }
            secFldNames = bdc.Attributes["SecondaryFieldsWssStaticNames"].Value.Split(':');
            SPFieldCollection flds = list.Fields;

            List<SPField> fldsWithBDCFieldValue = new List<SPField>();
            foreach (SPField f in flds)
            {  
                 string BdcProperty ="";
                try
                {
                   XmlDocument schema =new XmlDocument();
                   schema.LoadXml(f.SchemaXml);
                   XmlNode fld = schema.DocumentElement;

                    if(fld!=null && fld.Attributes["BdcField"]!=null)
                    {
                        BdcProperty = fld.Attributes["BdcField"].Value;
                    }
                }
                catch(Exception ex)
                {
                }
               
                if(BdcProperty.Equals(BDCFieldValue))
                {
                    fldsWithBDCFieldValue.Add(f);
                }

               
            }

            var wssFldInternalNames= from schemaFld in fldsWithBDCFieldValue
                    join secFld in secFldNames on schemaFld.StaticName equals secFld
                    select schemaFld.InternalName;

            string wssFldInternalName = wssFldInternalNames.FirstOrDefault();
          
               
            return wssFldInternalName;
}