This blog post is a contribution from Mustaq Patel, an engineer with the SharePoint Developer Support team.

The end goal is to populate User Profile Property values from an external database.  From SharePoint 2010 UI, this requires the following three steps:

1. Create BCS Model.  Have Finder and SpecificFinder methods defined in it.  Make sure the mode has unique identifier, which should match with the user profile property (for e.g., AccountName).  A model created using SPD should work as well.

2. Create User Profile Synchronization Connection (Manage User Profile Service Application > Configure Synchronization Connections).

3. Associate out of the box user profile property (or custom property) to the BCS field (Manage User Profile Service Application > Manage User Properties > Edit Existing property or create a new custom property).

The code sample shown below achieves the points 2 & 3 specified above.  It assumes that the BCS model is published and fetches data successfully (test by having an external list).

Scenario

My external database has fields AAccountName (which is a primary key and will act as association to AccountName in user profile property) and AOffice (which I would like to map to a custom User Profile Property called MyOffice).

I also had to add a Filter which is associated to the AAccountName field.  Here’s the screenshot of my external list.

image

After the code (shown below) executes, it creates User Profile Synchronization Connection in BCS (check CreateProfileConnectionMy() in the sample code).

image

This code sets association to the filter in BCS to Accountname user profile property (check CreateProfileConnectionMy and SetDSMLAttribute() method in the sample code).

image

User Profile Property mapping is done in method SetPropertyMapping.  Now, you should see BCS fields available to associate with User Profile Property.  I associated MyOffice to BCS field AOffice (check SetPropertyMapping() method in the sample code).

image

After running user profile synchronization, the end result should be to have values populated in MyOffice user profile property.

image

Few notes on the code:

- Setting List<DSMLAttribute> dsmlAttributesList and passing it to userProfileConfigManager.ConnectionManager.AddBusinessDataCatalogConnection is important and will populate BCS fields in User Profile properties.  If you pass in empty DSMLAttribute list, the connection will be created but you will not see BCS fields in the drop-down when you go in to the user profile properties of a user profile and edit a particular property.  Further, it seems that you also need the code in the method SetDSMLAttribute().  If that’s not present, the AddBusinessDataCatalogConnection gives an exception (FIM put error exception).  The connection will get created, but if you do user profile sync (full or incremental), you will see that the property value is not getting populated from external database.  This is what the problem I had a chance to work on was about.

DSMLAttribute dsmlAttributeDN = new DSMLAttribute();
            dsmlAttributeDN.Indexible = true; 
            dsmlAttributeDN.Name = PropertyConstants.DistinguishedName;
            dsmlAttributeDN.ID = PropertyConstants.DistinguishedName;
            dsmlAttributeDN.SingleValued = true;
            dsmlAttributeDN.Syntax = DSMLSyntax.String;
            dsmlAttributesList.Add(dsmlAttributeDN);

- SetPropertyMapping() method, sets property mapping with BCS fields and will need to be called after the connection is created (after AddBusinessDataCatalogConnection).  It also shows how to connect to BCS Service, get specific method and iterate through the TypeDescriptor and fields.

Here’s the entire code.  It’s implemented as a farm level feature event receiver (OnFeatureActivated) code.

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.SharePoint.BusinessData.SharedService;
using Microsoft.SharePoint.BusinessData.Administration;
using Microsoft.SharePoint.BusinessData.Parser;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.BusinessData.MetadataModel.Collections;
using Microsoft.BusinessData.Runtime;
using System.Collections.Generic;
using Microsoft.Office.Server.UserProfiles.Synchronization;
using System.IO;
using System.Xml;
 
 
namespace CreateUserProfileSyncConnectionEV.Features.UserProfileSyncFeature
{    
 
    [Guid("1a568d3d-75c2-49b5-aa46-cea75dc669de")]
    public class UserProfileSyncFeatureEventReceiver : SPFeatureReceiver
    {
        List<DSMLAttribute> dsmlAttributesList = new List<DSMLAttribute>();
        
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            CreateProfileConnectionMy();
        }
 
       
       private void CreateProfileConnectionMy()
        {
            SPSite centralAdminSite = SPAdministrationWebApplication.Local.Sites[0];
            SPServiceContext context = SPServiceContext.GetContext(centralAdminSite);
            UserProfileManager userProfileManager = new UserProfileManager(context);
            UserProfileConfigManager userProfileConfigManager = new UserProfileConfigManager(context);
 
            string connectionName = "MyBCSConnection";
 
            // The LobSystemInstance name
            string systemName = "MyUsers";
 
            // The Entity name
            string entityName = "MyUsers";
 
            //The FilterName 
            string filterName = "AAccountNameFilter";
 
            // Namespace of the project
            string entityNamespace = "http://demo2010a";
 
            // The unique SharePoint property to map the BDC to
            string profilePropertyName = "AccountName";
 
            // The unique BDC property
            string mappedAttribute = "AAccountName";
 
            try
            {
                SetDSMLAttribute();
                userProfileConfigManager.ConnectionManager.AddBusinessDataCatalogConnection(connectionName, systemName, entityName, entityNamespace, profilePropertyName, filterName, mappedAttribute, dsmlAttributesList);
                SetPropertyMapping();
                
            }    
            catch (Exception ex)
            {
 
            }
            finally
            {
                
            }
        }        
 
 
        private void SetDSMLAttribute()
        {
            SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);
            BdcService bdcService = SPFarm.Local.Services.GetValue<BdcService>();
 
