Utility to generate Word documents from templates using Visual Studio 2010 and Open Xml 2.0 SDK – Part 4

[Update: The source code has been updated to Visual Studio 2017 and DocumentFormat.OpenXml 2.8.1You can download the code from GitHub.]

This is the fourth part of this series. The code is available for download at http://worddocgenerator.codeplex.com/.

In Part 1 I discussed about

  • Document generation using Content Controls and Open XML 2.0 SDK
  • Creating Word templates
  • Implementation and Samples

In Part 2 I discussed about

  • List of functionalities that can be achieved using the utility/source code
  • Description regarding Samples provided with utility
  • New samples added in this update

In Part 3 I discussed about

  • Document-level customizations for Word 2007 and Word 2010
  • One of the ways to “Refresh the document from within the Word(e.g. right click on document and click Refresh) using document-level customizations for Word 2007 and Word 2010“

This post will focus on Charts. I’ve added the samples to generate/refresh charts using OpenXML. The screenshot below displays the template having Scatter chart and Line chart and the document generated out of this template using this utility.

Word 2010 Template having Charts –> Generated documents with Charts refreshed:

image

image

Code Changes:

Class diagram for Chart specific classes is displayed below

image

“SampleDocumentWithTableAndChartsGenerator” is the sample that shows how to refresh template having Scatter and Line charts. The code snippets are displayed below

  • “RefreshCharts” method is added to DocumentGenerator class
/// <summary>

/// Refreshes the charts.

/// </summary>

/// <param name="mainDocumentPart">The main document part.</param>

protected virtual void RefreshCharts(MainDocumentPart mainDocumentPart)

{


}

  • “SampleDocumentWithTableAndChartsGenerator” class overrides “RefreshCharts” method
/// <summary>

/// Refreshes the charts.

/// </summary>

/// <param name="mainDocumentPart">The main document part.</param>

protected override void RefreshCharts(MainDocumentPart mainDocumentPart)

{

    if (mainDocumentPart != null)

    {

        foreach (ChartPart chartPart in mainDocumentPart.ChartParts)

        {

            Chart chart = chartPart.ChartSpace.Elements<Chart>().FirstOrDefault();


            if (chart != null)

            {

                DocumentFormat.OpenXml.Drawing.Charts.ScatterChart scatterChart = chart.Descendants<DocumentFormat.OpenXml.Drawing.Charts.ScatterChart>().FirstOrDefault();

                DocumentFormat.OpenXml.Drawing.Charts.Line3DChart lineChart = chart.Descendants<DocumentFormat.OpenXml.Drawing.Charts.Line3DChart>().FirstOrDefault();


                if (scatterChart != null)

                {

                    ScatterChartEx chartEx = new ScatterChartEx(chartPart, this.scatterChartData);

                    chartEx.Refresh();

                }


                if (lineChart != null)

                {

                    Line3DChartEx chartEx = new Line3DChartEx(chartPart, this.lineChartData);

                    chartEx.Refresh();

                }

            }


            chartPart.ChartSpace.Save();

        }

    }

}

  • Refresh method is defined in ChartEx<T>
/// <summary>

/// Refreshes this instance.

/// </summary>

public void Refresh()

{

    ChartData chartData = this.GetChartData();


    if (chartData != null && chartData.IsValid())

    {

        string sheetName = this.UpdateEmbeddedObject();


        Chart chart = chartPart.ChartSpace.Elements<Chart>().FirstOrDefault();


        if (chart != null)

        {

            this.UpdateChart(chart.Descendants<T>().FirstOrDefault(), sheetName);

        }

    }

}

For complete code download the source code.

Summary:

Please provide feedback/comments and I’ll try to incorporate most of them in new releases.

How to use Pex and Moles to generate unit tests for a project having external dependency(WCF Proxy) using Visual Studio 2010 SP1

Source Code:

In this post I’ll discuss about writing unit tests using Pex and Moles. Pex and Moles are Visual Studio 2010 Power Tools that help Unit Testing .NET applications.

  • Pex automatically generates test suites with high code coverage. Right from the Visual Studio code editor, Pex finds interesting input-output values of your methods, which you can save as a small test suite with high code coverage.
  • Moles allows to replace any .NET method with a delegate. Moles supports unit testing by providing isolation by way of detours and stubs i.e. Generate a Mole type from the original type and redefine its behavior using delegates.

