2LeggedSpider

Container-Managed Persistence

Posted in ASP.NET, C# by Sumit Thomas on February 28, 2006

[tweetmeme style=”compact”]The most fascinating part in developing ASP.NET applications for me is how you can leverage different design models relatively easily, compared to other platforms. Coming from a ASP background, I was excited when I created my first 3-tier application in ASP.NET. I’ve been interested in architectures ever since. The n-tier architecture adopted in most of the Microsoft’s sample applications such as IBuySpy, is one of the commonly used models and I’ve used it successfully in my applications. It is what we call a Component-Managed Persistence model, where the component needs to know how to make calls to the persistence layer such as SQL Server and also what are the parameters required for a transaction with the persistence layer. So in a sense the business layer is tightly coupled with the under lying persistence layer.

I came across this book “ASP.NET E-commerce Programming: Problem – Design – Solution” by Kevin Hoffman a long time back. Though I bought the book, i really didn’t understand the Container-Managed Persistence model described in the book. I had tough time trying to download the code associated with the book as the book was no longer supported on www.wrox.com. Anyway, I managed to download the code after several hours of surfing and googling the web. I spent a lot od time trying to understand how the architecture works and I finally managed to understand how brilliant it is. I was however surprised to find that there ain’t much reviews or talk about this book anywhere. Anyway, I would like to thank Kevin Hoffman for this wonderful book.

So what is Container-Managed Persistence?

Quoting from the book: “Container-Managed Persistence is a design pattern whereby business objects have no direct knowledge of where their data came from and how it will be persisted” The business objects are pure business objects and are never tighly coupled with any persistence layer.

After trying several sample applications with the CMPServices library and the associated libraries that formed the part of the architecture, there was one issue that I wasn’t comfortable with in the CMPServices library. The PersistableObjectSet.cs class uses an internal DataSet for handling the resultset from the database. Now, I being a strong advocate of using DataReaders in ASP.NET application was not really comfortable with it. So, I decided to try my hands on tweaking the CMPServices library to make DataReader as the default data handling object and also provide the developer with an option to choose the data object – DataSet, DataTable or DataReader he/she wants to use in the application.

I made few additions to the CMPServices library to achieve what I said. Now to start off I created an Enum CMPDataObjectType as follows

public enum CMPDataObjectType
	{
		/// 
		/// DataSet
		/// 
		DataSet = 1,
		/// 
		/// DataTable
		/// 
		DataTable = 2,
		/// 
		/// DataReader
		/// 
		DataReader = 3
	}

The enum has three fields, representing the Data object types that we are going to support in the CMPServices library. The actual PerisistableObjectSet class in the CMPServices library is as follows

public class PersistableObjectSet : PersistableObject
	{
		protected DataSet internalData;

		public PersistableObjectSet()
		{
			internalData = new DataSet();	
		}

		public virtual void FinalizeData()
		{
			PersistableObjectSet.FinalizeData( internalData );
		}

		public static void FinalizeData(DataSet scratchData )
		{

		}

		public DataSet ResultSet
		{
			get 
			{
				return internalData;
			}
			set 
			{
				internalData = value;
			}
		}
	}

As you can see it has an internal DataSet which is used to hold the records returned from the database. Now we need to add support for DataReader and DataTable in this class. So I modified the class as follows

