This topic demonstrates how a file-based connected data source uses connected data source extensions to import objects.

When you create an extensible management agent in Synchronization Service Manager based on the sample code in this topic, you must have a file template to describe the schema of the import file to create the synchronization rules. The sample code in this topic creates an import file that has the following schema:

  Copy Code
ObjectClass,Delta,Anchor-Attribute,Name,Email
Person,Add,1,Object1,Object1@fabrikam.com

You can use this schema as a template for creating a management agent. Copy it to a file using a text editor, save the file, and select it as the Template Input File in the sample code in this topic.

The following C# example shows how a file-based connected data source uses connected data source extension to import objects. For more information about how this extension works, see Connected Data Source Extensions for File-Based Data Sources.

C#  Copy Code
using System;

using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Specialized;
using Microsoft.MetadirectoryServices;

namespace SampleMAFileExport
{
   public class SampleMAFileExport :  IMAExtensibleFileImport, IMAExtensibleFileExport

	{
		public void GenerateImportFile( string					filename, 
								 
										string					connectTo, 
								 
										string					user, 
															 
										string					password, 
															 
										ConfigParameterCollection   configParameters,
															 
										bool						fullImport, 
															 
										TypeDescriptionCollection   types,
															 
										ref string				customData 
									)
		{
			//
			// In the following sample, we read the XML file and generate
			// a comma-delimited XML file.
			// NOTE: The connectTo, user, and password attributes in the XML file
			// are not used. However, if the connected data source
			// requires this connection information, these connection
			// parameters are passed into the method.
			// customData can be used for retaining necessary data
			// for performing a subsequent import. 
			// A typical case of this might be the watermark used 
			// for delta import.
			//
		
			SampleFile.GenerateSampleImportFile(
		
												configParameters,
																			
												types,
																			
												fullImport
																			
												? @"c:\sample_full_import.xml" 
																			
												: @"c:\sample_delta_import.xml",
																			
												filename,
																			
												UnicodeEncoding.Unicode
																			
											 );
	}
	
		public void DeliverExportFile(  string					fileName,
								
										string					connectTo,
															
										string					user,
															
										string					password,
															
										ConfigParameterCollection   configParameters,
															
										TypeDescriptionCollection   types
									 
									 )
									 
		{
	
			throw new EntryPointNotImplementedException();
	
	}
	
}

	public class SampleFile

	{

		// This class is used to read XML and to mimic a connection to the 
		// connected directory.
	
		public static void GenerateSampleImportFile(
	
													ConfigParameterCollection   configParameters,
												
													TypeDescriptionCollection   types,
												
													string					filenameXML,
												
													string					filenameGeneratedImport,
												
													System.Text.Encoding		encoding
												
													)
		
		{
		
			StreamWriter	swImport	= new StreamWriter(
											
															filenameGeneratedImport, 
																						
															false, 
																						
															encoding
																								
														);
													
			XmlDocument	 doc		 = new XmlDocument();
		
			XmlElement	root		= null;

			//
			// Read the sample XML file from GenerateSampleImportFile.
			//
			doc.Load(filenameXML);
			
			root = doc.DocumentElement;

			//
			// Write out the header of the delimited file.
			//
			swImport.WriteLine(
		
								"{0},{1},{2},{3},{4}",
						
								Nodes.ObjectClass,
						
								Nodes.Delta,
						
								Nodes.Anchor,
						
								Nodes.Name,
						
								Nodes.Email
		
							);
			
			foreach (XmlNode node in root.ChildNodes)
			{
		
				GenerateSampleImportFileForNode(
		
					(XmlElement)node, 
		
					configParameters, 
		
					types, 
		
					swImport
		
					);
		
		}

			swImport.Close();
		
	}

		private static void GenerateSampleImportFileForNode(
		
															XmlElement				elem,
																										
															ConfigParameterCollection   configParameters,
																										
															TypeDescriptionCollection   types,
																										
															StreamWriter				swImport
																										
															)
	
