Archive

Posts Tagged ‘Share Point’

SharePoint: User from trusted domain doesn’t see search result

April 23rd, 2012 No comments

    Our production SharePoint Farm and customer users are deployed in one domain, let’s call it Root, while the development and quality control farms are in a child domain, let’s name it Child.Root. The Child.Root domain trust to the parent Root domain, while the Root domain knows nothing about the Child.Root. So, we have a one way trust domains configuration where the Child.Root trusts Root, but not vice versa. Under such conditions we have faced the issue with SharePoint Search when an user from the trusted parent Root domain gets zero results, executing a search query on the Child.Root.

The solution was borrowed from the Microsoft Knowledge Base ArticleUnable to Perform a query on a One-Way trust Domains Scenario when an User from the trusted domain performs the query and the SSA Application Pool account is from the Trustee Domain. The topology described in the KB article presumes that domains with a one way trust relationship are in two separate forests. Despite the fact that in our case both domains are in one forest, the solution works great, though.

So, you need to follow these steps:

  1. Launch SharePoint 2010 Management Shell (click on Start, then All Programs -> Microsoft SharePoint 2010 Products -> SharePoint 2010 Management Shell);
  2. Type $searchapp = Get-SPEnterpriseSearchServiceApplication and press Enter;
  3. Type $searchapp.SetProperty(“ForceClaimACLs”,1) and press Enter. Don’t wait for any confirmation, you won’t see it;
    Set ForceClaimACLs property
  4. Restart a full crawl through Central Administration (click on Start, then All Programs -> Microsoft SharePoint 2010 Products -> SharePoint 2010 Central Administration, then go to Application Management -> Manage Service Applications -> Search Service Application, then in Crawling section click on Content Sources, open context menu for the Content Source you want to re-crawl, for example, Local SharePoint sites and click Start Full Crawl);
    Start Full Crawl

After the SetProperty() command has set value of the ForceClaimACLs parameter in the search administration database to 1, ACLs are stored as Claims instead of NT tokens. Note, however, that you needn’t switch other SharePoint applications (different from the Search Service Application) to Claims based authentication. Also, keep in mind that this is a one-way change, so you won’t be able to reverse it back to classic mode.

After the full crawl is performed, users see search results, regardless from which domain they are logged in.

SharePoint: Cannot open log for source. You may not have write access.

April 21st, 2012 No comments

    In our SharePoint applications we actively use writing into Application Event Log. After adding a new Windows 2008 Server R2 machine to our SP 2010 farm, we was getting the exception “System.ComponentModel.Win32Exception: Access is denied” with the description “Cannot open log for source {*}. You may not have write access.” Apparently, the given error is caused by writing to log when it’s called under an ordinary user with limited rights, who, however, can view web pages. I tried to provide Authenticated Users group with Full Control to the [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog] registry key, with no success though.

The workaround is add or modify the magic CustomSD value under the registry key [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application]. So,

  1. Open Registry Editor (click Start, then Run, then type regedit and click Ok);
  2. Locate the [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application] key in the registry tree;
  3. If the CustomSD value doesn’t exist, create it (right click on Application key, then click New -> String Value and set CustomSD name). Then set value data to O:BAG:SYD:(A;;0x3;;;AU) (right click on CustomSD, then click Modify, type the O:BAG:SYD:(A;;0x3;;;AU) and click Ok). The result should look as shown on the picture below:
    Create CustomSD Value
  4. If the CustomSD value already exists, append (A;;0x3;;;AU) to the value data (right click on CustomSD, then click Modify, type the (A;;0x3;;;AU) at the end of value data and click Ok). After appending, the resultant value data would be similar to:

    O:BAG:SYD:(D;;0xf0007;;;AN)(D;;0xf0007;;;BG)(A;;0x f0007;;;SY)(A;;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A;;0x3;;;S-1-5-3)(A;;0x3;;;AU)

The CustomSD registry value describes which accounts have the read/write/clear permissions to Application Event Log. The format of the value data corresponds to Security Descriptor Definition Language (SDDL), so (A;;0x3;;;AU) consists of

  • A – SDDL_ACCESS_ALLOWED or ACCESS_ALLOWED_ACE_TYPE, one of ACE types;
  • 0x3 – ELF_LOGFILE_WRITE (0x2) & ELF_LOGFILE_READ (0x1), the access rights to the EventLog;
  • AU – Authenticated Users group;

It looks funny that direct giving permissions for Authenticated Users group haven’t had effect, while the EventLog‘s security is controlled by the CustomSD registry value.

