Convert Visual Studio 2005 Code Coverage Results to XML

Saturday, 26 August 2006 07:20 by slanford

One of the great new features in Visual Studio 2005 Team Suite, Team Edition for Developers or Team Edition for Testers is the unit testing and code coverage analysis. One limitation however is that the code coverage results are output to a binary file making it useless for reporting in 3rd party tools. You can export the results to XML from Visual Studio but I needed an easy way to automate the conversion to XML as part of an automated build process using CruiseControl.NET. Having the data in XML makes displaying the results in the CruiseControl.NET dashboard a piece of cake.

After doing a bit of research of I came across this MSDN article that gave me the solution I was after. The trick is to make use of the CoverageInfo class contained in the Microsoft.VisualStudio.Coverage.Analysis.dll assembly to convert the coverage data file to a Dataset which is then easily convertible to XML.

//set symbols/exe paths

CoverageInfoManager.SymPath = SymbolsPath;

CoverageInfoManager.ExePath = ExePath;

 

//load the specified code coverage data file

CoverageInfo cinfo = CoverageInfoManager.CreateInfoFromFile(CoverageDataFile);

 

//Generate a dataset from the code coverage data file

CoverageDS cdata = cinfo.BuildDataSet(null);

From the code sample above you can see that the first thing we need to do is specify the location of the symbol file and exe file. The reason this is necessary is because the coverage file is generated in a subfolder different than the binary and symbol file which are required for conversion. If these two paths aren't set you'll see and error such as this:

Error when creating coverage info: Error loading symbol file. Symbol and binary files should be in the same folder as the coverage file or on the symbol path:

Next we create a CoverageInfo object from our coverage data file, convert it to a CoverageDS Dataset and finally save it to disk as XML.

Complete source code for the console application I created can be found in the attachment below. Note that to compile the project you will need either Visual Studio 2005 Team Suite, Team Edition for Developers or Team Edition for Testers installed. The Microsoft.VisualStudio.Coverage.Analysis.dll assembly referenced by the project is typically located in  C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies.

CodeCoverageConverter.zip (26.66 kb)

Categories:   .NET 2.0
Actions:   E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Implementing Transactional TableAdapters

Monday, 17 July 2006 11:06 by slanford

Almost everyone who uses the new TableAdapter functionality in Visual Studio 2005 (note that I did not write .NET 2.0 as you will find no such thing in the framework) will eventually have the need for transaction support. The basic problem is that TableAdapters do not expose the underlying Command objects, as a result you cannot set their Transaction properties in order to enlist them in a transaction.

Fortunately we can derive our TableAdapters from a custom base class which will allow us to extend the out of the box implementation and provide the functionality we need. The first thing we need to do is define the interface our base class will implement.

public interface IExtendTableAdapter

{

   /// <summary>

   /// Returns the DataAdapter for the TableAdapter

   /// </summary>

   AdapterDecorator DataAdapter { get; }

 

   /// <summary>

   /// Gets or sets the DbConnection for the TableAdapter.

   /// </summary>

   System.Data.IDbConnection DbConnection { get; set;}

}

The purpose of the DataAdapter property is to expose and extend the underlying DataAdapter to include transaction support. The DbConnection property allows us to set the Connection object used by our transaction.

Next we define the AdapterDecorator class which exposes the underlying DataAdapter and provides a method to enlist the TableAdapter in a transaction.

   public class AdapterDecorator

   {

      #region Properties

      private System.Data.Common.DbDataAdapter _adapter;

      /// <summary>

      /// Gets or sets the DataAdapter for the TableAdapter.

      /// </summary>

      public System.Data.Common.DbDataAdapter Adapter

      {

         get { return _adapter; }

         set { _adapter = value; }

      }

 

      private System.Data.Common.DbCommand[] _commands;

      /// <summary>

      /// Gets or sets the non-DataAdapter Command objects.

      /// </summary>

      public System.Data.Common.DbCommand[] Commands

      {

         get { return _commands; }

         set { _commands = value; }

      }

      #endregion Properties

 

      #region Constructors

      public AdapterDecorator(System.Data.Common.DbDataAdapter adapter)

      {

         _adapter = adapter;

      }

 

      public AdapterDecorator(System.Data.Common.DbDataAdapter adapter, System.Data.Common.DbCommand[] commands)

      {

         _adapter = adapter;

         _commands = commands;

      }

      #endregion Constructors

 

      #region Public Methods

      public void EnlistTransaction(System.Data.Common.DbTransaction transaction)

      {

         //set Adapter Command object transactions

         if (_adapter != null)

         {

            if (_adapter.InsertCommand != null)

            {

               _adapter.InsertCommand.Connection = transaction.Connection;

               _adapter.InsertCommand.Transaction = transaction;

            }

            if (_adapter.UpdateCommand != null)

            {

               _adapter.UpdateCommand.Connection = transaction.Connection;

               _adapter.UpdateCommand.Transaction = transaction;

            }

            if (_adapter.DeleteCommand != null)

            {

               _adapter.DeleteCommand.Connection = transaction.Connection;

               _adapter.DeleteCommand.Transaction = transaction;

            }

         }

 

         //set non-Adapter Command object transactions

         if (_commands != null)

         {

            for (int i = 0; i < this._commands.Length; i++)

            {

               _commands[i].Connection = transaction.Connection;

               _commands[i].Transaction = transaction;

            }

         }

      }

      #endregion Public Methods

   }

Now we define the TableAdapterBase class which will be the base class from which our TableAdapters will be derived.