public class PersistableObjectSet : PersistableObject
	{

		/// 
		/// The internal DataSet of the PersistableObjectSet
		/// 
		protected DataSet objectDataSet;
		/// 
		/// The internal DataReader of the PersistableObjectSet
		/// 
		protected IDataReader objectDataReader;
		/// 
		/// The internal DataTable of the PersistableObjectSet
		/// 
		protected DataTable objectDataTable;

		/// 
		/// CMPDataObjectType
		/// 
		protected CMPDataObjectType cmpDataObjectType;

		/// 
		/// Default constructor
		/// 
		public PersistableObjectSet()
		{
			
		}

		/// 
		/// Gets/Sets the value of the internal DataSet of the PersistableObjectSet
		/// 
		public DataSet DataSet
		{
			get
			{
				return objectDataSet;
			}
			set
			{
				objectDataSet = value;
			}
		}

		/// 
		/// Gets/Sets the value of the internal DataReader of the PersistableObjectSet
		/// 
		public IDataReader DataReader
		{
			get
			{
				return objectDataReader;
			}
			set
			{
				objectDataReader = value;
			}
		}


		/// 
		/// Gets/Sets the value of the internal DataTable of the PersistableObjectSet
		/// 
		public DataTable DataTable
		{
			get
			{
				return objectDataTable;
			}
			set
			{
				objectDataTable = value;
			}
		}

		/// 
		/// The CMPDataObjectType refers to the Datatype that will be used for Data retrieval.
		/// There are 3 CMPDataObjectTypes - DataSet, DataTable and DataReader.
		/// DataReader is the default one and it is recommended to use it for any kind of data retrieval.
		/// DataTable and DataSet must be used only for special data maniuplations.
		/// DataSet should be used only for special requirements like serialization/deserialization, caching etc.
		/// 
		public CMPDataObjectType DataObjectType
		{
			get
			{
				if( cmpDataObjectType == 0 )
				{
					cmpDataObjectType = CMPDataObjectType.DataReader;
				}
				return cmpDataObjectType;
			}
			set
			{
				cmpDataObjectType = value;
			}
		}

		/// 
		/// This method provides a format for child classes to implement data manipulation methods.
		/// If any additional work needs to be done on the retrieved data, this method can be 
		/// implemented as a standard place for data finalisation to take place.
		/// 
		/// If you are using a DataReader you SHOULD call this method in your business logic to
		/// close the Connection object.
		/// 
		public virtual void FinaliseData()
		{
			if( cmpDataObjectType == CMPDataObjectType.DataSet )
			{
				PersistableObjectSet.FinaliseData( objectDataSet );
			}
			else if( cmpDataObjectType == CMPDataObjectType.DataReader )
			{
				PersistableObjectSet.FinaliseData( objectDataReader );
			}
			else if( cmpDataObjectType == CMPDataObjectType.DataTable )
			{
				PersistableObjectSet.FinaliseData( objectDataTable );
			}
		}

		/// 
		/// This method is called by the FinaliseData() overload in the business logic to do
		/// any additional work with the DataSet. 
		/// 
		/// DataSet
		public static void FinaliseData( DataSet tempData )
		{
			tempData.Dispose();
		}

		/// 
		/// This method is called by the FinaliseData() overload in the business logic to do
		/// any additional work with the DataTable. 
		/// 
		/// DataTable
		public static void FinaliseData( DataTable tempData )
		{
			tempData.Dispose();
		}

		/// 
		/// This method is called by the FinaliseData() overload in the business logic to do
		/// any additional work with the DataReader. 
		/// 
		/// IDataReader/SqlDataReader
		public static void FinaliseData( IDataReader tempData )
		{
			tempData.Close();
			tempData.Dispose();
		}

	}

So basically I have added a property DataObjectType which is used to set the type of data object we are going to use. By default it is DataReader. Each data types will have their own FinaliseData method. For the DataReader we make sure that the object is closed in order to close its connection object.

We also need to do couple of changes to the SqlPersistenceContainer class, as we now have three types of data object types to handle.

