SQL Comparison SDK

Latest version: 10.0

SQL Comparison SDK

Knowledge Base

Creating an HTML report of schema differences in C#

Category: How do I?
Date: 20 Sep 2012
Product: SQL Comparison SDK
Versions: 7.x

SQL Compare's API does not directly support the creation of HTML reports as the user interface of the program does. By manually generating an XML document, a report can be generated by transforming the XML using the XSL template supplied with the SQL Compare program. This report code examines the differences between databases WidgetStaging and WidgetProduction, generates suitable XML, and converts it to HTML using the template SQLCompareInteractiveReportTemplate.xsl.

  1. [C#]
  2. //====================================================================
  3. // Save the following as Program.cs
  4. //====================================================================
  5. using System;
  6. using System.Data;
  7. using System.IO;
  8. using RedGate.SQLCompare.Engine; //Reference %programfiles%\Red Gate\SQL Compare 6\RedGate.SQLCompare.Engine.dll
  9. using RedGate.Shared.SQL; // Reference %programfiles%\Red Gate\SQL Compare 6\RedGate.SQL.Shared.dll
  10. using System.Diagnostics; // for ProcessStartInfo
  11.  
  12. namespace SQLCompareReport
  13. {
  14. /// <summary>
  15. /// Two functions -- CreateHTMLReport and ViewReport -- create an HTML report and optionally view it.
  16. /// </summary>
  17.  
  18. class Program
  19. {
  20. static void Main(string[] args)
  21. {
  22. using (Database dbSource = new Database(),
  23. dbTarget = new Database())
  24. {
  25. // Retrieve the schema information for the two databases
  26. Console.WriteLine("Registering databases");
  27. dbSource.Register(new ConnectionProperties(".", "WidgetStaging"), Options.Default);
  28. dbTarget.Register(new ConnectionProperties(".", "WidgetProduction"), Options.Default);
  29.  
  30. Console.WriteLine("Comparing Databases");
  31. Differences dbSourceVsdbTarget = dbSource.CompareWith(dbTarget, Options.Default);
  32.  
  33. // Set the filespec for our HTML report
  34. string ReportOutput = @"c:\Program files\Red Gate\SQL Compare 7\htmlreport.html";
  35. // Set the XSL template to use for the report. These ship with the SQL Compare software
  36. string xsltemplate = @"c:\Program files\Red Gate\SQL Compare 7\SQLCompareInteractiveReportTemplate.xsl";
  37.  
  38. Console.WriteLine("Creating report...");
  39. HTMLReport.CreateHtmlReport(ReportOutput, dbSource, dbTarget, dbSourceVsdbTarget, Options.Default,xsltemplate);
  40.  
  41. Console.WriteLine("Finished creating {0}, viewing", ReportOutput);
  42. HTMLReport.ViewReport(ReportOutput);
  43. }
  44. Console.WriteLine("Press any key to continue");
  45. Console.ReadLine();
  46. }
  47. }
  48. }
  49. //====================================================================
  50. // EOF Program.cs
  51. //====================================================================
  52. //====================================================================
  53. // Save the following as HTMLReport.cs
  54. //====================================================================
  55. using System;
  56. using System.Collections;
  57. using System.Data;
  58. using System.IO;
  59. using System.Xml;
  60. using System.Xml.Xsl;
  61. using System.Text;
  62. using System.Reflection;
  63. using RedGate.SQLCompare.Engine;
  64. using RedGate.Shared.SQL;
  65. using System.Diagnostics;
  66. using RedGate.Shared.Utils;
  67. using System.Collections.Generic; // for ProcessStartInfo
  68.  
  69. namespace SQLCompareReport
  70. {
  71. public class HTMLReport
  72. {
  73.  
  74. // This method will create the XML needed for the report and transform it to an HTML page
  75. // specified by fileName. It looks in the current folder for the template file.
  76. // Please supply the two database objects, the Differences object that you get after a comparison,
  77. // and the set of options that you used for the comparison.
  78.  
  79. public static void CreateHtmlReport(string fileName, RedGate.SQLCompare.Engine.Database dbSourceDatabase, RedGate.SQLCompare.Engine.Database dbTargetDatabase, RedGate.SQLCompare.Engine.Differences obDatabaseDifferences, RedGate.SQLCompare.Engine.Options enOptions,string xlstemplate)
  80. {
  81. string tempFile = Path.GetTempFileName();
  82. XslCompiledTransform xslt = new XslCompiledTransform();
  83.         //Load the XSL template
  84.         XsltSettings xSettings = new XsltSettings();
  85.         xSettings.EnableScript = true;
  86.         xslt.Load(xlstemplate, xSettings, new XmlUrlResolver());
  87.         try
  88.         {
  89.                 XmlTextWriter writer = new XmlTextWriter(tempFile, Encoding.Unicode);
  90.                 //Generate the raw data that will go into the report
  91.                 GenerateXml(writer, dbSourceDatabase, dbTargetDatabase, obDatabaseDifferences, enOptions);
  92.                 writer.Close();
  93.                 xslt.Transform(tempFile, fileName);
  94.         }
  95.         catch (Exception e)
  96.         {
  97.                 Console.WriteLine("Unable to generate html report " + e.Message);
  98.         }
  99.         finally
  100.         {
  101.                 File.Delete(tempFile);
  102.         }
  103.   }
  104.  
  105. /// <summary>
  106. /// This is the method that creates the XML data used in the report (SQL Compare v7)
  107. /// </summary>
  108. /// <param name="writer">XmlTextWriter object</param>
  109. /// <param name="dbSourceDatabase">A registered database</param>
  110. /// <param name="dbTargetDatabase">The second registered database</param>
  111. /// <param name="obDatabaseDifferences">The differences between the two databases</param>
  112. /// <param name="m_Options">Set of options used during the comparison process</param>
  113. private static void GenerateXml(XmlTextWriter writer, RedGate.SQLCompare.Engine.Database dbSourceDatabase, RedGate.SQLCompare.Engine.Database dbTargetDatabase, RedGate.SQLCompare.Engine.Differences obDatabaseDifferences, RedGate.SQLCompare.Engine.Options options)
  114. {
  115.         writer.WriteStartDocument();
  116.         //Header
  117.         writer.WriteStartElement("comparison");
  118.         writer.WriteAttributeString("direction", "1to2");
  119.         writer.WriteAttributeString("timestamp", DateTime.Now.ToString());
  120.         //Datasources
  121.         writer.WriteStartElement("datasources");
  122.         writer.WriteStartElement("datasource");
  123.         writer.WriteAttributeString("type", "live");
  124.         writer.WriteAttributeString("id", "1");
  125.         writer.WriteStartElement("server");
  126.         writer.WriteString(dbSourceDatabase.ConnectionProperties.ServerName);
  127.         writer.WriteEndElement(); // </server>
  128.         writer.WriteStartElement("database");
  129.         writer.WriteString(dbSourceDatabase.ConnectionProperties.DatabaseName);
  130.         writer.WriteEndElement(); // </database>
  131.         writer.WriteEndElement(); // <datasource[@id=1]>
  132.         //Second database
  133.         writer.WriteStartElement("datasource");
  134.         writer.WriteAttributeString("type", "live");
  135.         writer.WriteAttributeString("id", "2");
  136.         writer.WriteStartElement("server");
  137.         writer.WriteString(dbTargetDatabase.ConnectionProperties.ServerName);
  138.         writer.WriteEndElement(); // </server>
  139.         writer.WriteStartElement("database");
  140.         writer.WriteString(dbTargetDatabase.ConnectionProperties.DatabaseName);
  141.         writer.WriteEndElement(); // </database>
  142.         writer.WriteEndElement(); // </datasource>
  143.         writer.WriteEndElement(); // </datasources>
  144.         //Differences collection
  145.         writer.WriteStartElement("differences");
  146.         foreach (Difference d in obDatabaseDifferences)
  147.         {
  148.         if (d.Type == DifferenceType.Equal)
  149.                 continue;
  150.         if (!d.Selected)
  151.                 continue;
  152.                 writer.WriteStartElement("difference");
  153.                 writer.WriteAttributeString("objecttype", d.DatabaseObjectType.ToString().ToLower());
  154.                 writer.WriteAttributeString("status", d.Type.ToString().Trim().ToLower());
  155.                 writer.WriteAttributeString("fqn", String.Format("{0}-{1}",d.DatabaseObjectType.ToString().ToLower(),d.Name.ToString().ToLower()));
  156.  
  157.                 switch (d.Type){
  158.                         case DifferenceType.OnlyIn1:
  159.                                 writer.WriteStartElement("object");
  160.                                 writer.WriteAttributeString("owner", d.ObjectIn1.Owner);
  161.                                 writer.WriteAttributeString("id", "1");
  162.                                 writer.WriteString(d.ObjectIn1.FullyQualifiedName);
  163.                                 writer.WriteEndElement();
  164.                                 writer.WriteStartElement("object");
  165.                                 writer.WriteAttributeString("owner","");
  166.                                 writer.WriteAttributeString("id", "2");
  167.                                 writer.WriteEndElement();
  168.                         break;
  169.                         case DifferenceType.OnlyIn2:
  170.                                 writer.WriteStartElement("object");
  171.                                 writer.WriteAttributeString("owner", "");
  172.                                 writer.WriteAttributeString("id", "1");
  173.                                 writer.WriteEndElement();
  174.                                 writer.WriteStartElement("object");
  175.                                 writer.WriteAttributeString("owner", d.ObjectIn2.Owner);
  176.                                 writer.WriteAttributeString("id", "2");
  177.                                 writer.WriteString(d.ObjectIn2.FullyQualifiedName);
  178.                                 writer.WriteEndElement();
  179.                         break;
  180.                         default: // object exists in both
  181.                                 writer.WriteStartElement("object");
  182.                                 writer.WriteAttributeString("owner", d.ObjectIn1.Owner);
  183.                                 writer.WriteAttributeString("id", "1");
  184.                                 writer.WriteString(d.ObjectIn1.FullyQualifiedName);
  185.                                 writer.WriteEndElement();
  186.                                 writer.WriteStartElement("object");
  187.                                 writer.WriteAttributeString("owner", d.ObjectIn2.Owner);
  188.                                 writer.WriteAttributeString("id", "2");
  189.                                 writer.WriteString(d.ObjectIn2.FullyQualifiedName);
  190.                                 writer.WriteEndElement();
  191.                         break;
  192.         }
  193.         // Now we write the actual SQL code for the objects in database 1 and 2
  194.         // Since the reordering of lines is copyright code in SQL Compare
  195.         // we are going to simply dump the SQL in the order it comes
  196.                 writer.WriteStartElement("comparisonstrings");
  197.                 Work w = new Work();
  198.                 Regions regions1 = w.ScriptObject(d.ObjectIn1, options);
  199.                 Regions regions2 = w.ScriptObject(d.ObjectIn2, options);
  200.                 // Work out which region is "shortest"
  201.                 int regionCount=regions1.Count;
  202.                 bool oneIsLonger=true;
  203.                 if (regions2.Count>regions1.Count)
  204.                 {
  205.                         regionCount=regions2.Count;
  206.                         oneIsLonger=false;
  207.                 }
  208.                 //loop through all SQL regions -- append the longer lines
  209.                 int j = 0;
  210.                 for (j=0; j < regioncount;="">
  211.                 {
  212.                         //Start writing out the lines of SQL code
  213.                         bool oneHasMoreLines = false;
  214.                         string[] linesFrom1;
  215.                         string[] linesFrom2;
  216.                         try
  217.                         {
  218.                                 linesFrom1 = regions1[j].SQL.Split('\n');
  219.                         }
  220.                         catch (ArgumentOutOfRangeException) // There are more regions in region2
  221.                         {
  222.                                 linesFrom1 = new string[regions2[j].SQL.Split('\n').Length];
  223.                                 for (int y = 0; y < linesfrom1.length;="">
  224.                                 {
  225.                                         linesFrom1[y] = String.Empty;
  226.                                 }
  227.                         }
  228.                         try
  229.                         {
  230.                                 linesFrom2 = regions2[j].SQL.Split('\n');
  231.                         }
  232.                         catch (ArgumentOutOfRangeException) // There are more regions in region1
  233.                         {
  234.                                 linesFrom2 = new string[regions1[j].SQL.Split('\n').Length];
  235.                                 for(int y=0;y<>
  236.                                 {
  237.                                         linesFrom2[y] = String.Empty;
  238.                                 }
  239.                         }
  240.                         int sqlLineCount = linesFrom1.Length;
  241.                         int sqlLineCount2 = linesFrom2.Length;
  242.                         if (sqlLineCount > sqlLineCount2)
  243.                         {
  244.                                 sqlLineCount = sqlLineCount2;
  245.                                 oneHasMoreLines = true;
  246.                         }
  247.                         int l=0;
  248.                         for (; l <>
  249.                         {
  250.                                 writer.WriteStartElement("line");
  251.                                 writer.WriteAttributeString("type", String.Compare(linesFrom1[l],linesFrom2[l], true) != 0 ? "different" : "same");
  252.                                 // Dump the line of SQL from db1
  253.                                 writer.WriteStartElement("left");
  254.                                 writer.WriteString(linesFrom1[l].Trim());
  255.                                 writer.WriteEndElement(); // </left>
  256.                                 // ...and db2
  257.                                 writer.WriteStartElement("right");
  258.                                 writer.WriteString(linesFrom2[l].Trim());
  259.                                 writer.WriteEndElement(); // </right>
  260.                                 writer.WriteEndElement(); //</line>
  261.                         }
  262.                         // Write out any "leftover" SQL
  263.                         string[] leftoverSql=linesFrom2;
  264.                         if (oneHasMoreLines) leftoverSql=linesFrom1;
  265.                         for (int m = l; m < leftoversql.length;="">
  266.                         {
  267.                                 writer.WriteStartElement("line");
  268.                                 writer.WriteAttributeString("type", "different");
  269.                                 writer.WriteStartElement("left");
  270.                                 if (oneHasMoreLines) writer.WriteString(leftoverSql[m].Trim());
  271.                                 writer.WriteEndElement(); // </left>
  272.                                 writer.WriteStartElement("right");
  273.                                 if (!oneHasMoreLines) writer.WriteString(leftoverSql[m].Trim());
  274.                                 writer.WriteEndElement(); // </right>
  275.                                 writer.WriteEndElement(); //</line>
  276.                         }
  277.                 }
  278.                 writer.WriteEndElement(); // </comparisonStrings>
  279.                 writer.WriteEndElement(); // </difference>
  280.         }
  281.         writer.WriteEndElement(); // </differences>
  282.         writer.WriteEndElement(); // </comparison>
  283.         writer.WriteEndDocument(); //EOF
  284. }
  285.  
  286.  
  287. //Feed the .htm file to Windows and let it start the viewer (IE)
  288. public static void ViewReport(string sPath)
  289. {
  290. if (sPath == string.Empty)
  291. return;
  292.  
  293. // view the doc
  294. try
  295. {
  296. ProcessStartInfo psi = new ProcessStartInfo(sPath);
  297. psi.UseShellExecute = true;
  298. Process.Start(psi);
  299. }
  300. catch { }
  301. }
  302.  
  303. } //end class
  304. }
  305.  
  306. //====================================================================
  307. // EOF HTMLReport.cs
  308. //====================================================================
  309. [/C#]

Document ID: KB200805000257 Keywords: SQL, Compare, report, Toolkit, C#

Was this article helpful?

Search support
Forums

SQL Comparison SDK

all SQL products

all products