ASP.NET: Modifying DetailsView Select and Updating Events

Recently I was tasked with encrypting/decrypting some data that could be edited using ASP.NET’s DetailsView control.  Along with the details view control, the legacy code used a SQL Data Source to select and update everything. This means the data access layer was bypassed completely, so any business logic that dealt with encryption on this page had to use a different execution path. So, how would you encrypt and decrypt data?

The problem could be divided into two tasks:

  1. Create a filter to decrypt data in any bound columns.
  2. Edit the DetailsView event, ItemUpdating and figure out which columns needed to be updated during the event.

The first task was the easier of the two, as it just involved referencing the encryption library from the code behind page and calling the function inline in the aspx page. So, the method in the code behind page would look like something like this:

protected string Decrypt(object input)
{
return EncryptionHelper.Decrypt(input.ToString());
}

While calling the decrypt function in the DetailsView child  Fields tags on the aspx page:

<asp:templatefield headertext="Decrypted Data" sortexpression="EncryptedData>
<edititemtemplate>
<asp:TextBox ID="TextBox1" runat="Server" Text='<%#Decrypt(Eval("EncryptedData"))%>' />
</edititemtemplate>
<itemtemplate>
<asp:label runat="server" text='<%# Decrypt(Eval("EncryptedData")) %>' id="Label1"></asp:label>
 </itemtemplate>
</asp:templatefield>

However, decrypting the data using a templatefield tag posed another problem, extracting new values from the ‘TextBox1’ control. This will be solved when calling the ItemUpdating event.

After setting the DetailsView’s ItemUpdating method, you will need to modify the SQL Data Source’s UpdateCommand, UpdateCommandType, and UpdateParameters. The legacy code in the SQL data source used a update statement with parameters. Normally the details view control automatically detects whether to pass a null in the parameter. Instead when  setting the UpdateParameters in the codebehind, the control will update the database with the parameter names entered. For example ‘@Parameter’, will be sent in the update to the record instead of null. To solve this, you would need to automatically detect whether a value is null and then add it to the UpdateParameters list. This was done using the ‘IsFieldNull’ method [shown below].

Initializing the SQL data source also had another problem – because certain data needed to be re-encrypted before being sent back to the database. You will need to single out any columns that need to be encrypted. On top of that, the table being updated had over 50 columns, so typing in 50+ lines just to initialize the parameters was required, but I decided to do something else instead.

The parameters being sent to the stored procedure were identically named to the column names. Using this protocol, I decided to simply grab all the field names, which were identical to the column names and add the ‘@’ character next during initialization. So, the method to get the column names looked like the following:

private List<string> GetColumnNameList()
{
List<string> Ret = new List<string>();

for (int i = 0; i < DetailsView.Rows.Count; i++)
{
if (!String.IsNullOrEmpty(DetailsView.Rows[i].Cells[0].Text))
{
Ret.Add(DetailsView.Rows[i].Cells[0].Text);
}
}
return Ret;
}

Initializing the update parameters looked like the following:

private void InitSqlDataSource()
{
SQLDataSource.UpdateCommand = "Update";
SQLDataSource.UpdateParameters.Clear();

List<string> paramList = GetColumnNameList();

foreach (string col in paramList)
{
if (!IsFieldNull(col))
{
if (col != "EncryptedData")
{
SQLDataSource.UpdateParameters.Add(col, "@" + col);
}
else
{
SQLDataSource.UpdateParameters.Add("EncrptedData",
EncryptionHeper.Encrypt(GetDecryptedValue()));
}
}
}
}

The method checks to make sure the data being sent isn’t blank or null by executing the ‘IsFieldNull’ method. This method uses the ‘ExtractValuesFromCell’ method to grab values from the DetailsView control. This method isn’t exactly well documented on MSDN, so it’s not obvious at first that the passed parameter, IOrderedDictionary dictionary, is being used as a referenced parameter, not as a copy. [another weird quirk about the .NET framework] The method, GetValues, used is identical to what is covered at the article written at David Fowler’s blog.

private bool IsFieldNull(string fieldName)
{
OrderedDictionary vals = GetValues(DetailsView) as OrderedDictionary;

if (vals[fieldName] == null)
{
return true;
}

return string.IsNullOrEmpty(vals[fieldName].ToString());
}

public IDictionary GetValues(DetailsView detailsView)
{
IOrderedDictionary values = new OrderedDictionary();

foreach (DetailsViewRow row in detailsView.Rows)
{
if (row.RowType != DataControlRowType.DataRow)
{
continue;
}

DataControlFieldCell dataCell = (DataControlFieldCell)row.Cells[0];

if (dataCell.ContainingField.ShowHeader)
{
dataCell = (DataControlFieldCell)row.Cells[1];
}

dataCell.ContainingField.ExtractValuesFromCell(values, dataCell, row.RowState, true);
}

return values;
}

Leave a Reply