Update: If a web application supports anonymous access, you’d better replace AU in (A;;0x3;;;AU) with WD, where WD is Everyone or a group that includes all users. So, the final version in this case is (A;;0x3;;;WD).

Related posts:

SharePoint: Working with BDC Secondary Fields

April 17th, 2012 No comments

    As you probably know, in SharePoint 2010 Business Data Connectivity replaced Business Data Catalog of SharePoint 2007. Some changes affects how Business Data Columns are presented in a list’s schema. In SP 2007 a declaration of a Business Data Column in a schema.xml may look like the following:

<Field Type="BusinessData" DisplayName="Product"
Required="FALSE" ID="{bc203358-6113-470f-9b08-f6100cc034f2}"
StaticName="Product" BaseRenderingType="Text" Name="Product"
SystemInstance="ExternalProductDB_Instance" Entity="Products"
BdcField="Name" Profile="" HasActions="False"
RelatedField="Products_ID"
RelatedFieldBDCField="" RelatedFieldWssStaticName="Products_ID"

SecondaryFieldBdcNames="Price:Producer"
SecondaryFieldWssNames="Product_x003a__x0020_Price:Product_x003a__x0020_Producer"
SecondaryFieldsWssStaticNames="Product_x003a__x0020_Price:Product_x003a__x0020_Producer" />

In contrast, in SP 2010 it looks like

<Field Type="BusinessData" DisplayName="Product"
Required="FALSE" ID="{bc203358-6113-470f-9b08-f6100cc034f2}"
StaticName="Product" BaseRenderingType="Text" Name="Product"
SystemInstance="ExternalProductDB_Instance" Entity="Products"
BdcField="Name" Profile="" HasActions="False"
RelatedField="Products_ID"
RelatedFieldBDCField="" RelatedFieldWssStaticName="Products_ID"

SecondaryFieldBdcNames="6%209%20Price%20Producer%204"
SecondaryFieldWssNames="27%2030%20Product%5Fx003a%5F%5Fx0020%5FPrice%20Product%5Fx003a%5F%5Fx0020%5FProducer%206"
SecondaryFieldsWssStaticNames="27%2030%20Product%5Fx003a%5F%5Fx0020%5FPrice%20Product%5Fx003a%5F%5Fx0020%5FProducer%206" />

Undoubtedly, in SP 2010 the secondary fields became practically unreadable. Indeed, the format of secondary fields‘ presentation is revised. Moreover some kind of URL encoding are applied to them. Let’s examine how these secondary fields could look before the URL encoding is applied:

<Field
...
SecondaryFieldBdcNames="6 9 Price Producer 4"
SecondaryFieldWssNames="27 30 Product_x003a__x0020_Price Product_x003a__x0020_Producer 6"
SecondaryFieldsWssStaticNames="27 30 Product_x003a__x0020_Price Product_x003a__x0020_Producer 6" />

Now it’s pretty easy to figure out the new format. Take a look at the SecondaryFieldBdcNames attribute. It contains names of two secondary bdc fields: ‘Price’ and ‘Producer’. 6 is the length of the ‘Price’ name + 1 for a space character right after the name. 9 is the length of the ‘Procuder’ name + 1 for a space character after the name. 4 is the length of the sub-string ‘6 9 ‘ (including spaces), which contains the lengths of the fields’ names. See a picture below:

Format of Secondary Fields

Note that the SecondaryFieldBdcNames, SecondaryFieldWssNames and SecondaryFieldsWssStaticNames have the same format.

We have a lot of code interacting with Business Data Columns, thus we were interested in means allowing easily to decode, encode and parse Secondary Fields attributes. In the Microsoft.SharePoint.dll, there is the internal BdcClientUtil class containing the basic methods to work with Secondary Fields:

internal class BdcClientUtil
{
    ...
    string[] SplitStrings(string combinedEncoded);
    string   CombineStrings(string[] strings);
    ...
}

So, using .Net Reflector I’ve extracted these methods along with several others auxiliary ones and put them into the helper-class called SecondaryFieldNamesHelper. All internal methods and properties were honestly stolen from Microsoft.SharePoint.dll, the public ones were added by me and described below:

  • string Encode(string[] secondaryFieldNames) – accepts an array of field names and returns the string formatted and encoded according to the SharePoint 2010 requirements;
  • string[] Decode(string str) – accepts an encoded string, decodes it and returns a resultant array of field names;
  • bool IsEncodedString(string str) – checks whether a passed string is encoded;
  • string ConvertToSP2010(string str) – converts a SP 2007 colon-separated string of secondary fields into another one formatted and encoded according to the SharePoint 2010 requirements;

