On my first ever MIIS project we were an OCG customer so were able to use their nice XML library. I can’t remember in great detail what it did, but I’ve always considered the concept a best practise: if there’s anything that you find yourself hard-coding as a constant value in extension code, then you should put it in a lookup file, where you can change the values later without having to recompile the code.
I’ve been working on a couple of projects this year which are big and complicated enough to warrant a proper lookup file, so I’ve finally had to get in and figure out how to parse XML in .NET. As usual this post is just about things I’ve been doing and I’m not trying to say this is the best way to go. You will have to adapt to your own requirements.   Â
The Structure of the XML File
The general outline of my XML file is to have a section for Global settings, and then individual sections for each MA. Note the “tag” for each MA, which I’ll be using in the code.  The specifc values you include for each MA are entirely up to your requirements.
<SyncParams> <Global> <SyncService> <ServerName>mysyncserver.mydomain.com</ServerName> <IsDev>False</IsDev> </SyncService> <SQL> <Server>mysqlserver.mydomain.com</Server> </SQL> </Global> <ManagementAgents> <MA name='FIM Portal' tag='MAName_FIM'> <maType>FIM</maType> <MVobjectType>person</MVobjectType> <MVobjectType>group</MVobjectType> Â Â Â Â Â <MVobjectSource>group</MVobjectSource> </MA> <MA name='SQL HR Employees' tag='MAName_HR'> <maType>SQL</maType> <MVobjectType>person</MVobjectType> <MVobjectSource>person</MVobjectSource> </MA> <MA name='Notes' tag='MAName_Notes'> <maType>NOTES</maType> <MVobjectType>person</MVobjectType> <MVobjectSource>person</MVobjectSource> </MA> <MA name='AD mydomain' tag='MAName_AD'> <maType>AD</maType> Â Â Â Â Â <provEnabled>True</provEnabled> <MVobjectType>person</MVobjectType> <MVobjectType>group</MVobjectType> <OU_USERS_INTERNAL>OU=Internal,OU=Users,OU=MyOrg,DC=mydomain,DC=com</OU_USERS_INTERNAL> <OU_USERS_EXTERNAL>OU=External,OU=Users,OU=MyOrg,DC=mydomain,DC=com</OU_USERS_EXTERNAL> <OU_USERS_GENERIC>OU=Generic,OU=Users,OU=MyOrg,DC=mydomain,DC=com</OU_USERS_GENERIC> <OU_USERS_RESIGNED>OU=Resigned,OU=Users,OU=MyOrg,DC=mydomain,DC=com</OU_USERS_RESIGNED> <OU_GROUPS>OU=Groups,OU=MyOrg,DC=mydomain,DC=com</OU_GROUPS> <OU_CONTACTS>OU=Contacts,OU=MyOrg,DC=mydomain,DC=com</OU_CONTACTS> </MA> </ManagementAgents> </SyncParams>
Initialization
You can use the Initialization section of your extension code to open and load the XML lookup file. Note that you’ll have to do this in each extension project where you want to access parameters from the file. Your code will also need a reference to System.Xml.   Â
Dim xmlParams As Xml.XmlDocument Public Sub Initialize() Implements IMVSynchronization.Initialize xmlParams = New Xml.XmlDocument Dim xmlPath As String = Utils.ExtensionsDirectory & "\SyncParams.xml" Try xmlParams.Load(xmlPath) Catch ex As Exception Throw New ExtensionException("Failed while opening XML parameter file. " & ex.Message) End Try End Sub   Â
Accessing the Global parameters
TheGlobal parameters are easy to get to because there’s only one node for them. So to retrieve the server name from the XML file above I’d just do this:Â Â
xmlParams.SelectSingleNode("//Global/SyncService/ServerName").InnerText  Â
Accessing a specific MA’s Parameters
The next thing you will probably want to do is access the values for a specifc MA. I’m using the “tag” attribute as a way to identitfy the correct node. In this example I retrieve an OU name from the “AD mydomain” parameters.  Â
xmlParams.SelectSingleNode("//ManagementAgents/MA[@tag='MAName_AD']/OU_USERS_EXTERNAL").InnerText
Creating a Hash Table of MA Names
The other thing that can be useful is to make a hash table of MA Names for easy reference. First you need to define a global variable:Â Â
Dim MA_Names As New Collection
Then you add something like this to the Initialization sub:Â Â
For Each xmlNode In xmlParams.SelectNodes("//ManagementAgents/MA") MA_Names.Add(xmlNode.Attributes("name").Value, xmlNode.Attributes("tag").Value) Next
Now in the rest of the project, when you want to use an MA’s name, you only need refer to it using its tag: Â
MA_Names.Item("MAName_AD").ToStringÂ
Selectively disabling provisioning
Another thing you might like to do, especially if you have a number of MAs that you are provisioning to, is to be able to selectively disable the provisioning code for one of them just by using the XML file.
Obviously you’ll have to modify your provisioning code to check the value of the flag. In the example above I would need to test for the value of “provEnabled” before calling the provisioning code for the “AD mydomain” MA:
If xmlParams.SelectSingleNode("//ManagementAgents/MA[@tag='MAName_AD']/provEnabled").InnerText = "True" then Provision_AD(mventry) End If
Conclusion
Of course all of this takes more effort in the coding but the code will be more robust if it avoids hard-coded values, and it will be easier for others to make simple changes.
Be careful with not guarding the SelectSingleNode calls – if the XPath expression doesn’t return anthing, accessing InnerText will throw a NullReferenceException which may be less than ideal depending on where you are calling it.
I usually have a generic GetConfigEntry type method which takes a defaultValue parameter to return if nothing is configured.
Also btw – inside System.IO is a class called Path – rather than concatenating strings together (and hoping you have all your slahes in order), Path.Combine will do it all for you. 🙂
OK thanks, I’ll have a look at Path.Combine – I’m just learning this stuff.
Yes I do a lot more error checking in my code, but it makes for a very long blog post 🙂