Pex and Moles can be downloaded from http://research.microsoft.com/en-us/projects/pex/.

I’ll explain the steps to generate unit tests for a project which calls a WCF service. Pex will be used to generate unit tests. Moles will be generated to isolate the external dependency(WCF proxy) and behavior will be redefined using delegates.

The projects inside the sample solution are

  1. DemoService: This project is a WCF Service.
  2. DemoLibrary: This project is a Class library and service reference to DemoService has been added. Unit tests will be generated for this project.
  3. ConsoleApp: This project is a Console application.
  4. DemoLibrary.Tests: This is a Test project and contains unit tests for DemoLibrary.

The solution structure is displayed below

image

DemoLibrary calls DemoService though proxy as displayed in the Layer diagram

image

I’ll now discuss in brief the code snippets of each project

WCF Service(DemoService): This service provided only a single operation

[ServiceContract]

public interface IDemoService

{

    [OperationContract]

    string Search(string criteria);

}

WCF Service Client(DemoLibrary): It calls the Search method of DemoService through proxy as displayed below

public string GetResults(string s)

{

    DemoServiceReference.DemoServiceClient client = null;

 

    try

    {

        client = new DemoServiceReference.DemoServiceClient();

        client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;                

        client.ChannelFactory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;

        s = client.Search(s);

        return s;

    }

    finally

    {

        if (client != null)

        {

            if (client.State == CommunicationState.Opened)

            {

                client.Close();

            }

            else if (client.State == CommunicationState.Faulted)

            {

                client.Abort();

            }

        }

    }

}

Unit Testing DemoLibrary using Pex and Moles:

