First published on TECHNET on Mar 18, 2013
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.
After the code (shown below) executes, it creates User Profile Synchronization Connection in BCS (check CreateProfileConnectionMy() in the sample code).
This code sets association to the filter in BCS to Accountname user profile property (check CreateProfileConnectionMy and SetDSMLAttribute() method in the sample code).
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).
After running user profile synchronization, the end result should be to have values populated in MyOffice user profile property.
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.
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!
Welcome to the SharePoint Blog! Learn best practices, news, and trends directly from the SharePoint team.