public abstract class TableAdapterBase : System.ComponentModel.Component, IExtendTableAdapter

   {

      #region Private Fields

      AdapterDecorator adapterDecorator;  //The extended DataAdapter

      IDbConnection connection;           //The database connection

      #endregion Private Fields

 

      #region IExtendTableAdapter Members

 

      public AdapterDecorator DataAdapter

      {

         get

         {

            //========================================================================================

            // Use Reflection to get the private Adapter and CommandCollection properties defined by

            // the Designer generated code.

            //========================================================================================

 

            if (this.adapterDecorator == null)

            {

               System.Data.Common.DbDataAdapter adapter = null;

               System.Data.Common.DbCommand[] commands = null;

 

               Type t = this.GetType();

               //If parent type is not TableAdapterBase then we need to reflect the properties

               //from the parent type. Otherwise the "Adapter" property will not be found since

               //it is private.

               Type parentT = t.BaseType;

               if (!(parentT.FullName == "TransactionalTableAdapters.TableAdapterBase"))

                  t = parentT;

 

               //Get the Adapter property defined by the TableAdapter's designer generated code

               BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

               PropertyInfo pi = t.GetProperty("Adapter", bf);

               if (pi != null)

                  adapter = pi.GetValue(this, null) as System.Data.Common.DbDataAdapter;

 

               //Get the CommandCollection property defined by the TableAdapter's designer generated code

               pi = t.GetProperty("CommandCollection", bf);

               if (pi != null)

                  commands = pi.GetValue(this, null) as System.Data.Common.DbCommand[];

 

               //Create the AdapterDecorator object

               this.adapterDecorator = new AdapterDecorator(adapter, commands);

            }

 

            return this.adapterDecorator;

         }

      }

 

      public IDbConnection DbConnection

      {

         get

         {

            //=================================================================

            // Use Reflection to get the Connection property defined by the

            // Designer generated code.

            //=================================================================

 

            if (this.connection == null)

            {

               Type t = this.GetType();

               BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

 

               //Get the Connection property.

               PropertyInfo pi = t.GetProperty("Connection", bf);

               if (pi != null)

                  connection = pi.GetValue(this, null) as IDbConnection;

            }

 

            return this.connection;

         }

         set

         {

            this.connection = value;

         }

      }

 

      #endregion

   }

Finally, we define a TransactionHelper class to make it easy to enlist our TableAdapters in a transaction.

public sealed class TransactionHelper : IDisposable

   {

      #region Private Fields

      DbConnection _connection;     //The database connection

      DbTransaction _tran = null;         //The database transaction

      #endregion Private Fields

 

      #region Public Methods

      /// <summary>

      /// Enlists the specified table adapter in the current transaction.

      /// </summary>

      /// <param name="tableAdapter">The table adapter to enlist. The table adapter must implement <see cref="IExtendTableAdapter"/>.</param>

      /// <remarks>

      /// </remarks>

      public void EnlistParticipant(System.ComponentModel.Component tableAdapter)

      {

         //Make sure the TableAdapter implements IExtendTableAdapter which is required for transaction support

         if (!(tableAdapter is IExtendTableAdapter))

            throw new Exception("Type " + tableAdapter.GetType().Name + " is invalid because it does not implement IExtendTableAdapter.");

 

         IExtendTableAdapter ta = tableAdapter as IExtendTableAdapter;

 

         //We need to share the same connection across TableAdapters for them to participate in the same

         //transaction so get connection from the first TableAdapter we encounter and use it for all

         //others.

         if (_connection == null)

         {

            _connection = ta.DbConnection as DbConnection;  //get the connection

            _connection.Open();                                         //open the connection

            _tran = _connection.BeginTransaction();               //create a transaction

         }

         else //we already have our connection so change the connection of the TableAdapter to use it

         {

            ta.DbConnection = _connection;

         }

 

         //Enslist the TableAdapter in the transaction

         ta.DataAdapter.EnlistTransaction(_tran);

      }

 

      /// <summary>

      /// Completes the operations and commits the transaction.

      /// </summary>

      public void Complete()

      {

         //Commit and dispose the transaction

         if (_tran != null)

         {

            _tran.Commit();

            _tran.Dispose();

            _tran = null;

         }

      }

      #endregion Public Methods

 

      #region IDisposable Members

 

      /// <summary>

      /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

      /// </summary>

      public void Dispose()

      {

         //Rollback and dispose transaction if still present

         if (_tran != null)

         {

            _tran.Rollback();

            _tran.Dispose();

         }

 

         //close the connection

         if ((_connection != null) && (_connection.State != ConnectionState.Closed))

            _connection.Close();

      }

 

      #endregion

   }

This class is used very much like the TransactionScope class with the exception that we must explicitly enlist our TableAdapters in the transaction by calling the EnlistParticipant() method. For example:

using (TransactionHelper th = new TransactionHelper())

{

   //enlist TableAdapters in our transaction

   NorthwindDataSetTableAdapters.OrdersTableAdapter ordersTA =

      new TransactionalTableAdapters.NorthwindDataSetTableAdapters.OrdersTableAdapter();

   th.EnlistParticipant(ordersTA);

   NorthwindDataSetTableAdapters.Order_DetailsTableAdapter detailsTA =

      new TransactionalTableAdapters.NorthwindDataSetTableAdapters.Order_DetailsTableAdapter();

   th.EnlistParticipant(detailsTA);

 

   //Update the Orders table

   ordersTA.Update(ordersRow);

   //Get the order id

   int orderID = ordersRow.OrderID;

                       

   //Insert Order_Details row

   detailsTA.Insert(orderID, 1, Convert.ToDecimal(3.39), 10, 0f);

 

   //Commit the transaction

   th.Complete();

}

Complete source code for this post can be found in the attachment below.

TransactionalTableAdapters.zip (29.29 kb)

Categories:   .NET 2.0
Actions:   E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed