Thought I’d already covered joins sufficiently in this post? Heck no, I got plenty more to say!
Multiple Join Rules
You can have multiple join rules, which you may (or may not) combine into Mapping Groups.
- The components within a mapping group have AND relationships: eg., where “sn = sn” AND “department = department”.
- Between the mapping groups we have an OR relationship – so we are looking for an object that satisfies one of them.
- The mapping groups are evaluated top-down. If you are not resolving the search (more on that below) ILM will join the first match, so the order you put them in is significant.
Advanced Join Rules
As well as Direct join rules, we can use .NET to write Advanced rules for when we want to match a single metaverse attribute to a calculated value based on a number of CDS attributes.
For example: Take the first inital and surname from the CDS and try and match it to the uid in the metaverse, using a couple of different capitalisations.
Configuring an Advanced join rule in the MA is very similar to an Advanced flow rule – select the input and output attributes, and give the join rule a name (you do not have to accept the default one).
You now need to write the code for the join rule. And you do that in the MA Extension, under the Sub MapAttributesForJoin.
One of the arguments of this sub is a collection called values. You put your calculated values into here – one of which will hopefully match to a metaverse object.
Continuing on with our uid example, the code might look like this:
Public Sub MapAttributesForJoin(ByVal FlowRuleName As String, ByVal csentry As CSEntry, ByRef values As ValueCollection) Implements IMASynchronization.MapAttributesForJoin Â Select Case FlowRuleName Â Case "joinUid" Â Dim initial as String = csentry("givenName").Value.Substring(0,1) Â values.Add(initial.ToUpper & csentry("sn").Value) Â values.Add(initial.ToUpper & csentry("sn").Value.ToUpper) Â Case Else Â Throw New EntryPointNotImplementedException Â End Select End Sub
So all we’ve done is derive a string, returned it to ILM in the values collection, so that ILM can try to match it against the uid’s it has in the metaverse.
Note that it’s a good idea to leave that “Case Else” in there, just in case someone configures a join rule name without also writing the code.
Resolving Multiple Matches
There are some situations where you want to check through a group of possible matches, and select the most likely one.
I had an experience with trying to match student home directories against usernames. The usernames included the course code, but the home directories just used the first past of the username – ie without the course code. The only way you could tell for sure who’s directory it was was by looking at the folder path, which did include the course code somewhere in the parent heirarchy. Exactly where was not predicatable as it differed from department to department.
The solution was as follows:
- Create a new metaverse attribute called “uidSimple”,
- Add a seperate flow rule on the importing MA that cropped the course code from student IDs and populated uidSimple,
- Add a Direct join rule for the home folders: “folderName = uidSimple”,
- Use a Resolve rule to figure out which folder is the right one.
Configuring a Resolve Rule
In your join rule, tick the box saying you wish to use a rules extension to resolve multiple matches, and then enter a name. Once again you are under no obligation to accept the default (I never do) – just make sure you name check it correctly in your code.
Then modify the Function ResolveJoinSearch in the MA Extension.
This function passes you rgmventry – a collection of metaverse objects which have satisfied your join rule – and a single csentry. Your job is to return a TRUE (match found) or FALSE (match not found), and the index of the found object as imventry.
Public Function ResolveJoinSearch(ByVal joinCriteriaName As String, ByVal csentry As CSEntry, ByVal rgmventry() As MVEntry, ByRef imventry As Integer, ByRef MVObjectType As String) As Boolean Implements IMASynchronization.ResolveJoinSearch Â ResolveJoinSearch = False Â imventry = -1 index = 0 Â Dim mventry As MVEntry Â Select Case joinCriteriaName Â Case "resolveFolder" Â For Each mventry In rgmventry Â If mventry.ConnectedMAs(MAName).Connectors.Count = 0 Then Â 'Only considering metaverse obejcts that are not yet connected in this MA Â If csentry("Path").Value.IndexOf(mventry("CourseCode").Value) > 0 Then Â ResolveJoinSearch = True Â imventry = index Â Exit Function Â End If Â End If Â index = index + 1 Â Next Â Case Else Â Throw New EntryPointNotImplementedException() Â End Select End Function
Once you’ve got these hard-won joins in place, make sure you keep ’em!
There’s lots of creative stuff you can do to help you get a messy CDS joined up. It would, however, be nice if all these complex join rules can be retired once the CDS is being properly managed.
If at all possible export a “breadcrumb” value out to some attribute in the CDS that will enable a simple, 100% certain join to take place in the future if, for any reason, you have to re-import the entire connector space. If you plan for it it won’t hurt a bit when it happens!