Quantcast
Channel: Open XML Format SDK forum
Viewing all articles
Browse latest Browse all 1288

Programmatic Merge of SQL Data to a Word Template

$
0
0

I have a client that wants to be able to produce a Customer Profile report based on data that resides in a SQL 2008 R2 db.  They want to choose a customer from a drop-down list in a web page and have it spit out a rather extensive report on that customer.

I was going to use SSRS, but they wanted something that was not only functional but esthetically pleasing, so I'm merging data into a Word document using OpenXML.

Things have been going swimmingly, but I now have a request that I'm not sure how (or even if it is possible) to address with this.

The approach I've used so far is one of place-holders in a Word doc.  You add a control from the Developer Tab, you name it, you wire it up to your XML and you populate it in your CSharp app.  Works like a champ.

Now they want a kind of repeatable section thing.  Let's say that a customer has open projects.  For each of these projects, they would like to see the same set of data elements.  So conceptually, I need a template of place holders that I can populate and then merge into the document.

 

Here's some code:

 

privatevoid CreateDocument( )
		{// Get the template document file and create a stream from itconststring DocumentFile = @"~/App_Data/CustomerProfileTemplate.docx";//			const string DocumentFile = @"~/App_Data/OneOff.docx";// Read the file into memorybyte[ ] buffer = File.ReadAllBytes( Server.MapPath( DocumentFile ) );//			MemoryStream memoryStream = new MemoryStream( buffer, true ); // NoNoNo!!! MemoryStreams created this way cannot expand dynamically!
			MemoryStream memoryStream = new MemoryStream( );

			memoryStream.Capacity = buffer.Length;
			memoryStream.Write( buffer, 0, buffer.Length );
			buffer = null;// Open the document in the stream and replace the custom XML partusing ( Package pkgFile = Package.Open( memoryStream, FileMode.Open, FileAccess.ReadWrite ) )
			{
				PackageRelationshipCollection pkgrcOfficeDocument = pkgFile.GetRelationshipsByType( strRelRoot );foreach ( PackageRelationship pkgr in pkgrcOfficeDocument )
				{if ( pkgr.SourceUri.OriginalString == "/" )
					{// Get the root part
						PackagePart pkgpRoot = pkgFile.GetPart( new Uri( "/" + pkgr.TargetUri.ToString( ), UriKind.Relative ) );// Add a custom XML part to the package
						Uri uriData = new Uri( "/customXML/item1.xml", UriKind.Relative );if ( pkgFile.PartExists( uriData ) )
						{// Delete document "/customXML/item1.xml" part
							pkgFile.DeletePart( uriData );
						}// Load the custom XML data
						PackagePart pkgprtData = pkgFile.CreatePart( uriData, "application/xml" );
						GetDataFromSQLServer( pkgprtData.GetStream( ), ddlCustomer.SelectedValue );
					}
				}// Close the file - not needed thanks to "Using"//			pkgFile.Close( );
			}//Build File Namestring fileName = ddlCustomer.SelectedItem.ToString( ) + " Profile.docx";// Return the result
			Response.ClearContent( );
			Response.ClearHeaders( );
			Response.AddHeader( "content-disposition", "attachment; filename=" + fileName );
			Response.ContentEncoding = System.Text.Encoding.UTF8;

			memoryStream.WriteTo( Response.OutputStream );

			memoryStream.Close( );

			Response.End( );
		}

The function GetDataFromSQLServer( ) is where I get the data and drop it into the doc and looks something like this:

privatevoid GetDataFromSQLServer( Stream stream, string customerID = "" )
		{// Connect to a Microsoft SQL Server database and get data
			String source = System.Configuration.ConfigurationManager.ConnectionStrings[ "BlahBlahBlahConnectionString" ].ConnectionString;using ( SqlConnection conn = new SqlConnection( source ) )
			{
				conn.Open( );
				SqlCommand cmd = new SqlCommand( );
				cmd.CommandText = "dbo.usp_get_data";
				cmd.CommandType = CommandType.StoredProcedure;
				cmd.Connection = conn;

				cmd.Parameters.AddWithValue( "@CompanyId", customerID );
				SqlDataReader dr = cmd.ExecuteReader( );if ( dr.Read( ) )
				{
					XmlWriter writer = XmlWriter.Create( stream );// Get Data string companyName = ( string )dr[ "CompanyName" ];string numEmployees = ( string )dr[ "NumberOfEmployees" ].ToString( );string technologyID = ( string )dr[ "TechnologyID" ];
							. . .
							. . .
							. . .// Title Page
					writer.WriteStartElement( "Customer" );
					writer.WriteElementString( "CompanyName", companyName );
					writer.WriteElementString( "TechnologyID", technologyID );
					writer.WriteElementString( "NumberOfEmployees", numEmployees );
							. . .
							. . .
							. . .


					writer.WriteEndElement( );
					writer.Close( );
				}


				dr.Close( );
				conn.Close( );
			}
		}

Shameful lack of error handling aside, the above process works nicely; the resulting document is beautiful.  The problem is I don't see a way forward from here given the latest requests.

Any thoughts would be very much appreciated.

 

JP


Viewing all articles
Browse latest Browse all 1288

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>