            // Get MetaData Catalog  
            IMetadataCatalog catalog = bdcService.GetDatabaseBackedMetadataCatalog(context);
 
            // Get entity with appropriate namespace and name  
            IEntity entity = catalog.GetEntity("http://demo2010a", "MyUsers");
            IView entityViewFinder = null;
 
            INamedMethodInstanceDictionary methodInstances = entity.GetMethodInstances(MethodInstanceType.Finder);
            foreach (IMethodInstance methodInstance in methodInstances.Values)
            {
                if (methodInstance.IsDefault)
                {
                    entityViewFinder = entity.GetFinderView(methodInstance.Name);
                    break;
                }
            }
 
              //Part of below code is taken from Sharepoint Source
 
     List<string> CurrentEntityAttributeNames = new List<string>();
            if (entityViewFinder != null)
            {
                foreach (IField field in entityViewFinder.Fields)
                {
                    ITypeDescriptor typeDescriptor = field.TypeDescriptor;
                    DSMLAttribute dsmlAttribute = new DSMLAttribute();
                    dsmlAttribute.Indexible = false; 
                    dsmlAttribute.Name = field.Name;
                    dsmlAttribute.ID = field.Name;
                    dsmlAttribute.SingleValued = true;
                    dsmlAttribute.Syntax = GetDSMLSyntax(typeDescriptor.TypeName); 
                    dsmlAttributesList.Add(dsmlAttribute);
                    CurrentEntityAttributeNames.Add(field.Name);                                       
                    
                }
            }
 
            IView entityViewSpecificFinder = null;
 
            INamedMethodInstanceDictionary methodInstanceSpecificFinder = entity.GetMethodInstances(MethodInstanceType.SpecificFinder);
            foreach (IMethodInstance methodInstance in methodInstanceSpecificFinder.Values)
            {
                if (methodInstance.IsDefault)
                {
                    entityViewSpecificFinder = entity.GetSpecificFinderView(methodInstance.Name);
                    break;
                }
            }
 
            if (entityViewSpecificFinder != null)
            {
                foreach (IField field in entityViewSpecificFinder.Fields)
                {
                    if (!CurrentEntityAttributeNames.Contains(field.Name))
                    {
                        ITypeDescriptor typeDescriptor = field.TypeDescriptor;
                        DSMLAttribute dsmlAttribute = new DSMLAttribute();
                        dsmlAttribute.Indexible = false; 
                        dsmlAttribute.Name = field.Name;
                        dsmlAttribute.ID = field.Name;
                        dsmlAttribute.SingleValued = true;
                        dsmlAttribute.Syntax = GetDSMLSyntax(typeDescriptor.TypeName); 
                        dsmlAttributesList.Add(dsmlAttribute);
                        CurrentEntityAttributeNames.Add(field.Name);
}
                }
            }
 
            DSMLAttribute dsmlAttributeDN = new DSMLAttribute();
            dsmlAttributeDN.Indexible = true; 
            dsmlAttributeDN.Name = PropertyConstants.DistinguishedName;
            dsmlAttributeDN.ID = PropertyConstants.DistinguishedName;
            dsmlAttributeDN.SingleValued = true;
            dsmlAttributeDN.Syntax = DSMLSyntax.String;
            dsmlAttributesList.Add(dsmlAttributeDN);
              
        }
 
        private DSMLSyntax GetDSMLSyntax(string typeName)
        {
            DSMLSyntax syntax;
            if(typeName.Equals("System.DateTime"))
            {
                syntax = DSMLSyntax.Date;
            }
            else if(typeName.Equals("System.Int32"))
            {
                syntax = DSMLSyntax.Integer;
            }
            else if(typeName.Equals("System.Byte"))
            {
                syntax = DSMLSyntax.Binary;
            }
            else if(typeName.Equals("System.Boolean"))
            {
                syntax = DSMLSyntax.Boolean;
            }
            else
            {
                syntax = DSMLSyntax.String;
            }
            return syntax;
        }
 
        private void SetPropertyMapping()
        {
            try
            {
                SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);
                UserProfileConfigManager userProfileConfigManager = new UserProfileConfigManager(context);                
                UserProfileManager umgr = new UserProfileManager(context);
                ProfileSubtypePropertyManager submgr = umgr.DefaultProfileSubtypeProperties;
                ProfileSubtypeProperty subprop = submgr.GetPropertyByName("MyOffice");
 
                Connection con = userProfileConfigManager.ConnectionManager["MyBCSConnection"];
                
                if (con != null)
                {
                    PropertyMapCollection pmc = con.PropertyMapping;                    
                    PropertyMap pm = pmc[subprop.Name];
                    if (pm == null)
                    {
                        pmc.AddNewMapping(ProfileType.User, subprop.Name, "AOffice");
                    }                    
                }                
            }
            catch (Exception e)
            {
 
            }
 
        }
 
        
    }
}

Hope this post was helpful!