		{
		
			try
	
			{
		
				string			objectclassValue	= null;
		
				string			deltaValue		= null;
		
				string			nameValue		 = null;
		
				string			emailValue		= null;
		
				string			anchorValue		 = null;
		
				TypeDescription	 typeDescription	 = null;
							
				//
				// In the following sample, we do not use the ConfigParameterCollection
				// class. However, if you want to use it, you can do something
				// similar to the following solution.
				//
				//  try
				//  {
				//	ConfigParameter configParam		 = configParameters["Attrib"];
				//	string		configAttribValue   = configParam.Value;
				//  }
				//  catch (NoSuchParameterException)
				//  {
				//	...
				//  }
				//
				foreach (XmlNode node in elem.ChildNodes)
			
				{
					// NOTE: We assume that the XML is well-formed. The sample 
					// does not do any validation.
				
					if (Nodes.ObjectClass == node.Name)
				
					{
						//
						// Check to see if the object class that we are 
						// currently importing is something that we are 
						// interested in.
						// We do this by accessing the object class. If it is 
						// not supported, an exception will be thrown.
						// This is done for illustrative purposes and 
						// is not mandatory.
						//
						typeDescription = types[node.InnerText];

						objectclassValue = node.InnerText;
				
				}
				
					else if (Nodes.Delta == node.Name)
				
					{
						//
						// Delta node
						//
						deltaValue = node.InnerText;
				
				}
				
					else
				
					{
						//
						// As an example, we want to process all 
						// the attributes in the file. Some may not
						// be interesting to use. We will skip those attributes
						// with our try-catch block below.
						// The following task is an example only and is
						// not mandatory.
						// 
						// NOTE: We have made the assumption in this sample
						// that the XML for objectClass appears before 
						// any attributes below.
						// Otherwise, typeDescription is null and an
						// exception is thrown.
						//
						try
						{
				
							AttributeDescription	attribDescription;
					
							//
							// Access each attribute to see if an exception							 // will be thrown.
							//
				
							attribDescription = 
								typeDescription.Attributes[node.Name];

							//
							// Populate the correct attribute.
							//
							if (Nodes.Anchor == node.Name)
				
							{
				
								anchorValue = node.InnerText;
				
						}
				
							if (Nodes.Name == node.Name)
				
							{
				
								nameValue = node.InnerText;
				
						}
				
							else if (Nodes.Email == node.Name)
				
							{
				
								emailValue = node.InnerText;
				
						}
				
					}
				
						catch (NoSuchAttributeException)
				
						{
							// The attribute is not defined. Do not process it.
						
				
					}
				
				}
			
			}
			
				swImport.WriteLine(
			
					"{0},{1},{2},{3},{4}",
			
					objectclassValue,
			
					deltaValue,
			
					anchorValue,
			
					nameValue,
			
					emailValue
			
					);
				
		}
		
			catch (NoSuchClassException)
		
			{
				//
				// The object class is not defined. Do not process it.
				//
		
		}
		
	}
}

	struct Nodes
	{
		//
		// Struct used to keep track of the XML node names.
		// This is used when generating the XML file.
		//

		public const string Root		= "sample-objects";

		public const string Object	= "object";

		public const string Anchor	= "anchor-attribute";

		public const string Delta	 = "delta";

		public const string ObjectClass = "objectclass";

		public const string Name		= "name"; 	

		public const string Email	 = "email"; 	

}
}


The following Visual Basic example shows how a file-based connected data source uses a connected data source extension to import objects. For more information about how this extension works, see Connected Data Source Extensions for File-Based Data Sources.

Visual Basic  Copy Code
Imports System

Imports System.IO

Imports System.Xml

Imports System.Text

Imports System.Collections.Specialized

Imports Microsoft.MetadirectoryServices

Public Class SampleMAFileExport

	Implements IMAExtensibleFileImport

	Implements IMAExtensibleFileExport

	Public Sub GenerateImportFile(ByVal fileName As String, _
								ByVal connectTo As String, _
								ByVal user As String, _
								ByVal password As String, _
								ByVal configParameters As ConfigParameterCollection, _
								ByVal fFullImport As Boolean, _
								ByVal types As TypeDescriptionCollection, _
								ByRef customData As String) _
								Implements IMAExtensibleFileImport.GenerateImportFile

		' In the following sample, we read the XML file and generate
		' a comma-delimited XML file.
		'
		' NOTE: The connectTo, user, and password attributes in the 
		' XML file are not used. However, if the connected data source
		' requires this connection information, these connection
		' attributes are passed into the method.
		' customData can be used for retaining necessary data
		' for performing a subsequent import. 
		' A typical scenario for this might be the watermark used 
		' for delta import.
	 

		Dim importFile As String

		If fFullImport Then

			importFile = "c:\sample_full_import.xml"

		Else

			importFile = "c:\sample_delta_import.xml"

		End If

		SampleFile.GenerateSampleImportFile(configParameters, _
											types, _
											importFile, _
											fileName, m_encoding)

	End Sub

	Public Sub DeliverExportFile(ByVal fileName As String, _
								 ByVal connectTo As String, _
								 ByVal user As String, _
								 ByVal password As String, _
								 ByVal configParameters As ConfigParameterCollection, _
								 ByVal types As TypeDescriptionCollection) _
								 Implements IMAExtensibleFileExport.DeliverExportFile

		Throw New EntryPointNotImplementedException

	End Sub

	Private m_xmlWriterExport As XmlTextWriter

	Private m_encoding As Encoding