In order to generate unit tests for WCF Service Client(DemoLibrary) project the steps are

  1. Right click on the class for which unit tests needs to be generated and click “Create Parameterized Unit Tests” as displayed belowimage
  2. A popup will be displayed where Filters and Output can be modified. Click Ok to go to next step.image
  3. A new test project will be created. Test Class and Test stub will be be added to this project as displayed below
    /// <summary>This class contains parameterized unit tests for Search</summary>

    [PexClass(typeof(Search))]

    [PexAllowedExceptionFromTypeUnderTest(typeof(InvalidOperationException))]

    [PexAllowedExceptionFromTypeUnderTest(typeof(ArgumentException), AcceptExceptionSubtypes = true)]

    [TestClass]

    public partial class SearchTest

    {

        /// <summary>Test stub for GetResults(String)</summary>

        [PexMethod]

        public string GetResults([PexAssumeUnderTest]Search target, string s)

        {

            string result = target.GetResults1(s);

            return result;

            // TODO: add assertions to method SearchTest.GetResults(Search, String)

        }

    }

  4. There is an external dependency(GetResults makes a service call though the WCF Proxy) so “Run Pex Explorations” will not generate unit tests as displayed below                                                                          image
  5. In order to isolate the external dependency we need to generate Moles before running Pex Explorations. Moles will be generated for DemoLibrary and System.ServiceModel assemblies and behavior will be redefined using delegates. There are two ways to generate a mole for an assembly. I’ll show you both the approaches
    1. Visual Studio: This is the easiest way.
      1. Right click on the reference and generate Moles for that assembly as displayed below.image
      2. A .Moles file will be added to the project. Build the project and Moles.dll will be added to MolesAssemblies folder as displayed below                                                                                 .image
    2. Command Prompt: Moles can be generated from the command prompt.
      1. Run the moles.exe and specify the assembly path for which Moles needs to be created.image_thumb2[1]
      2. Copy the generated assembly to the Project and add reference to it.
  6. Similarly as explained above we need to generate Moles for System.ServiceModel assembly. For Visual Studio 2010 SP1 this may fail with error message “The type or namespace name ‘IHttpCookieContainerManager’ does not exist in the namespace ‘ssm::System.ServiceModel.Channels’ (are you missing an assembly reference?)”. This step however works fine for Visual Studio 2010. The fix is to exclude the type i.e. ‘IHttpCookieContainerManager’ from StubGeneration as displayed belowimage
  7. The next step is to Mock the Service call(redefine behavior using delegates) and add Asserts as displayed in code snippets below
    /// <summary>Test stub for GetResults(String)</summary>

    [PexMethod]

    public string GetResults([PexAssumeUnderTest]Search target, string s)

    {

        MockWCFService<IDemoService>();

     

        MDemoServiceClient.Constructor = (var1) =>

           new MDemoServiceClient { };

     

        MDemoServiceClient.AllInstances.SearchString = (var1, var2) =>

        {

            return "Result";

        };

     

        string result = target.GetResults(s);

        PexAssert.IsNotNullOrEmpty(result);

        PexAssert.AreEqual(result, "Result");

        return result;            

    }

     

    /// <summary>

    /// Mocks the WCF service.

    /// </summary>

    private void MockWCFService<TService>() where TService : class

    {

        MClientCredentials.Constructor = (var1) =>

            new MClientCredentials()

            {

                WindowsGet = () => { return new MWindowsClientCredential(); }

            };

     

        MClientCredentials.AllInstances.WindowsGet = (var1) =>

        {

            return new MWindowsClientCredential();

        };

     

        MWindowsClientCredential.AllInstances.ClientCredentialGet = (var1) =>

        {

            return new System.Net.NetworkCredential();

        };

     

        MWindowsClientCredential.AllInstances.ClientCredentialSetNetworkCredential = (var1, var2) => { };

     

        MWindowsClientCredential.AllInstances.AllowNtlmGet = (var1) => { return true; };

     

        MWindowsClientCredential.AllInstances.AllowNtlmSetBoolean = (var1, var2) => { };

     

        MWindowsClientCredential.AllInstances.AllowedImpersonationLevelGet = (var1) => { return System.Security.Principal.TokenImpersonationLevel.Impersonation; };

     

        MWindowsClientCredential.AllInstances.AllowedImpersonationLevelSetTokenImpersonationLevel = (var1, var2) => { };

     

        MChannelFactory.AllInstances.CredentialsGet = (var1) => { return new MClientCredentials(); };

     

        MClientBase<TService>.AllInstances.ClientCredentialsGet = (var1) =>

        {

            return new System.ServiceModel.Description.ClientCredentials();

        };

     

        MClientBase<TService>.AllInstances.ChannelFactoryGet = (var1) =>

        {

            return new MChannelFactory01<TService>();

        };

     

        MClientBase<TService>.AllInstances.StateGet = (var1) =>

        {

            return PexChoose.EnumValue<CommunicationState>("CommunicationState");

        };

     

        MClientBase<TService>.AllInstances.Close = (var1) =>

        { };

     

        MClientBase<TService>.AllInstances.Abort = (var1) =>

        { };              

    }

  8. Run Pex Explorations to generate unit tests for GetResults method as displayed below image
  9. Unit tests will be added to the Test Class as displayed below                      image
  10. Go to Test View and Run the unit tests as displayed below                  image

 

Summary:

In a similar way we can extract out external dependencies using Moles and then run Pex Explorations. A few examples of external dependencies can be data layer, UI layer, server calls etc. You can read more about Pex and Moles at http://research.microsoft.com/en-us/projects/pex/.

Utility to generate Word documents from templates using Visual Studio 2010 and Open Xml 2.0 SDK – Part 3

[Update: The source code has been updated to Visual Studio 2017 and DocumentFormat.OpenXml 2.8.1You can download the code from GitHub.]

This is the third post of this series. The earlier posts can be read at Part 1 and Part 2. The code is available for download at Utility Source Code.

In Part 1 I discussed about

  • Document generation using Content Controls and Open XML 2.0 SDK
  • Creating Word templates
  • Implementation and Samples

In Part 2 I discussed about

  • List of functionalities that can be achieved using the utility/source code
  • Description regarding Samples provided with utility
  • New samples added in this update

In this part I’ll discuss about the sample that shows one of the ways to “Refresh the document from within the Word(e.g. right click on document and click Refresh) using document-level customizations for Word 2007 and Word 2010“. On click of Refresh Document the content of the document is refreshed as displayed below

image