Below is the source code of the SecondaryFieldNamesHelper:

SecondaryFieldNamesHelper Sources

using System;
using System.Collections;
using System.Text;
using System.Globalization;
using System.IO;
using System.Web.UI;

namespace Helpers
{
    public static class SecondaryFieldNamesHelper
    {
        #region fields & properties
        private static string[] s_crgstrUrlHexValue = new string[] 
        { 
            "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", 
            "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", 
            "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27", "%28", "%29", "%2A", "%2B", "%2C", "%2D", "%2E", "%2F", 
            "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37", "%38", "%39", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F", 
            "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47", "%48", "%49", "%4A", "%4B", "%4C", "%4D", "%4E", "%4F", 
            "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57", "%58", "%59", "%5A", "%5B", "%5C", "%5D", "%5E", "%5F", 
            "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67", "%68", "%69", "%6A", "%6B", "%6C", "%6D", "%6E", "%6F", 
            "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77", "%78", "%79", "%7A", "%7B", "%7C", "%7D", "%7E", "%7F", 
            "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", 
            "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", 
            "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", 
            "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", 
            "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", 
            "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", 
            "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", 
            "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
        };
        #endregion

        #region public methods
        public static bool IsEncodedString(string str)
        {
            if (string.IsNullOrEmpty(str))
                return false;

            bool res = true;
            try
            {
                string[] splittedString = SplitStrings(str);
            }
            catch
            {
                res = false;
            }
            return res;
        }

        public static string Encode(string[] secondaryFieldNames)
        {
            return CombineStrings(secondaryFieldNames);
        }

        public static string[] Decode(string str)
        {
            if(string.IsNullOrEmpty(str))
                return new string[0];
            return SplitStrings(str);
        }

        public static string ConvertToSP2010(string str)
        {
            if (IsEncodedString(str))
                return str;

            string[] fieldNames = str.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
            string encodedVal = CombineStrings(fieldNames);
            return encodedVal;
        }
        #endregion

