By Damjan Kovac
How to build XML string with only changed rows/columns edit on user form?
Formerly I've been working on three-tier applications on J2EE platform. All aplication logic was written in EJB and
all the data was send as XML string from PowerBuilder clients. I was thinking how to implement such kind of
solution in .NET through ADO.NET. The most simple way is to send data as DataSet object which also includes XML
represetation of containing data objects (datatables, datacolumns, etc.). But sending the whole dataset has
too much overhead. We only want to send changed/modified data to middle-tier .NET component. How to do this in a
efficient manner?
The basic idea is to send only changed rows/columns of DataTable object. The method below takes two parameters:
- DataTable table: only changed rows from input form/datagrid
- string keyColName: name of key column of table
We use three different statuses for row tag:
- "INSERT": new added row
- "UPDATE": updated row
- "DELETE": deleted row
/// <summary>
/// Returns XML string of changed DataTable rows
/// </summary>
public string GetXML(DataTable table, string keyColName) {
DataView ldvOriginal = new DataView(table);
DataView ldvChanged = new DataView(table);
DataView ldvAdded = new DataView(table);
DataView ldvDeleted = new DataView(table);
ldvOriginal.RowStateFilter = DataViewRowState.ModifiedOriginal;
ldvChanged.RowStateFilter = DataViewRowState.ModifiedCurrent;
ldvAdded.RowStateFilter = DataViewRowState.Added;
ldvDeleted.RowStateFilter = DataViewRowState.Deleted;
string lsXml = "<?xml version=\"1.0\"?>\n";
string lsStatusInsert = "<row status=\"INSERT\">\n";
string lsStatusUpdate = "<row status=\"UPDATE\">\n";
string lsStatusDelete = "<row status=\"DELETE\">\n";
int noAdded = ldvAdded.Count;
int noChanged = ldvChanged.Count;
int noDeleted = ldvDeleted.Count;
try {
// added rows
lsXml += "<" + table.TableName + ">\n";
for (int i=0; i < noAdded; i++) {
lsXml += lsStatusInsert;
foreach (DataColumn dc in ldvAdded.Table.Columns) {
if (ldvAdded[i][dc.ColumnName].ToString().Length > 0)
lsXml += "<" + dc.ColumnName + ">" +
ldvAdded[i][dc.ColumnName] +
"<" + dc.ColumnName + ">\n";
} // foreach column
lsXml += "</row>\n";
} // for
// modified rows
for (int i=0; i < noChanged; i++) {
lsXml += lsStatusUpdate;
foreach (DataColumn dc in ldvChanged.Table.Columns) {
if (keyColName.Equals(dc.ColumnName))
lsXml += "<" + dc.ColumnName + ">" +
ldvChanged[i][dc.ColumnName] +
"<" + dc.ColumnName + ">\n";
else
if (!ldvOriginal[i][dc.ColumnName].ToString().Equals(ldvChanged[i][dc.ColumnName].ToString()))
lsXml += "<" + dc.ColumnName + ">" +
ldvChanged[i][dc.ColumnName] +
"<" + dc.ColumnName + ">\n";
} // foreach column
lsXml += "</row>\n";
} // for
// deleted rows
for (int i=0; i < noDeleted; i++) {
lsXml += lsStatusDelete;
lsXml += "<" + keyColName + ">" + ldvDeleted[i][keyColName] + "<" + keyColName + ">\n";
lsXml += "</row>\n";
} // for
lsXml += "<" + table.TableName + ">\n";
} catch (Exception ex) {
throw ex;
}
return lsXml;
}
...
Example:
DataTable dt = new DataTable("DemoTable");
// we fill this table through DataAdapter Fill() method
// and we edit data in user control such as DataGrid, form,..
..
if (dt.GetChanges()!=null) {
string xml = this.GetXML(dt.GetChanges(), "customer_id");
// pass xml to middle-tier..
}
Method returns XML string which can be passed to middle tier. Here we parse it and do all necessary
bussiness logic due to row status. We do not need XSD because we can get metadata in the middle tier
component directly from database table.