Project “WordDocumentGenerator.WordRefreshableDocumentAddin” has been added to the utility for this sample. The steps followed for creating this sample are listed below

  1. Added a new Word 2010 Document project as displayed below image
  2. Updated the document by adding the content controls as displayed below image
  3. Added a new Command bar button i.e. “Refresh Data” to the Command bar. On click of this button the document will be refreshed. The common scenario will be to refresh data from the Service. “WordDocumentGenerator.WordRefreshableDocumentAddin” is the document level customization project. This project references “WordDocumentGenerator.Library” and “WordDocumentGenerator.Client” projects.

The code snippet to add a new Command bar button is

/// <summary>

/// Adds the command bar.

/// </summary>

/// <param name="cmdBr">The CMD br.</param>

/// <param name="handler">The handler.</param>

/// <param name="index">The index.</param>

/// <param name="tag">The tag.</param>

/// <param name="caption">The caption.</param>

/// <returns></returns>

private CommandBarButton AddCommandBar(CommandBar cmdBr, _CommandBarButtonEvents_ClickEventHandler handler, int index, string tag, string caption)

{

    CommandBarButton cmdBtn = (CommandBarButton)cmdBr.FindControl(MsoControlType.msoControlButton, 0, tag, missing, missing);


    if ((cmdBtn != null))

    {

        cmdBtn.Delete(true);

    }


    cmdBtn = (CommandBarButton)cmdBr.Controls.Add(MsoControlType.msoControlButton, missing, missing, index, true);

    cmdBtn.Style = MsoButtonStyle.msoButtonCaption;

    cmdBtn.Caption = caption;

    cmdBtn.Tag = tag;

    cmdBtn.Visible = true;


    cmdBtn.Click -= handler;

    cmdBtn.Click += handler;


    if (!commandBarsTags.Contains(tag))

    {

        commandBarsTags.Add(tag);

    }


    return cmdBtn;

}

On click of refresh data the main steps are

1. Get package steam from the document

Microsoft.Office.Interop.Word.Document doc = app.ActiveDocument;


// Get the active documents as stream of bytes

byte[] input = doc.GetPackageStream();

2. Call the Service/Client method that generates/refreshes the document. This can be a Server or a direct call. In this method it’s a direct call.

// Generate document on the Server. AddInService can be a proxy to service, however here it's direct call

byte[] output = AddInService.GenerateDocument(input);

/// <summary>

/// Generates the document.

/// </summary>

/// <param name="documentStream">The document stream.</param>

/// <returns></returns>

public static byte[] GenerateDocument(byte[] documentStream)

{}

3. Update the document contents

XDocument xDoc = OPCHelper.OpcToFlatOpc(wordDocument.Package);

string openxml = xDoc.ToString();

doc.Range().InsertXML(openxml);

These are partial code snippets to show the code flow. For complete sample please download the source code.

References:

Utility to generate Word documents from templates using Visual Studio 2010 and Open Xml 2.0 SDK – Part 2 (Samples Updated)

[Update: The source code has been updated to Visual Studio 2017 and DocumentFormat.OpenXml 2.8.1You can download the code from GitHub.]

This is the second post of this series. The previous post can be read at Part 1. The next post can be read at Part 3. The code is available for download at Utility Source Code.

In Part 1 I discussed about

  • Document generation using Content Controls and Open XML 2.0 SDK
  • Creating Word templates
  • Implementation and Samples

In Part 3 I have explained one of the way to “Refresh the document from within the document(e.g. right click on document and click Refresh) using document-level projects for Word 2007 and Word 2010“

In this post I’ll discuss

  • List of functionalities that can be achieved using the utility/source code
  • Description regarding Samples provided with utility
  • New samples added in this update