End Class

Public Class SampleFile

	'
	' This class is used to read XML and to mimic a connection to the 
	' connected directory.
	'
	Public Shared Sub GenerateSampleImportFile(ByVal configParameters As ConfigParameterCollection, _
											 ByVal types As TypeDescriptionCollection, _
											 ByVal filenameXML As String, _
											 ByVal filenameGeneratedImport As String, _
											 ByVal encoding As System.Text.Encoding)

		Dim swImport As New StreamWriter(filenameGeneratedImport, False, encoding)

		Dim doc As New XmlDocument

		Dim root As XmlElement = Nothing

		'
		' Read the sample XML file from the preceding specified file.
		'
		doc.Load(filenameXML)

		root = doc.DocumentElement

		'
		' Write out the header of the delimited file.
		'
		swImport.WriteLine("{0},{1},{2},{3},{4}", Nodes.ObjectClass, Nodes.Delta, Nodes.Anchor, Nodes.Name, Nodes.Email)

		Dim node As XmlNode

		For Each node In root.ChildNodes

			GenerateSampleImportFileForNode(CType(node, XmlElement), configParameters, types, swImport)

		Next node

		swImport.Close()

	End Sub

	Private Shared Sub GenerateSampleImportFileForNode(ByVal elem As XmlElement, ByVal configParameters As ConfigParameterCollection, ByVal types As TypeDescriptionCollection, ByVal swImport As StreamWriter)

		Try

			Dim objectclassValue As String = Nothing

			Dim deltaValue As String = Nothing

			Dim nameValue As String = Nothing

			Dim emailValue As String = Nothing

			Dim anchorValue As String = Nothing

			Dim typeDescription As TypeDescription = Nothing

			'
			' In the sample below, we do not use the ConfigParameterCollection
			' class. However, if you want to use it, you can do something 
			' similar to the following solution.
					 '
			'  try
			'  {
			'	ConfigParameter configParam		 = configParameters["Attrib"];
			'	string		configAttribValue   = configParam.Value;
			'  }
			'  catch (NoSuchParameterException)
			'  {
			'	...
			'  }
			'
			Dim node As XmlNode

			For Each node In elem.ChildNodes

				'
				' NOTE: We assume the correct order of the XML coming in.
				' The sample does not do any validation. 
			
				If Nodes.ObjectClass = node.Name Then

					'
					' Check to see if the object class that we are 
					' currently importing is something that we are 
					' interested in.
					' We do this by accessing the object class. If it is 
					' not supported, an exception will be thrown.
					' The following task is an example only and is not mandatory.
					'
					typeDescription = types(node.InnerText)

					objectclassValue = node.InnerText

				ElseIf Nodes.Delta = node.Name Then

					'
					' Delta node
					'
					deltaValue = node.InnerText

				Else
					'
					' As an example, we want to process all 
					' the attributes in the file. Some may not
					' be interesting to use. We will skip those attributes
					' via our try-catch block below.
					' The following tasks is an example only and is not mandatory.
				
					' NOTE: We have made the assumption in this sample
					' that the XML for the objectClass appears before 
					' any attributes below.
					' Otherwise, typeDescription would be null and an
					' exception would be thrown.
					'
					Try

						Dim attribDescription As AttributeDescription

						'
						' Access each attribute to see if an exception will
						' be thrown.
						'
						attribDescription = typeDescription.Attributes(node.Name)

						'
						' Populate the proper attribute below.
						'
						If Nodes.Anchor = node.Name Then


							anchorValue = node.InnerText
						End If

						If Nodes.Name = node.Name Then


							nameValue = node.InnerText


						ElseIf Nodes.Email = node.Name Then


							emailValue = node.InnerText
						End If


					Catch
						' Attribute is not defined and so we do not need to process it.
												'
					End Try

				End If

			Next node

		 swImport.WriteLine("{0},{1},{2},{3},{4}", _
							objectclassValue, _
							deltaValue, _
							anchorValue, _
							nameValue, _
							emailValue)

		Catch

		' Object class is not defined and so we do not need to process it.

		End Try

	End Sub

End Class

Module Nodes
	'
	' Struct used to keep track of the XML node names.
	' This is used when generating the XML file.
	'
	Public Const Root As String = "sample-objects"

	Public Const [Object] As String = "object"

	Public Const Anchor As String = "anchor-attribute"

	Public Const Delta As String = "delta"

	Public Const ObjectClass As String = "objectclass"

	Public Const Name As String = "name"

	Public Const Email As String = "email"

End Module 'Nodes 

See Also