Since we use the data object only while retrieving the data, we modify the Select method as follows

		/// 
		/// This method performs the Select operation. It executes a stored procedure and places any return or output values
		/// back onto the instance of the PersistableObject that was provided. If the object instance can be cast to a PersistableObjectSet, then the
		/// container will attempt to assign the output or return value to a Data object within the PersistableObjectSet.
		/// 
		/// Persistable object
		public override void Select( PersistableObject selectObject )
		{
			try
			{
				CommandMapping commandMap	= containerMap.SelectCommand;
				SqlCommand selectCommand	= BuildCommandFromMapping( commandMap );
				AssignValuesToParameters( commandMap, ref selectCommand, selectObject );
				if(selectCommand.Connection.State == ConnectionState.Closed)
					selectCommand.Connection.Open();

				if( selectObject is PersistableObjectSet )
				{
					PersistableObjectSet objectSet = (PersistableObjectSet)selectObject;
					
					AssignResultSetToObjectSet( commandMap, selectCommand, ref selectObject );
					AssignOutputValuesToInstance( commandMap, selectCommand, ref selectObject );
					//selectCommand.Connection.Close();
				}
				else
				{
					selectCommand.ExecuteNonQuery();
					selectCommand.Connection.Close();
					AssignOutputValuesToInstance( commandMap, selectCommand, ref selectObject );
				}
				//selectCommand.Connection.Dispose();
				//selectCommand.Dispose();
			}
			catch (Exception dbException)
			{
				throw new Exception("Persistance (Select) Failed for PersistableObject", dbException );
			}
		}

We have remaned AssignResultSetToDataSet to AssignResultSetToObjectSet to give it a more generic name. The AssignResultSetToObjectSet method is as follows

private void AssignResultSetToObjectSet( CommandMapping commandMap, SqlCommand sqlCommand, ref PersistableObject persistObject )
		{
			SqlDataAdapter sqlDa = null;
			PersistableObjectSet objectSet = (PersistableObjectSet)persistObject;
			if( objectSet.DataObjectType == CMPDataObjectType.DataSet )
			{
				sqlDa = new SqlDataAdapter( sqlCommand );
				objectSet.DataSet = new DataSet();
				sqlDa.Fill ( objectSet.DataSet );
				sqlCommand.Connection.Close();
				sqlCommand.Connection.Dispose();
				sqlCommand.Dispose();
			}
			else if( objectSet.DataObjectType == CMPDataObjectType.DataReader )
			{
				SqlDataReader objReader = sqlCommand.ExecuteReader( CommandBehavior.CloseConnection );
				objectSet.DataReader = objReader;
			}
			else if( objectSet.DataObjectType == CMPDataObjectType.DataTable )
			{
				sqlDa = new SqlDataAdapter( sqlCommand );
				objectSet.DataTable = new DataTable();
				sqlDa.Fill ( objectSet.DataTable );
				sqlCommand.Connection.Close();
				sqlCommand.Connection.Dispose();
				sqlCommand.Dispose();
			}
		}

We create the instance of the select data object type and fetch the data using it. So, thats all the changes we do to the CMPServices library.

Now lets see how we use it. I created a simple contacts management application and here is a sample of a function in the Business layer. Since I am sure I am not going to change the underlying datasource, I populate the data to a strongly-typed collection object which I will then use in my presentation layer. I have a function called GetContacts and I have three versions of the function to support the data object type under consideration.

For DataReader (default)

public static Contacts GetContacts()
            {
                  SqlPersistenceContainer spc = new SqlPersistenceContainer( CMPConfigurationHandler.ContainerMaps["SherstonContacts"] );
                  ContactSet contactSet         = new ContactSet();

                  spc.Select( contactSet );

                  Contacts contactList         = new Contacts();

                  while( contactSet.DataReader.Read() )
                  {
                        IDataReader row               = contactSet.DataReader;
                        Contact contact               = new Contact();
                        contact.ContactId             = Convert.ToInt32( row["ContactId"] );
                        contact.ContactName           = row["ContactName"].ToString();
                        contact.Email                 = row["Email"].ToString();

                        contactList.Add( contact );
                  }

                  ///It is imperative that you call the FinaliseData method for DataReader as this will
                  ///automatically close it’s connection object. Note that it is called after the DataReader's values
                  ///are retrieved as DataReader's require the connection object to be open unlike DataSet/DataTable.

                  contactSet.FinaliseData();
                  return contactList;
            }