        #region internal methods
        private static string[] SplitStrings(string combinedEncoded)
        {
            string[] array = null;
            ArrayList list = new ArrayList();
            if ("0" == combinedEncoded)
                return new string[0];
            try
            {
                string str = UrlKeyValueDecode(combinedEncoded);
                string[] strArray2 = str.Split(new char[] { ' ' }, StringSplitOptions.None);
                int result = 0;
                if ((strArray2 == null) || !int.TryParse(strArray2[strArray2.Length - 1], NumberStyles.Integer, 

CultureInfo.InvariantCulture, out result))
                    throw new ArgumentException(string.Empty, "combinedEncoded");
                int num2 = str.LastIndexOf(' ');
                string str2 = str.Substring(result, num2 - result);
                int length = str2.Length;
                int index = 0;
                int startIndex = 0;
                while (startIndex < length)
                {
                    string s = strArray2[index];
                    int num6 = 1;
                    if ((s != null) && (s.Length == 0))
                        list.Add(null);
                    else
                    {
                        if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out num6))
                            throw new ArgumentException(string.Empty, "combinedEncoded");
                        list.Add(str2.Substring(startIndex, num6 - 1));
                    }
                    startIndex += num6;
                    index++;
                }
                array = new string[list.Count];
                list.CopyTo(array);
            }
            catch (Exception exception)
            {
                throw new ArgumentException(string.Empty, "combinedEncoded", exception);
            }
            return array;
        }

        private static string UrlKeyValueDecode(string keyOrValueToDecode)
        {
            if (string.IsNullOrEmpty(keyOrValueToDecode))
                return keyOrValueToDecode;
            return UrlDecodeHelper(keyOrValueToDecode, keyOrValueToDecode.Length, true);
        }

        private static string UrlDecodeHelper(string stringToDecode, int length, bool decodePlus)
        {
            if ((stringToDecode == null) || (stringToDecode.Length == 0))
                return stringToDecode;
            StringBuilder builder = new StringBuilder(length);
            byte[] bytes = null;
            int nIndex = 0;
            while (nIndex < length)
            {
                char ch = stringToDecode[nIndex];
                if (ch < ' ')
                    nIndex++;
                else
                {
                    if (decodePlus && (ch == '+'))
                    {
                        builder.Append(" ");
                        nIndex++;
                        continue;
                    }
                    if (IsHexEscapedChar(stringToDecode, nIndex, length))
                    {
                        if (bytes == null)
                            bytes = new byte[(length - nIndex) / 3];
                        int count = 0;
                        do
                        {
                            int num3 = (FromHexNoCheck(stringToDecode[nIndex + 1]) * 0x10) + FromHexNoCheck(stringToDecode[nIndex + 

2]);
                            bytes[count++] = (byte)num3;
                            nIndex += 3;
                        }
                        while (IsHexEscapedChar(stringToDecode, nIndex, length));
                        builder.Append(Encoding.UTF8.GetChars(bytes, 0, count));
                        continue;
                    }
                    builder.Append(ch);
                    nIndex++;
                }
            }
            if (length < stringToDecode.Length)
                builder.Append(stringToDecode.Substring(length));
            return builder.ToString();
        }

        private static bool IsHexEscapedChar(string str, int nIndex, int nPathLength)
        {
            if ((((nIndex + 2) >= nPathLength) || (str[nIndex] != '%')) || (!IsHexDigit(str[nIndex + 1]) || !IsHexDigit(str[nIndex + 

2])))
                return false;
            if (str[nIndex + 1] == '0')
                return (str[nIndex + 2] != '0');
            return true;
        }

        private static bool IsHexDigit(char digit)
        {
            if ((('0' > digit) || (digit > '9')) && (('a' > digit) || (digit > 'f')))
                return (('A' <= digit) && (digit <= 'F'));
            return true;
        }

        private static int FromHexNoCheck(char digit)
        {
            if (digit <= '9')
                return (digit - '0');
            if (digit <= 'F')
                return ((digit - 'A') + 10);
            return ((digit - 'a') + 10);
        }

        private static string CombineStrings(string[] strings)
        {
            StringBuilder builder = new StringBuilder();
            int index = 0;
            for (int i = 0; i < strings.Length; i++)
            {
                string str = strings[i];
                string str2 = ((str != null) ? ((str.Length + 1)).ToString(CultureInfo.InvariantCulture) : string.Empty) + ' ';
                builder.Insert(index, str2);
                index += str2.Length;
                builder.Append(str + ' ');
            }
            builder.Append(index.ToString(CultureInfo.InvariantCulture));
            return UrlKeyValueEncode(builder.ToString());
        }

        private static string UrlKeyValueEncode(string keyOrValueToEncode)
        {
            if ((keyOrValueToEncode == null) || (keyOrValueToEncode.Length == 0))
                return keyOrValueToEncode;
            StringBuilder sb = new StringBuilder(0xff);
            HtmlTextWriter output = new HtmlTextWriter(new StringWriter(sb, CultureInfo.InvariantCulture));
            UrlKeyValueEncode(keyOrValueToEncode, output);
            return sb.ToString();
        }

        private static void UrlKeyValueEncode(string keyOrValueToEncode, TextWriter output)
        {
            if (((keyOrValueToEncode != null) && (keyOrValueToEncode.Length != 0)) && (output != null))
            {
                bool fUsedNextChar = false;
                int startIndex = 0;
                int length = 0;
                int num3 = keyOrValueToEncode.Length;
                for (int i = 0; i < num3; i++)
                {
                    char ch = keyOrValueToEncode[i];
                    if (((('0' <= ch) && (ch <= '9')) || (('a' <= ch) && (ch <= 'z'))) || (('A' <= ch) && (ch <= 'Z')))
                        length++;
                    else
                    {
                        if (length > 0)
                        {
                            output.Write(keyOrValueToEncode.Substring(startIndex, length));
                            length = 0;
                        }
                        UrlEncodeUnicodeChar(output, keyOrValueToEncode[i], (i < (num3 - 1)) ? keyOrValueToEncode[i + 1] : '\0', out 

fUsedNextChar);
                        if (fUsedNextChar)
                            i++;
                        startIndex = i + 1;
                    }
                }
                if ((startIndex < num3) && (output != null))
                    output.Write(keyOrValueToEncode.Substring(startIndex));
            }
        }

        private static void UrlEncodeUnicodeChar(TextWriter output, char ch, char chNext, out bool fUsedNextChar)
        {
            bool fInvalidUnicode = false;
            UrlEncodeUnicodeChar(output, ch, chNext, ref fInvalidUnicode, out fUsedNextChar);
        }

        private static void UrlEncodeUnicodeChar(TextWriter output, char ch, char chNext, ref bool fInvalidUnicode, out bool 

fUsedNextChar)
        {
            int num = 0xc0;
            int num2 = 0xe0;
            int num3 = 240;
            int num4 = 0x80;
            int num5 = 0xd800;
            int num6 = 0xfc00;
            int num7 = 0x10000;
            fUsedNextChar = false;
            int index = ch;
            if (index <= 0x7f)
                output.Write(s_crgstrUrlHexValue[index]);
            else
            {
                int num8;
                if (index <= 0x7ff)
                {
                    num8 = num | (index >> 6);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | (index & 0x3f);
                    output.Write(s_crgstrUrlHexValue[num8]);
                }
                else if ((index & num6) != num5)
                {
                    num8 = num2 | (index >> 12);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | ((index & 0xfc0) >> 6);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | (index & 0x3f);
                    output.Write(s_crgstrUrlHexValue[num8]);
                }
                else if (chNext != '\0')
                {
                    index = (index & 0x3ff) << 10;
                    fUsedNextChar = true;
                    index |= chNext & 'Ͽ';
                    index += num7;
                    num8 = num3 | (index >> 0x12);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | ((index & 0x3f000) >> 12);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | ((index & 0xfc0) >> 6);
                    output.Write(s_crgstrUrlHexValue[num8]);
                    num8 = num4 | (index & 0x3f);
                    output.Write(s_crgstrUrlHexValue[num8]);
                }
                else
                    fInvalidUnicode = true;
            }
        }
        #endregion
    }
}

The SecondaryFieldNamesHelper can be used as shown below:

SPBusinessDataField bdcField = ...

string secondaryFieldWssNamesProperty = bdcField.GetProperty("SecondaryFieldWssNames");
string[] secondaryWssFieldNames = SecondaryFieldNamesHelper.Decode(property);

string secondaryFieldBdcNamesProperty = bdcField.GetProperty("SecondaryFieldBdcNames");
string[] secondaryFieldBdcNames = SecondaryFieldNamesHelper.Decode(secondaryFieldBdcNamesProperty);

string sp2010WssStaticNames = 
   SecondaryFieldNamesHelper.ConvertToSP2010("Product_x003a__x0020_Price:Product_x003a__x0020_Producer");

As a .cs file the SecondaryFieldNamesHelper class is available here.

SharePoint: Code blocks are not allowed in this file

March 23rd, 2012 No comments

    The SharePoint is based on ASP.Net, so all possible ASP.Net errors may easily become apparent in a SharePoint application. The ‘Code blocks are not allowed in this file‘ issue isn’t an exception. To get rid of it we need to enable server side scripts by modifying the web.config file. Specifically we need to locate <PageParserPaths> within web.config and add a proper <PageParserPath> node to it. The following example demonstrates how to allow server side scripts for all pages, which contain the apps virtual folder in their relative paths:

<PageParserPaths>
    <PageParserPath VirtualPath="/apps/*" CompilationMode="Always" 
              AllowServerSideScript="true" IncludeSubFolders="true" />
</PageParserPaths>

After the modification, server side scripts will work for such pages as e.g.

http://myServer/apps/default.aspx
http://myServer/apps/appsubfolder1/MyPage.aspx (*)
http://myServer/apps/appsubfolder2/MyPage.aspx (*)
http://myServer/apps/appsubfolder2/subfolder3/MyPage.aspx (*)
and so on. 

Note that pages urls marked with asterisks (*) are eligible only if IncludeSubFolders is set to true.

To enable server side scripts for certain page, use something like this:

<PageParserPath VirtualPath="/apps/appsubfolder2/subfolder3/MyPage.aspx" 
              CompilationMode="Always" AllowServerSideScript="true" />

SharePoint: Manually Upgrade Business Data Catalog Application Definitions to Business Data Connectivity Models

March 13th, 2012 No comments

    Trying to import a legacy Application Definition File of SharePoint 2007 into Business Data Connectivity Service of SharePoint 2010, you apparently got at least one of the errors shown below:

  • Application definition import failed. The following error occurred: The root element of a valid Metadata package must be ‘Model’ in namespace ‘http://schemas.microsoft.com/windows/2007/BusinessDataCatalog’. The root in the given package is ‘LobSystem’ in namespace ‘http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog’. Error was encountered at or just before Line: ‘2’ and Position: ‘2’;
  • Application definition import failed. The following error occurred: BDC Model does not correctly match the schema. The required attribute ‘Namespace’ is missing. Error was encountered at or just before Line: ’20’ and Position: ’10’;
  • Application definition import failed. The following error occurred: ReturnTypeDescriptor of MethodInstance with Name ‘ProductSpecificFinderInstance’ on Entity (External Content Type) with Name ‘Product’ in Namespace ‘ExternalProductDB’ should not be a Collection TypeDescriptor for MethodInstances of Type ‘SpecificFinder’. Parameter name: rawValues.ReturnTypeDescriptorId Error was encountered at or just before Line: ‘171’ and Position: ’18’;
  • and so on