The sample document generators are discussed later. The functionalities that can be achieved using the utility/source code are:
Document Generation

  1. Generate document from a Word template using content controls as place holders and populate controls with data(Object)[SampleDocumentGenerator, SampleRefreshableDocumentGenerator, SampleDocumentWithTableGenerator]
  2. Generate document from a Word template using content controls as place holders(data bound content controls) and populate controls with data(Object is serialized to Xml). [SampleDocumentGeneratorUsingDatabinding, SampleDocumentWithTableGeneratorUsingDatabinding, SampleDocumentGeneratorUsingXmlAndDatabinding]
  3. Generate document from a Word template using content controls as place holders and populate controls with data(XmlNode)[SampleDocumentGeneratorUsingXml]
  4. Generate document from a Word template using content controls as place holders(data bound content controls) and populate controls with data(XmlNode) [SampleDocumentGeneratorUsingXmlAndDatabinding]
  5. Refresh the document from within the document(e.g. right click on document and click Refresh) using document-level projects for Word 2007 and Word 2010 [Explained in next post i.e. Part 3]
  6. Generate document that can be
    1. Standalone: Once generated document cannot be refreshed.
    2. Refreshable: Once generated document can be refreshed. Content controls will be added/updated/deleted and content control’s content will be refreshed as per data.
  7. Append documents using AltChunk
  8. Protect Document
  9. UnProtect Document
  10. Removal of Content Controls from a document while keeping contents
  11. Removal of Foot notes
  12. Ensuring the each content control has unique Id’s by fixing the duplicate Id’s if any for a document
  13. Serializing an Object to Xml using XmlSerializer(Used for document generation using data bound content controls as serialized object is written to CustomXmlPart)

Content Controls

  1. Set text of a content control(not applicable for data bound content controls)
  2. Get text from a content control(not applicable for data bound content controls)
  3. Set text of content control while keeping PermStart and PermEnd elements(not applicable for data bound content controls)
  4. Set Tag of a content control
  5. Get Tag of a content control
  6. Set data binding of a content control
  7. Set text of a data bound content control from CustomXmlPart manually. This is helpful in cases when CustomXmlPart needs to be removed and this copies the text from the CustomXmlPart node using XPath.

CustomXmlPart

  1. Adding a CustomXmlPart to a document
  2. Removing CustomXmlPart from a document
  3. Getting CustomXmlPart from a document
  4. Add/Update a Xml element node inside CustomXmlPart. This is required
    1. To keep Document related metadata e.g. Document type, version etc.
    2. To make the Document self-refreshable. In this case the container content control is persisted inside a Placeholder node, the first time document is generated from template. Onwards when refreshing document we fetch the container content control from CustomXmlPart
    3. Saving the Xml e.g. serialized object which will be the data store for data bound content controls

Sample Generators:

  1. SampleDocumentGenerator: This sample shows how to generate a non-refreshable document from a template. The content controls are populated using C# code i.e. not using data bound content controls. It covers both direct assignment as well as recursive controls. The screenshot is image
  2. SampleRefreshableDocumentGenerator: This sample shows how to generate a refreshable document from a template. The content controls are populated using C# code i.e. not using data bound content controls. This is similar to SampleDocumentGenerator in implementation. Only difference is the generated document can be refreshed in this case. The screenshot is image
  3. SampleDocumentWithTableGenerator: This sample shows how to generate a refreshable document from a template having Table. The content controls are populated using C# code i.e. not using data bound controls. This is similar to to SampleRefreshableDocumentGenerator in implementation. Only difference is the template has an additional table. The screenshot is image
  4. SampleDocumentGeneratorUsingDatabinding: This sample shows how to generate a refreshable document from a template using data bound content controls. This requires that each of the placeholder(template’s content control) has a predefined XPath. The generated document is similar to SampleRefreshableDocumentGenerator.
  5. SampleDocumentWithTableGeneratorUsingDatabinding: This sample shows how to generate a refreshable document from a template having table using data bound content controls. This requires that each of the placeholder(template’s content control) has a predefined XPath. The generated document is similar to SampleDocumentWithTableGenerator.
  6. SampleDocumentGeneratorUsingXml (New): This sample shows how to generate a document from a template using XmlNode as data. This approach shows that a generic generator can be created which requires Xml as data. This requires that XPath for Tag as well as content needs to be provided. The content controls are populated using C# code i.e. not using data bound controls. It covers both direct assignment as well as recursive controls. The generated document is similar to SampleDocumentGenerator.
  7. SampleDocumentGeneratorUsingXmlAndDatabinding (New): This sample shows how to generate a document from a template using XmlNode as data and data bound content controls. This approach shows that a generic generator can be created which requires Xml as data. This requires that XPath for Tag as well as content needs to be provided. It covers both direct assignment as well as recursive controls. The generated document is similar to SampleDocumentGenerator.

Utility to generate Word documents from templates using Visual Studio 2010 and Open Xml 2.0 SDK