For DataSet

	    public static Contacts GetContacts()
            {
                  SqlPersistenceContainer spc = new SqlPersistenceContainer( CMPConfigurationHandler.ContainerMaps["SherstonContacts"] );
                  ContactSet contactSet         = new ContactSet();

                  //Setting the DataObjectType of the contactSet object to DataSet
                  contactSet.DataObjectType     = CMPDataObjectType.DataSet;

                  spc.Select( contactSet );

                  Contacts contactList         = new Contacts();

                  ///For DataSet and DataTable the FinaliseData method should be called before extracting the values
                  ///as the manipulation with the data would have occured if there is an overriden FinaliseData method 
                  ///in the ContactSet class

                  contactSet.FinaliseData();

                  foreach( DataRow row in contactSet.DataSet.Tables[0].Rows )
                  {
                        Contact contact               = new Contact();
                        contact.ContactId             = Convert.ToInt32( row["ContactId"] );
                        contact.ContactName           = row["ContactName"].ToString();
                        contact.Email                 = row["Email"].ToString();

	                contactList.Add( contact );
                  }

                  return contactList;
            }

For DataTable

	  public static Contacts GetContacts()
         {
                 SqlPersistenceContainer spc = new SqlPersistenceContainer( CMPConfigurationHandler.ContainerMaps["SherstonContacts"] );
                  ContactSet contactSet         = new ContactSet();

                  //Setting the DataObjectType of the contactSet object to DataTable
                  contactSet.DataObjectType     = CMPDataObjectType.DataTable;

                  spc.Select( contactSet );

                  Contacts contactList         = new Contacts();


                  ///For DataSet and DataTable the FinaliseData method should be called before extracting the values
                  ///as the manipulation with the data would have occured if there is an overriden FinaliseData method 
                  ///in the ContactSet class

                  contactSet.FinaliseData();

                  foreach( DataRow row in contactSet.DataTable.Rows )
                  {
                        Contact contact               = new Contact();
                        contact.ContactId             = Convert.ToInt32( row["ContactId"] );
                        contact.ContactName           = row["ContactName"].ToString();
                        contact.Email                 = row["Email"].ToString();

                        contactList.Add( contact );
                  }

                  return contactList;

            }

So here it is a modified CMPServices library with support for DataReader and DataTable 🙂

Technorati: , ,

Strongly-typed collections

Posted in C# by Sumit Thomas on May 23, 2005

I use DataReader as the data access object in my ASP.NET applications for obvious reasons. To improve the performance of my applications, I also need to utilise the data caching feature in .NET. DataReader cannot be cached, hence I create my custom class with public properties resembling the structure of the table from which the data was fetched, create an instance of this custom class, assign the values from the DataReader to the instance and then add the instance to a Collection object, which can be later cached.

There are many useful Collection classes such as ArrayList, SortedList, Queue, Stack and Hashtable available in System.Collections namespace. These are out-of-the-box classes that you can use for your data manipulation. More information on the .NET collection classes can be found here.

ArrayList is one of the commonly used collection types in .NET and that is attributed to its flexibility in storing data and also its useful methods to manipulate the data. But it also has its own implications. Since ArrayList is implemented internally as an Array of Object type, it is resource intensive as it has to box the data added to it at runtime and also we might receive unexpected exceptions while unboxing the data within the ArrayList. For instance, lets check this code…

ArrayList list = new ArrayList();
list.Add("string value");
list.Add(1);
list.Add(true);

As you can see, we have created an object of type ArrayList and added three items of different data types, namely a string, integer and boolean to it. Now, lets try to retrieve the values from the ArrayList and cast them to string data type, as follows..

for( int i=0; i < list.Count; i++ )
{
	Console.WriteLine( (string)list[i] );
}

Now when we run the above code, we can expect an error when i=1 as we cannot directly cast an integer to string. To avoid this problem we can write our own strongly-typed collection. In a strongly-type collection, we can store data only of a particular type and there won’t be any need to unbox it as we know the type of data residing in the collection. To create a strongly-type collection we can create a custom class that inherits from the CollectionBase abstract class present in the System.Collections namespace. The CollectionBase class implements three interfaces namely IList, IEnumerable and ICollection available in the same System.Collections namespace.