As it’s known, in SharePoint 2010, Business Data Catalog (BDC) was replaced with Business Data Connectivity with the same abbreviation. One of the changed things is the format of xml-based Application Definition Files. If you make an in-place upgrade of a live SharePoint 2007 application to SharePoint 2010, bdc metadata will be automatically upgraded as well and will become usable with the Business Data Connectivity. But if the in-place upgrade isn’t an option for you, you can upgrade your xml-based Application Definition Files manually. The manual algorithm step by step is described here – How to: Manually Upgrade Business Data Catalog Application Definitions to Business Data Connectivity Models.

For one of our applications we settled on the manual upgrade of its metadata files. But If I call myself a programmer, I have to try to automate the algorithm, especially taking into account 20+ files required to upgrade. So, I’ve developed a simple application for alteration of the legacy xml-based Application Definition Files to make them compatible with SharePoint 2010. However I’d like to notice that the given converter doesn’t follow entirely the procedure described by Microsoft, but performs only steps allowing our particular metadata files to be successfully imported into the Business Data Connectivity Service. For example, our files don’t comprise actions and associations, thus the application does nothing at all with <Action> and <Association> elements. So, consider this converter as a start point of developing the new one satisfying your own conditions and requirements.

Below I enumerated the necessary and sufficient changes to be applied to our particular metadata files so that it enables us to make them compatible with SharePoint 2010. Exactly these very steps and a few less important I’ve implemented in the converter.

  • the root element in the Application Definition File must be a <Model>;
  • the <Model> must contain <LobSystems> element, which in turn must wrap the former root node – <LobSystem>;
  • the <LobSystem> element mustn’t contain the Version-attribute;
  • the <Entity> element must contain the new attributes – Namespace and Version;
  • the <Identifier> element mustn’t contain an assembly name in its TypeName-attribute; For example, TypeName=”System.String, mscorlib” has to turn into TypeName=”System.String”;
  • the <MethodInstance> element with Type-attribute value of SpecificFinder should include the Default-attribute with value of true;
  • if the <TypeDescriptor> element has the IsCollection attribute set to true, the MethodInstance return TypeDescriptor should be updated to be an element of the collection. In practice, that means the ReturnTypeDescriptorPath-attribute with an appropriate value should be added to <MethodInstance> element, and the obsolete ReturnTypeDescriptorLevel and ReturnTypeDescriptorName attributes should be deleted;

Note that when modifying a <Entity> element, the values of the Namespace and Version attributes are copied respectively from the values of Name and Version attributes of the <LobSystem> element, which wraps the <Entity> element. The same approach is used while the in-place upgrade takes place.

After the changes are applied, and if they are sufficient for your metadata, the result files can be imported into Business Data Connectivity Service. During the import process, you may get the warnings. Consider fixing them in the future, but at the present stage you can simply ignore them. The most popular warnings are listed below:

  • This Model contains LobSystem (External System) of Type ‘WebService’ which is deprecated and may be removed in future releases.
  • The MethodInstance of type ‘Finder’ with Name ‘FindProducts’ does not have a Limit Filter.
  • The TypeDescriptor ‘From’ on Parameter ‘GetProductsFiltered’ on Method ‘GetItems’ of Entity (External Content Type) ‘Product’ with Namespace ‘ExternalProductDB’ has a DateTime value, but there is no Interpretation describing what the External System expects and returns as the DateTimeKind. The DateTime value is assumed to be UTC.
  • Note: I made the converter-application fix the warnings regarding the DateTime type and UTC, so they won’t bother you.

    The converter is very straightforward to use. Using the button ‘+’, simply add to the left section the files to be upgraded. Then click the button ‘>>’, and you’ll get the upgraded ones in the right section. Double click on file name opens an overview form to browse the input or result xml. Physically, the result files are located in c:\output folder. The application doesn’t use any SharePoint-related libraries.

    Upgrade 2007 BDC model to SP2010

    You can download the application from this page or by using the direct link. The Visual Studio 2010 solution and appropriate executable file are in the archive.