[Update: The source code has been updated to Visual Studio 2017 and DocumentFormat.OpenXml 2.8.1You can download the code from GitHub.]

This utility generates Word documents from templates using Content controls. The utility source code is available for download at http://worddocgenerator.codeplex.com/. It has been created in Visual Studio 2010 and uses Open Xml 2.0 SDK which can be downloaded from http://www.microsoft.com/download/en/details.aspx?id=5124.

The next parts in this series are

  • In Part 2 I have discussed about
    • List of functionalities that can be achieved using the utility/source code
    • Description regarding Samples provided with utility
    • New samples added in this update
  • In Part 3 I have explained one of the way to “Refresh the document from within the document(e.g. right click on document and click Refresh) using document-level projects for Word 2007 and Word 2010“

The purpose of creating this utility was to use the Open Xml 2.0 SDK to generate Word documents based on predefined templates using minimum code changes. These documents can either be refreshable or non- refreshable. I’ll explain this difference later. Also there is no dependency that Word should be installed.

A few samples for generating Word 2010 documents have been provided. The screenshots below display the sample template and the document generated out of this template using this utility.

Word 2010 Template –> Generated Document:

3

Word 2010 Template –> Generated Document –> Refreshed Document:

1

 

Document Refresh from within Word –> Refreshed Document:

2

Template Design:

The sample templates are inside “WordDocumentGenerator.Client\Sample Templates” folder. A content control as displayed below can have Title and Tag properties.

clip_image003

The logic is to have a tag property of a content control and then populate data using that placeholder. This means every content control inside a Word template will have a different Tag.

As per image above the tag of the content control is “PlaceholderNonRecursiveB”. During document generation we can assign (not mandatory) a unique Id e.g. Guid of a record to make the tag unique e.g. “PlaceholderNonRecursiveB:Guid”. Let’s say that we have an Id and Name field. Thus the Name will be the content of the control and tag will be “PlaceholderNonRecursiveB:Id”. As per Word 2010 the Tag maximum length is 64.

In code we map the tag to the PlaceHolderType enum.

public enum PlaceHolderType

{

        None = 0,

        Recursive = 1,

        NonRecursive = 2,

        Ignore = 3,

        Container = 4

}

There can be multiple types of PlaceHolders
  • Recursive: This type corresponds to controls where there is 1:N relation between template and data i.e. one example will be repeating a list of Items.
  • Non-Recursive: This type corresponds to controls where there is 1:1 relation between template and data i.e. one example will be showing a User name.
  • Ignore: No action is required for these controls.
  • Container: This type is required only for refreshable documents. We save the container region in CustomXmlPart the first time document is generated from template. Next time onwards we retrieve the container region that was saved and refresh the document. This makes the document self-refreshable.

I’ve named the tags in “Test_Template – 1.docx” as per their PlaceHolder type to make it more clear.

Implementation:

As explained above the Tag property will used to bind data to this content control. The project “WordDocumentGenerator.Library” is the utility library. The project “WordDocumentGenerator.Client” shows how this library can be used to generate documents.

“DocumentGenerator” is the base class that needs to be inherited by the document generators. The sample generators location is “WordDocumentGenerator.Client\Sample Document Generators”.

In order to protect the document I’ve used already created salt and hash. For an implementation where one needs to have document protection enabled for custom passwords one can view http://blogs.msdn.com/b/vsod/archive/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0.aspx

Summary:

The purpose of creating this utility was to use the Open Xml 2.0 SDK to generate Word documents based on predefined templates using minimum code changes. These documents can either be refreshable or non- refreshable. New samples will be added as per feedback.

How to choose between IDisposable, IComponent, Component, MarshalByValueComponent and Control while implementing a Class

In this article I’ll discuss about the criteria behind choosing IDisposable, IComponent, Component, MarshalByValueComponent and base Control classes(System.Windows.Form.Control and System.Web.UI.Control) while implementing a Class.

Prior to discussing further I’d like to share the reason behind this article. I ran code analysis in a project code and got a warning that Dispose was not called for a DataSet in the project’s code. I than called the Dispose method for that particular DataSet and this time code analysis executed with zero errors/warnings. Out of curiosity I thought of verifying the use of calling Dispose on DataSet as even though when Dispose was not being called everything was fine. I found that calling Dispose method did nothing as DataSet is a managed object and does not contain unmanaged resources(if I’m wrong please correct me on this). I could verify this as I was able to access/update the DataSet even after Dispose had been called without any “Object Disposed Exception”. FeatureSchema  is a typed DataSet as displayed in code snippet below