So lets get started. We are going to create a custom collection called Staffs which can hold data of type Staff. Here is the code for Staff.cs

public class Staff
{
	private int staffId;
	private string firstName;
	private string lastName;
	private string department;

	public Staff(int _staffId, string _firstName, string _lastName, string _department)
	{
		staffId = _staffId;
		firstName	 = _firstName;
		lastName	 = _lastName;
		department = _department;
	}

	public int StaffId
	{
		get
		{
			return staffId;
		}
		set
		{
			staffId = value;
		}
	}

	public string FirstName
	{
		get
		{
			return firstName;
		}
		set
		{
			firstName = value;
		}
	}

	public string LastName
	{
		get
		{
			return lastName;
		}
		set
		{
			lastName = value;
		}
	}

	public string Department
	{
		get
		{
			return department;
		}
		set
		{
			department = value;
		}
	}

}//end of class

It is a simple class with four public properties.

Now, we have to create our stongly-type collection which can hold a data of type Staff. Here is the code for Staffs.cs

	public class Staffs : CollectionBase
	{
		public Staffs()
		{
			
		}

		//An indexer of type Staff
		public Staff this[int index]
		{
			get
			{
				return (Staff)List[index];
			}
			set
			{
				List[index] = value;
			}
		}

		//Add object of type Staff to the List
		public int Add( Staff staff )
		{
			return List.Add( staff );
		}
	}

There are many other overridable methods within the CollectionBase class which you can explore.

Lets write a console application to try our strongly-typed collection

		[STAThread]
		static void Main(string[] args)
		{
			Staffs staffs = new Staffs();
			staffs.Add( new Staff(1, "Steven", "Tyler", "Rock" ) );
			staffs.Add( new Staff(2, "Michael", "Jackson", "Pop" ) );
			staffs.Add( new Staff(3, "Marshall", "Mathers", "Rap" ) );
			staffs.Add( new Staff(4, "George", "Bush", "Crap" ) );

			foreach(Staff staff in staffs)
			{
				Console.WriteLine( staff.FirstName + " " + staff.LastName + " is a " + staff.Department + " artist" );
				Console.ReadLine();
			}
		}

If we can ignore the staff data that we entered and focus on collection object ;), we find that it is used similarly to an ArrayList and we don’t have to worry about unboxing the data, as we know it has only objects of type Staff in it.

Eventhough there are several advantages in using strongly-type collection, we have to create a collection for each custom class which might greatly increase the amount of code we have to write. This will however be overcomed with the release of Generics in .NET 2.0

Technorati:
Tagged with: ,

Generating a dynamic bar graph using ASP.NET and C#

Posted in ASP.NET, C# by Sumit Thomas on November 21, 2004

[tweetmeme style=”compact”]Back in the days of ASP, web developers had to rely on third party components to dynamically create images on a web page. In ASP, we would use colored tables to display a bar graph. But wouldn’t it be great if we could generate a dynamic graph by ourselves instead of relying on some third-party components. Well, the .NET framework allows us to do just that and more.

Consider a scenario where we had to display a graph representing the sales of a company from say 2000-2004. We need to generate a dynamic bar graph using the data provided to us. Let me take you through the step-by-step process to accomplish the task.

I am using Notepad and .NET framework 1.0. Why notepad? Well, I believe it’s the best way to learn .NET or for that matter any programming language and also the code is very clean.

The two classes, which we need to generate images on the fly, are System.Drawing.Bitmap and System.Drawing.Graphics. The Bitmap class is used to represent an instance of an image and the Graphics class can be used to draw lines, curves and other geometric shapes.

Be sure to check about these two classes and their methods from http://msdn.microsoft.com

So lets get started. Open Notepad and type the following two lines



