init
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities
|
||||
{
|
||||
internal class DatabaseAdapter
|
||||
{
|
||||
private readonly SqlConnection _connection;
|
||||
|
||||
internal DatabaseAdapter(SqlConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
|
||||
if (_connection.State == ConnectionState.Closed)
|
||||
_connection.Open();
|
||||
}
|
||||
|
||||
internal bool IsSnapshot(string name)
|
||||
{
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
command.CommandText =
|
||||
"--Testing Existence Type\nSELECT source_database_id FROM sys.databases WHERE name = @name";
|
||||
|
||||
command.Parameters.AddWithValue("@name", name);
|
||||
|
||||
object objResult = command.ExecuteScalar();
|
||||
|
||||
if (objResult != null && !(objResult is DBNull))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void UseDatabase(string databaseName)
|
||||
{
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
command.CommandText = "USE " + databaseName;
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
internal void CreateSnapshot(string databaseName, string snapshotName)
|
||||
{
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder("-- Creating Snapshot \nCREATE DATABASE ");
|
||||
stringBuilder.Append(snapshotName);
|
||||
stringBuilder.Append(" ON ( NAME = ");
|
||||
stringBuilder.Append(databaseName);
|
||||
stringBuilder.Append(", FILENAME = 'C:\\Temp\\");
|
||||
stringBuilder.Append(snapshotName);
|
||||
stringBuilder.Append("') AS SNAPSHOT OF ");
|
||||
stringBuilder.Append(databaseName);
|
||||
command.CommandText = stringBuilder.ToString();
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
internal void DropSnapshot(string snapshotName)
|
||||
{
|
||||
if (!IsSnapshot(snapshotName))
|
||||
throw new ArgumentException("A snapshot of name " + snapshotName + " does not exist.");
|
||||
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
command.CommandText = "DROP DATABASE " + snapshotName;
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
using DatabaseUnitTesting.Utilities.Results;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities
|
||||
{
|
||||
internal class DatabaseComparer
|
||||
{
|
||||
private readonly SqlConnection _connection;
|
||||
private readonly DatabaseAdapter _databaseAdapter;
|
||||
private readonly string _databaseOne;
|
||||
private readonly string _databaseTwo;
|
||||
private readonly List<ObjectsToCompare> _objectsToCompare = new List<ObjectsToCompare>();
|
||||
|
||||
internal DatabaseComparer(SqlConnection connection, string databaseOneName, string databaseTwoName)
|
||||
{
|
||||
_connection = connection;
|
||||
_databaseAdapter = new DatabaseAdapter(connection);
|
||||
_databaseAdapter.UseDatabase(databaseOneName);
|
||||
_databaseOne = databaseOneName;
|
||||
_databaseTwo = databaseTwoName;
|
||||
}
|
||||
|
||||
internal void CleanUp()
|
||||
{
|
||||
_objectsToCompare.Clear();
|
||||
}
|
||||
|
||||
internal Database GenerateDifferences(SqlTransaction transaction)
|
||||
{
|
||||
Database tables = new Database();
|
||||
|
||||
foreach (ObjectsToCompare objects in _objectsToCompare)
|
||||
{
|
||||
Table table = RunCompare(transaction, objects);
|
||||
if (table.RowCount > 0)
|
||||
tables.AddTable(table);
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
internal string GetAllColumns(SqlTransaction transaction, ObjectsToCompare objects)
|
||||
{
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
command.CommandText =
|
||||
"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = @table AND" +
|
||||
" table_schema = @schema AND table_catalog = @database";
|
||||
command.Transaction = transaction;
|
||||
command.Parameters.AddWithValue("@table", objects.Object1Name);
|
||||
command.Parameters.AddWithValue("@schema", objects.Schema1Name);
|
||||
command.Parameters.AddWithValue("@database", _databaseOne);
|
||||
|
||||
List<string> allColumns = new List<string>();
|
||||
|
||||
using (SqlDataReader result = command.ExecuteReader())
|
||||
{
|
||||
while (result.Read())
|
||||
allColumns.Add(result.GetString(0));
|
||||
}
|
||||
|
||||
if (allColumns.Count == 0)
|
||||
throw new ArgumentException("Object " + objects.Qualified1 + " does not exist");
|
||||
|
||||
foreach (string victim in objects.ColumnsToIgnore)
|
||||
{
|
||||
if (!allColumns.Remove(victim))
|
||||
throw new ArgumentException("Specified column " + victim + " was not in table " +
|
||||
objects.Qualified1);
|
||||
if (allColumns.Count == 0)
|
||||
throw new ArgumentException("User cannot ignore all columns in a table.");
|
||||
}
|
||||
|
||||
return String.Join(",", allColumns.ToArray());
|
||||
}
|
||||
|
||||
internal Table RunCompare(SqlTransaction transaction, ObjectsToCompare objectComparison)
|
||||
{
|
||||
Table table =
|
||||
new Table(objectComparison.Qualified1, objectComparison.Qualified2);
|
||||
|
||||
string columns = GetAllColumns(transaction, objectComparison);
|
||||
|
||||
StringBuilder select = new StringBuilder("SELECT ", 1000);
|
||||
select.Append(columns);
|
||||
select.Append(", ROW_NUMBER() OVER(PARTITION BY ");
|
||||
select.Append(columns);
|
||||
select.Append(" ORDER BY @@SPID) AS 'TempRowNumber' FROM ");
|
||||
string select1 = select + _databaseOne + "." + objectComparison.Qualified1;
|
||||
string select2 = select + _databaseTwo + "." + objectComparison.Qualified2;
|
||||
string commandText = "--Data Comparison\n" + select1 + "\nEXCEPT\n" + select2 + "\n" +
|
||||
select2 + "\nEXCEPT\n" + select1;
|
||||
SqlCommand command = _connection.CreateCommand();
|
||||
command.CommandText = commandText;
|
||||
command.Transaction = transaction;
|
||||
|
||||
using (SqlDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
string type = "In First";
|
||||
string[] columnNames = columns.Split(',');
|
||||
do
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Row row = new Row(type);
|
||||
for (int i = 0; i < (reader.FieldCount - 1); i++)
|
||||
{
|
||||
object value = reader.GetValue(i);
|
||||
if (!(value is DBNull))
|
||||
row.AddColumn(new Column(columnNames[i].ToLower(), value));
|
||||
}
|
||||
table.AddRow(row);
|
||||
}
|
||||
type = "In Second";
|
||||
} while (reader.NextResult());
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
internal void AddObjectComparison(string schema1, string object1, string schema2, string object2)
|
||||
{
|
||||
_objectsToCompare.Add(new ObjectsToCompare(schema1, object1, schema2, object2));
|
||||
}
|
||||
|
||||
internal void AddColumnToIgnore(string schemaName, string objectName, string columnName)
|
||||
{
|
||||
_objectsToCompare.Find(
|
||||
delegate (ObjectsToCompare item) { return item.Schema1Name == schemaName && item.Object1Name == objectName; }
|
||||
).AddColumnToIgnore(columnName);
|
||||
}
|
||||
|
||||
internal void AddColumnsToIgnore(string schemaName, string tableName, List<string> columnNames)
|
||||
{
|
||||
foreach (string columnName in columnNames)
|
||||
AddColumnToIgnore(schemaName, tableName, columnName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities
|
||||
{
|
||||
internal class ObjectsToCompare
|
||||
{
|
||||
private readonly string _schema1Name;
|
||||
private readonly string _object1Name;
|
||||
private readonly string _schema2Name;
|
||||
private readonly string _object2Name;
|
||||
|
||||
private readonly List<string> _columnsToIgnore = new List<string>();
|
||||
|
||||
public ObjectsToCompare(string schema1Name, string object1Name, string schema2Name,
|
||||
string object2Name)
|
||||
{
|
||||
_schema1Name = schema1Name;
|
||||
_object1Name = object1Name;
|
||||
_schema2Name = schema2Name;
|
||||
_object2Name = object2Name;
|
||||
}
|
||||
|
||||
public IEnumerable<string> ColumnsToIgnore
|
||||
{
|
||||
get { return _columnsToIgnore; }
|
||||
}
|
||||
|
||||
public string Object1Name
|
||||
{
|
||||
get { return _object1Name; }
|
||||
}
|
||||
|
||||
public string Schema1Name
|
||||
{
|
||||
get { return _schema1Name; }
|
||||
}
|
||||
|
||||
public string Object2Name
|
||||
{
|
||||
get { return _object2Name; }
|
||||
}
|
||||
|
||||
public string Schema2Name
|
||||
{
|
||||
get { return _schema2Name; }
|
||||
}
|
||||
|
||||
public string Qualified1
|
||||
{
|
||||
get { return _schema1Name + "." + _object1Name; }
|
||||
}
|
||||
|
||||
public string Qualified2
|
||||
{
|
||||
get { return _schema2Name + "." + _object2Name; }
|
||||
}
|
||||
|
||||
public void AddColumnToIgnore(string columnName)
|
||||
{
|
||||
_columnsToIgnore.Add(columnName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal class Column
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly string _value;
|
||||
private readonly string _sortString;
|
||||
public const string DELIMITER = "\x1f;;";
|
||||
|
||||
public Column(string name, object value)
|
||||
{
|
||||
_name = name;
|
||||
_value = Convert(value);
|
||||
_sortString = String.Concat(_name.ToLower(), DELIMITER, _value);
|
||||
}
|
||||
|
||||
public static string Convert(object value)
|
||||
{
|
||||
if (value is byte[])
|
||||
{
|
||||
string[] binary = new string[((byte[])value).Length + 1];
|
||||
binary[0] = "0x";
|
||||
for (int i = 1; i < binary.Length; i++)
|
||||
binary[i] = ((byte[])value)[i - 1].ToString("X1");
|
||||
|
||||
return String.Join("", binary);
|
||||
}
|
||||
|
||||
if (value is DateTime)
|
||||
{
|
||||
string time = ((DateTime)value).ToShortDateString() + " ";
|
||||
time += ((DateTime)value).TimeOfDay;
|
||||
return time.TrimEnd('0').TrimEnd(':');
|
||||
}
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
public string SortString
|
||||
{
|
||||
get { return _sortString; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal class Database
|
||||
{
|
||||
private int _tableCount = 0;
|
||||
private int _hashCode = 0;
|
||||
private readonly Dictionary<Table, int> _tables = new Dictionary<Table, int>();
|
||||
|
||||
public int TableCount
|
||||
{
|
||||
get { return _tableCount; }
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<Table, int>> Tables
|
||||
{
|
||||
get { return _tables; }
|
||||
}
|
||||
|
||||
public void AddTable(Table table)
|
||||
{
|
||||
if (_tables.ContainsKey(table))
|
||||
_tables[table]++;
|
||||
else
|
||||
_tables.Add(table, 1);
|
||||
|
||||
_tableCount++;
|
||||
_hashCode = _hashCode + table.GetHashCode();
|
||||
}
|
||||
|
||||
public bool ContainsTable(Table table)
|
||||
{
|
||||
return _tables.ContainsKey(table);
|
||||
}
|
||||
|
||||
public int GetCount(Table table)
|
||||
{
|
||||
return _tables[table];
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _hashCode;
|
||||
}
|
||||
|
||||
public override bool Equals(object otherObject)
|
||||
{
|
||||
if (!(otherObject is Database))
|
||||
return false;
|
||||
|
||||
Database other = (Database)otherObject;
|
||||
|
||||
if (TableCount != other.TableCount ||
|
||||
GetHashCode() != other.GetHashCode())
|
||||
return false;
|
||||
|
||||
foreach (KeyValuePair<Table, int> pair in _tables)
|
||||
if (!other.ContainsTable(pair.Key) ||
|
||||
other.GetCount(pair.Key) != pair.Value)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal static class ResultSetParser
|
||||
{
|
||||
private const int NAME = 0;
|
||||
private const int WIDTH = 2;
|
||||
private const int PRECISION = 3;
|
||||
private const int SCALE = 4;
|
||||
private const int TYPE = 24;
|
||||
|
||||
internal static Database Parse(SqlCommand command)
|
||||
{
|
||||
using (SqlDataReader sqlReader = command.ExecuteReader())
|
||||
{
|
||||
Database results = new Database();
|
||||
|
||||
if (!sqlReader.HasRows)
|
||||
return results;
|
||||
|
||||
do
|
||||
{
|
||||
Table table = new Table("Result Set");
|
||||
List<string> fieldNames = SetFields(table, sqlReader.GetSchemaTable());
|
||||
while (sqlReader.Read())
|
||||
{
|
||||
Row row = new Row("datarow");
|
||||
for (int i = 0; i < sqlReader.FieldCount; i++)
|
||||
{
|
||||
object value = sqlReader.GetValue(i);
|
||||
if (!(value is DBNull))
|
||||
row.AddColumn(new Column(fieldNames[i], sqlReader.GetValue(i)));
|
||||
}
|
||||
table.AddRow(row);
|
||||
}
|
||||
results.AddTable(table);
|
||||
} while (sqlReader.NextResult());
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
internal static List<string> SetFields(Table table, DataTable schema)
|
||||
{
|
||||
List<string> fieldNames = new List<string>();
|
||||
foreach (DataRow dataRow in schema.Rows)
|
||||
{
|
||||
string name = dataRow[NAME].ToString();
|
||||
string type = dataRow[TYPE].ToString().ToLower();
|
||||
string precision = dataRow[PRECISION].ToString();
|
||||
string scale = dataRow[SCALE].ToString();
|
||||
string width = dataRow[WIDTH].ToString();
|
||||
|
||||
StringBuilder typeString = new StringBuilder(type);
|
||||
|
||||
if (type.Equals("decimal") || type.Equals("numeric"))
|
||||
{
|
||||
typeString.Append("(");
|
||||
typeString.Append(precision);
|
||||
typeString.Append(",");
|
||||
typeString.Append(scale);
|
||||
typeString.Append(")");
|
||||
}
|
||||
else if (type.Contains("char") || type.Contains("binary"))
|
||||
{
|
||||
if (int.Parse(width) > 8000)
|
||||
width = "max";
|
||||
|
||||
typeString.Append("(");
|
||||
typeString.Append(width);
|
||||
typeString.Append(")");
|
||||
}
|
||||
|
||||
fieldNames.Add(name);
|
||||
table.AddField(name, typeString.ToString());
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal class Row
|
||||
{
|
||||
private readonly string _type;
|
||||
private readonly List<Column> _columns = new List<Column>();
|
||||
private string _keyString;
|
||||
private bool _keyValid = false;
|
||||
public const string DELIMITER = "\x1e;;";
|
||||
|
||||
public Row(string type)
|
||||
{
|
||||
_type = type.ToLower();
|
||||
}
|
||||
|
||||
public void AddColumn(Column column)
|
||||
{
|
||||
_columns.Add(column);
|
||||
_keyValid = false;
|
||||
}
|
||||
|
||||
public string KeyString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_keyValid)
|
||||
{
|
||||
string[] keyString = new string[_columns.Count + 1];
|
||||
keyString[0] = _type;
|
||||
for (int i = 1; i < keyString.Length; i++)
|
||||
keyString[i] = _columns[i - 1].SortString;
|
||||
|
||||
_keyString = String.Join(DELIMITER, keyString);
|
||||
_keyValid = true;
|
||||
}
|
||||
return _keyString;
|
||||
}
|
||||
}
|
||||
|
||||
public int ColumnCount
|
||||
{
|
||||
get { return _columns.Count; }
|
||||
}
|
||||
}
|
||||
}
|
||||
117
DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Table.cs
Normal file
117
DataPRO/UnitTest/DatabaseUnitTesting/Utilities/Results/Table.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal class Table
|
||||
{
|
||||
private readonly string _name1;
|
||||
private readonly string _name2;
|
||||
private Row _schema = new Row("schema");
|
||||
private readonly Dictionary<string, int> rows = new Dictionary<string, int>();
|
||||
|
||||
private int _hashCode;
|
||||
private int _rowCount;
|
||||
private int _fieldCount;
|
||||
|
||||
public Table(string name1) : this(name1, String.Empty)
|
||||
{ }
|
||||
|
||||
public Table(string name1, string name2)
|
||||
{
|
||||
_name1 = name1.ToLower();
|
||||
_name2 = name2.ToLower();
|
||||
}
|
||||
|
||||
|
||||
public override bool Equals(object otherObject)
|
||||
{
|
||||
if (!(otherObject is Table))
|
||||
return false;
|
||||
|
||||
Table other = (Table)otherObject;
|
||||
|
||||
if (GetHashCode() != other.GetHashCode())
|
||||
return false;
|
||||
|
||||
if (RowCount != other.RowCount ||
|
||||
FieldCount != other.FieldCount ||
|
||||
!Schema.KeyString.Equals(other.Schema.KeyString))
|
||||
return false;
|
||||
|
||||
int otherCount;
|
||||
|
||||
foreach (string row in rows.Keys)
|
||||
if (!other.LookupRow(row, out otherCount) ||
|
||||
otherCount != rows[row])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _hashCode;
|
||||
}
|
||||
|
||||
public string Name1
|
||||
{
|
||||
get { return _name1; }
|
||||
}
|
||||
|
||||
public string Name2
|
||||
{
|
||||
get { return _name2; }
|
||||
}
|
||||
|
||||
public void AddRow(Row row)
|
||||
{
|
||||
string key = row.KeyString;
|
||||
if (rows.ContainsKey(key))
|
||||
rows[key]++;
|
||||
else
|
||||
rows.Add(key, 1);
|
||||
|
||||
_rowCount = _rowCount + 1;
|
||||
_hashCode = _hashCode + key.GetHashCode();
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<string, int>> Rows
|
||||
{
|
||||
get { return rows; }
|
||||
}
|
||||
|
||||
public int RowCount
|
||||
{
|
||||
get { return _rowCount; }
|
||||
}
|
||||
|
||||
public int FieldCount
|
||||
{
|
||||
get { return _fieldCount; }
|
||||
}
|
||||
|
||||
public Row Schema
|
||||
{
|
||||
get { return _schema; }
|
||||
set
|
||||
{
|
||||
_schema = value;
|
||||
_fieldCount = _schema.ColumnCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddField(string name, string type)
|
||||
{
|
||||
Column c = new Column(name, type.ToLower());
|
||||
_schema.AddColumn(c);
|
||||
_fieldCount++;
|
||||
// _hashCode = _hashCode + c.SortString.GetHashCode();
|
||||
}
|
||||
|
||||
public bool LookupRow(string key, out int other)
|
||||
{
|
||||
return rows.TryGetValue(key, out other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace DatabaseUnitTesting.Utilities.Results
|
||||
{
|
||||
internal static class XmlFileAdapter
|
||||
{
|
||||
private const string LEFT = "\0L;;";
|
||||
private const string RIGHT = "\0R;;";
|
||||
|
||||
internal static Database Read(string filename)
|
||||
{
|
||||
XmlDocument document = new XmlDocument();
|
||||
Database database = new Database();
|
||||
|
||||
document.Load(filename);
|
||||
XmlNode xmlRoot = document.LastChild;
|
||||
|
||||
if (xmlRoot == null)
|
||||
return database;
|
||||
|
||||
foreach (Table diff in ReadTables(xmlRoot))
|
||||
database.AddTable(diff);
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
internal static List<Table> ReadTables(XmlNode xmlRoot)
|
||||
{
|
||||
List<Table> tableDiffs = new List<Table>();
|
||||
for (XmlNode xmlObject = xmlRoot.FirstChild; xmlObject != null; xmlObject = xmlObject.NextSibling)
|
||||
{
|
||||
XmlAttribute name1Attribute = xmlObject.Attributes["name1"];
|
||||
XmlAttribute name2Attribute = xmlObject.Attributes["name2"];
|
||||
|
||||
if (name1Attribute == null || name2Attribute == null)
|
||||
throw new XmlSyntaxException("Tables must have name1 and name2 attributes");
|
||||
|
||||
Table tableDiff = new Table(name1Attribute.Value, name2Attribute.Value);
|
||||
|
||||
Row schema = new Row("schema");
|
||||
ReadColumns(xmlObject.FirstChild, schema);
|
||||
|
||||
tableDiff.Schema = schema;
|
||||
|
||||
foreach (Row row in ReadRows(xmlObject))
|
||||
{
|
||||
tableDiff.AddRow(row);
|
||||
}
|
||||
|
||||
tableDiffs.Add(tableDiff);
|
||||
}
|
||||
return tableDiffs;
|
||||
}
|
||||
|
||||
internal static List<Row> ReadRows(XmlNode xmlTable)
|
||||
{
|
||||
List<Row> rowDiffs = new List<Row>();
|
||||
for (XmlNode xmlRow = xmlTable.FirstChild.NextSibling; xmlRow != null; xmlRow = xmlRow.NextSibling)
|
||||
{
|
||||
XmlAttribute typeAttribute = xmlRow.Attributes["type"];
|
||||
|
||||
if (typeAttribute == null)
|
||||
throw new XmlSyntaxException("Row does not have a 'type' attribute");
|
||||
|
||||
Row row = new Row(typeAttribute.Value);
|
||||
ReadColumns(xmlRow, row);
|
||||
rowDiffs.Add(row);
|
||||
}
|
||||
return rowDiffs;
|
||||
}
|
||||
|
||||
internal static void ReadColumns(XmlNode xmlRow, Row row)
|
||||
{
|
||||
for (XmlNode column = xmlRow.FirstChild; column != null; column = column.NextSibling)
|
||||
{
|
||||
XmlAttribute nameAttribute = column.Attributes["name"];
|
||||
|
||||
if (nameAttribute == null)
|
||||
throw new XmlSyntaxException("Fields and Keys must have 'name' attributes");
|
||||
|
||||
string name = nameAttribute.Value.ToLower();
|
||||
|
||||
if (column.Name.ToLower().Equals("column"))
|
||||
{
|
||||
XmlAttribute valueAttribute = column.Attributes["value"];
|
||||
if (valueAttribute == null)
|
||||
throw new XmlSyntaxException("Columns must have 'value' attribute");
|
||||
|
||||
row.AddColumn(new Column(name, valueAttribute.Value.Replace(LEFT, "<").Replace(RIGHT, ">")));
|
||||
}
|
||||
else
|
||||
throw new XmlSyntaxException("Rows may only contain 'column' children");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Write(string filename, Database diffs)
|
||||
{
|
||||
using (XmlTextWriter writer = new XmlTextWriter(filename, Encoding.UTF8))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("results");
|
||||
|
||||
foreach (KeyValuePair<Table, int> table in diffs.Tables)
|
||||
for (int i = 0; i < table.Value; i++)
|
||||
WriteTable(writer, table.Key);
|
||||
|
||||
writer.WriteEndDocument();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WriteTable(XmlTextWriter writer, Table tableDiff)
|
||||
{
|
||||
writer.WriteStartElement("object");
|
||||
|
||||
writer.WriteAttributeString("name1", tableDiff.Name1);
|
||||
writer.WriteAttributeString("name2", tableDiff.Name2);
|
||||
|
||||
|
||||
WriteRow(writer, tableDiff.Schema.KeyString);
|
||||
|
||||
foreach (KeyValuePair<string, int> row in tableDiff.Rows)
|
||||
{
|
||||
for (int i = 0; i < row.Value; i++)
|
||||
WriteRow(writer, row.Key);
|
||||
}
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
internal static void WriteRow(XmlTextWriter writer, string rowDiff)
|
||||
{
|
||||
writer.WriteStartElement("row");
|
||||
string[] columns = rowDiff.Split(new string[] { Row.DELIMITER }, StringSplitOptions.None);
|
||||
|
||||
writer.WriteAttributeString("type", columns[0]);
|
||||
for (int i = 1; i < columns.Length; i++)
|
||||
WriteColumn(writer, columns[i]);
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
internal static void WriteColumn(XmlTextWriter writer, string column)
|
||||
{
|
||||
string[] definition = column.Split(new string[] { Column.DELIMITER }, StringSplitOptions.None);
|
||||
|
||||
writer.WriteStartElement("column");
|
||||
writer.WriteAttributeString("name", definition[0]);
|
||||
writer.WriteAttributeString("value", definition[1].Replace("<", LEFT).Replace(">", RIGHT));
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user