FeatureSchema fs = Proxy.GetFeatureSchema();

fs.Dispose(); 

 

foreach (FeatureSchema.FeatureRow featureRow in fs.Feature.Rows)

{

//Do procesing on dataset

}

 

I went one step further to find the reason behind having Dispose method in DataSet/DataTable and the reason was inheritance as DataSet/DataTable inherits from System.ComponentModel.MarshalByValueComponent class. The reason DataSet/DataTable inherits MarshalByValueComponent class so that it can be designable i.e. used on a design surface. Thus DataSet/DataTable has dispose method even though it doesn’t clean up the resources. DataSet/DataTable call GC.SuppressFinalize in the constructor to prevent the Garbage Collector from calling Object.Finalize on an object that does not require it.
This can be proved by the code snippet displayed below
I have a typed dataset FeatureSchema and I have added a destructor to it. I have excluded the code that was auto generated as I am focusing on finalizers only
 
public partial class FeatureSchema : global::System.Data.DataSet

{

//Added only a destructor to this types dataset. I have excluded all other code

// as I am focusing on finalizers

~FeatureSchema ()

{

 

}

}

 
As displayed below I am loading data to this typed dataset from an .xml file
 
//I am just loading the data into this typed dataset

FeatureSchema schema = new FeatureSchema();

schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);

 
As DataSet calls GC.SuppressFinalize in the constructor Object.Finalize will not be called by the Garbage Collector. This can be verified by placing a break point on the destructor and you will find that it is not getting called.
 
Now I will call GC.ReRegisterForFinalize as displayed in the code snippet below
 
FeatureSchema schema = new FeatureSchema();

GC.ReRegisterForFinalize(schema);

schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);

 
Calling GC.ReRegisterForFinalize will register the object for finalization. I am doing this as I know that DataSet calls GC.SuppressFinalize in the constructor. Now the code will break on the break point which I placed on the destructor at some random time i.e. when GC will execute the “GC Finalizer Thread”.
This verifies the need of calling GC.SuppressFinalize from the constructor of DataSet/DataTable to prevent the garbage collector from requesting finalization.
Even though Dispose in DataSet/DataTable does nothing we should not ignore it and stick to the .Net coding practices otherwise in future versions of Framework things may break. Please do correct me if I am wrong. This concludes the reason behind this article.
 
I’ll continue with the main theme of article. The guidelines for implementers are

 

  • IDisposable:

    If a class uses external resources and will not be used on a design surface IDisposable interface has to be implemented directly or indirectly e.g. System.Drawing.Font class etc.

  • IComponent:

    If a class will be used on a design surface IComponent interface has be implemented directly or indirectly. IComponent implements IDisposable e.g. System.Web.UI.Control class etc.

  • Component:

    If a class will be used on a design surface and is Marshalled by reference then it has to derive from Component class e.g. System.Timers.Timer class etc.

  • MarshalByValueComponent:

    If a class will be used on a design surface and is Marshalled by value then it has to derive from MarshalByValueComponent class e.g. DataSet, DataTable etc.

  • Control:

    If a class will be used on a design surface and provides a user interface then this class is a control and has to derive from System.Windows.Form.Control or System.Web.UI.Control e.g. System.Windows.Forms.Button etc.

The above discussion does not apply to WPF. The WPF Designer architecture is significantly different from the Windows Forms Designer architecture, which is characterized by the IComponent interface and the System.ComponentModel namespace. The WPF Designer architecture retains the TypeConverter and TypeDescriptor classes from the Windows Forms Designer object model. Most other aspects of the WPF Designer architecture are different. For more information please read Comparing the Windows Forms Designer Framework to the WPF Designer Framework

Refrences: http://msdn.microsoft.com/en-us/library/0b1dk63b.aspx

del.icio.us Tags: .NET,.NET Framework,IDisposable,IComponent,Component,MarshalByValueComponent,Control,Windows Forms,ASP.NET,DataSet,DataTable,Garbage Collector,GC