We will be using these two namespaces in our application.

The method we will write to generate the graph is called ‘GenerateBarGraph’

The initial structure of the code will look like this


Bar graph generated using ASP.NET and C#


protected void GenerateBarGraph(
string graphTitle,
ArrayList xValues,
ArrayList yValues,
int barWidth,
int barSpaceWidth,
int graphHeight)
{
//Our code to generate bar graph will come here
}
private void Page_Load(object sender, System.EventArgs e)
{

}


<img src="" />


The parameters passed to the GenerateBarGraph method are as follows

graphTitle : The title of the graph
xValues : The values in the x-axis
yValues : The values in the y-axis
barWidth : The width of each bar
barSpaceWidth : The space between each bars
graphHeight : The height of the graph(excluding the title and x-values)

Change the GenerateBarGraph method as follows

protected void GenerateBarGraph(
string graphTitle,
ArrayList xValues,
ArrayList yValues,
int barWidth,
int barSpaceWidth,
int graphHeight)
{
int graphTitleHeight=20; // Height in pixels utilized by the title in the graph
int itemsHeight=35; // Height in pixels utilized by the items in the x-axis

/*
The Graph’s width is calculated by adding the width of a bar and the space between
two bars multiplied by the total values in the x-axis plus the space between two bars
*/
int graphWidth= (barWidth + barSpaceWidth) * xValues.Count + barSpaceWidth;

/*
The maximum height that a bar can attain needs to be found from the y-values passed
as parameter
*/
int maxBarHeight=0;

//Total height of the image is calculated
int totalGraphHeight = graphHeight + graphTitleHeight + itemsHeight;

//Create an instance of Bitmap class with the given width and height
Bitmap barBitmap=new Bitmap(graphWidth, totalGraphHeight);

/*
Graphics class does not have a constructor and hence we call its static method
FromImage and pass the Bitmap object to it
*/
Graphics barGraphics= Graphics.FromImage(barBitmap);

/*
Using the Graphics object we fill the image of given dimensions with light gray color
*/

barGraphics.FillRectangle(
new SolidBrush(Color.WhiteSmoke),
0,
0,
graphWidth,
totalGraphHeight);

/*
We create an instance of Font class available in System.Drawing. We will be using this
to display the title of the graph.
*/
Font titleFont=new Font("Verdana",14, FontStyle.Bold);

/*
Use the Graphics object’s DrawString method to draw the title at the specified location
*/
barGraphics.DrawString(
graphTitle,
titleFont,
new SolidBrush(Color.Red),
(graphWidth / 2) - graphTitle.Length * 5,
totalGraphHeight - itemsHeight);


//////////////Code to generate bars will come here/////////////////

/*
Save the image to the web server’s D: drive. We use the PNG format to make it look
crisp.
*/
barBitmap.Save("D:\\bargraph.png",ImageFormat.Png);

//Dispose off the Graphics and Bitmap objects
barGraphics.Dispose();
barBitmap.Dispose();
}

Change the Page_Load method as follows

private void Page_Load(object sender, System.EventArgs e)
{
ArrayList _years=new ArrayList();
_years.Add("2000");
_years.Add("2001");
_years.Add("2002");
_years.Add("2003");
_years.Add("2004");

ArrayList _sales=new ArrayList();
_sales.Add(270500);
_sales.Add(211930);
_sales.Add(223300);
_sales.Add(343000);
_sales.Add(424750);

GenerateBarGraph("ABC Ltd. Sales (2000-2004)",_years, _sales, 50, 25, 400);
}

Change the source of the IMG tag used in our aspx page as follows

<img />

Now, if you run this page you will see an image displayed on the browser with the title “ABC Ltd. Sales (2000-2004)” displayed in red at the bottom

If you notice in the Page_Load method, we have passed two ArrayLists to the GenerateBarGraph method and also the other previously mentioned parameters. We will be using these two ArrayLists to display the bars.

