Partager via


Best Practices for Data Source Extension Exports

Note

With the release of ECMA 2.0, this feature has been deprecated and will be removed in future versions. You should use the Extensible Connectivity 2.0 Management Agent Reference for Connector development going forward.

During an export to a connected directory, a problem can occur if the export session times out. In this event, resources are not released for unmanaged objects. Normally, when an export is completed, the EndExport method is called, which releases these resources. However, during a time-out, the object on which the EndExport method is normally called is unreachable.

To mitigate this problem, you must add a destructor in each class that you can call to clean up unmanaged resources when the export times out. The destructor contains cleanup code that unloads the application domain and aborts the export. For more information, see Destructors.

In your extension application, this problem might occur in the classes that implement the following interfaces:

If there are multiple destructors in the extension, there is no guarantee that they will be called in a certain order. For that reason, you should not make a reference between two classes that have destructors because one class might be destroyed before the other class can use its resources.

An example of how to use a destructor and cleanup code is shown in the following example.

Using the try-finally Statement

Another good practice is to use the try-finally block in all your extensions. When you use this statement, any managed resources that are allocated in the try block are cleaned up in the finally block. You should use the try-finally block at single function entry points, such as GenerateImportFile or DeliverExportFile. For more information, see try-finally.

Code Example for Data Source Extensions Used for Export

The following C# code example shows how to use a destructor, a cleanup statement, and a try-finally block.

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

namespace Miis_CallExport
{
    public class MACallExport :  
        IMAExtensibleFileImport, 
        IMAExtensibleCallExport
    {

        bool m_fInitialized;      // Used to indicate if we have initialized resources

        //
        // Constructor
        //
        public MACallExport(
            )
        {
            //
            // TODO: Add constructor logic here
            //

            // Set member variable indicating we have initialized class
            m_fInitialized = true;
        }

        //
        // Destructor
        //
        ~MACallExport()
        {
            if (m_fInitialized)
                Cleanup(true);
        }

        //
        //  Generic cleanup function used for import and exports.
        //  Cleanup is always called regardless, even if an
        //  exception is thrown from the import and export interfaces.
        //  It is also called in the case when the function times out.
        //  In this case it is called from the destructor (fFromDestructor 
        //  is set to true) and it should not rely on the 
        //  existence of other object instances, since garbage 
        //  collection is non-deterministic.
        //
        public void Cleanup(
            bool fFromDestructor
            )
        {
            if (!fFromDestructor)
            {
                //
                // TODO: Add cleanup logic.  Member objects of this 
                // class instance can be accessed. 
                //            
            }

            //
            // TODO: Add cleanup logic for unmanaged resources of this class.
            //            

            // Set member variable indicating we have released resources.
            m_fInitialized = false;
        }

        public void GenerateImportFile( 
            string                      filename, 
            string                      connectTo, 
            string                      user, 
            string                      password, 
            ConfigParameterCollection   configParameters,
            bool                        fullImport, 
            TypeDescriptionCollection   types,
            ref string                  customData 
            )
        {
            try
            {
                //
                // TODO: Add logic here to create import file.
                //
            }
            finally
            {
                //
                // TODO: Add code for import specific cleanup.
                //

                //
                // Call cleanup code to deallocate resources.
                //
                Cleanup(false);
            }
        }

        public void BeginExport( 
            string                      connectTo, 
            string                      user, 
            string                      password,
            ConfigParameterCollection   configParameters,
            TypeDescriptionCollection   types
            )
        {
            //
            // TODO: Add code to initialize an export run.
            //            
        }

        public void ExportEntry( 
            ModificationType    modificationType, 
            string[]            changedAttributes,
            CSEntry             csentry 
            )
        {
            //
            // TODO: Add code to export one entry to the target.
            //            
        }

        public void EndExport(
            )
        {
            //
            // TODO:  Add code for export specific cleanup.
            //

            //
            // Call cleanup code to deallocate resources.
            //
            Cleanup(false);
        }
    }
}

See Also

Concepts

Connected Data Source Extensions
Creating Connected Data Source Extensions