Now replace //////////////Code to generate bars will come here///////////////// in GenerateBarGraph method with the following code.

/*
Find the highest value in the yValues ArrayList and set it as the maximum height of the bar
*/
foreach(int _value in yValues)
if(_value > maxBarHeight) maxBarHeight=_value;

//barXPos will store the x position of a bar
int barXPos = barSpaceWidth;
int barHeight;

Font itemsFont=new Font("Verdana",9, FontStyle.Bold);
Font valuesFont=new Font("Verdana", 7, FontStyle.Italic);

Random rnd=new Random();

for(int i=0;i  Gives the bar height in percentage with respect to
the maximum bar height set by us

((((int)yValues[i]* 100 / maxBarHeight) )* graphHeight)/100 will give the bar height in
pixels
*/
barHeight=((((int)yValues[i]* 100 / maxBarHeight) )* graphHeight)/100;

//Draw the bar with the set brush, x and y positions, width and height
barGraphics.FillRectangle(
barBrush,
barXPos,
graphHeight-barHeight,
barWidth,
barHeight);

//Draw the x-value along the x-axis
barGraphics.DrawString(
xValues[i].ToString(),
itemsFont,
barBrush,
barXPos,
graphHeight);

//Draw the respective y value on top of the bar
barGraphics.DrawString(
yValues[i].ToString(),
valuesFont,
barBrush,
barXPos,
(graphHeight-barHeight)-itemsHeight);

//Change the x position of the next bar
barXPos += (barWidth + barSpaceWidth);
}

If you now run the aspx page you will notice the bar graph representing the sales of a company from 2000-2004. If you refresh the page you will see that each bar will have a new color.

The Graphics API provided by the .NET framework is very extensive and what we have seen gives us a useful startup. Do take time to understand the classes and its methods used in this article.

Technorati: , ,
Tagged with: , ,

Creating a Word document using ASP.NET and C#

Posted in ASP.NET, C# by Sumit Thomas on June 30, 2004

[tweetmeme style=”compact”]In one of my projects recently, I had to create word documents dynamically using ASP.NET. I turned to MSDN for help and was surprised to find how easy it was to do so.

I am using Visual Studio 2002 and .NET Framework 1.0. Therefore if you are using the latest versions, unlike me 😦 then do check out for the updates in the classes and methods that I am going to use in this article.

1) OK, lets start. Open up Visual Studio and create an ASP.NET Web application using C#.

2) Now we have to add a COM reference to our project. In the references dialog box, under COM you will find Microsoft Word 9.0 Object Library. Select it and add it to the project. This will add few COM dll’s in the bin directory.

3) Now the code…

object fileName="D:\\MyWord.doc";
object novalue=System.Reflection.Missing.Value;

Word.ApplicationClass objWord = new Word.ApplicationClass();
Word.Document objWordDoc=objWord.Documents.Add(ref novalue, ref novalue, ref novalue, ref novalue);

objWordDoc.Activate();

objWord.Selection.TypeText(".NET Rocks!!!");
objWord.Selection.TypeParagraph();
objWord.Selection.TypeText("Savage loves .NET");

objWordDoc.SaveAs(ref fileName, ref novalue, ref novalue, ref novalue,
            ref novalue, ref novalue, ref novalue, ref novalue, ref novalue,
            ref novalue, ref novalue); 

objWord.Quit(ref novalue, ref novalue, ref novalue);

Now check the MyWord.doc file in the D: drive.

The code is pretty simple and intuitive, hence a quick explanation.

* We are first creating a object of Word.ApplicationClass
* Next we add a Word document to this object. Incase you don’t want to pass a particular parameter, use the ‘novalue’ object that we have created in the code.
* The following steps are pretty self explanatory.

The Visual Studios’ Intellisense feature will let us know of the expected parameters of each method. In the latest versions of .NET framework the number of parameters required is more.

Do check out http://msdn.microsoft.comto learn about automation in .NET

Technorati: , ,
Tagged with: , , , ,