ASP.NET 中的有效调试






3.64/5 (4投票s)
一种在 ASP.NET 应用程序中进行调试的简单而有效的方法
引言
有效的调试是有效软件开发的关键部分。如果你没有意识到这一点,甚至不欣赏软件开发中的这个基本原理,这篇文章对你帮助不大。如果你仍在阅读,本文旨在为调试 ASP.NET 中对象的内容提供一种简单而强大的方法(而不是现成的解决方案)。实际上,你可以通过简单地使用另一种类型的控件/调试逻辑,将这种方法用于任何其他语言或 .NET 领域。
背景
通常,在代码中,你会有一个对象,其中包含你希望在特定运行时或页面加载时查看的内容。通常,你认为某些数据结构保存着特定值,但你的程序行为却告诉你你的假设是错误的,但你无法看到它,因为你没有适当的方法来查看这些数据结构中的内容。
方法
该方法包括使用一个简单的控件,该控件呈现 `string` 的内容,该 `string` 恰好是由专用调试器类生成的格式良好的 HTML `string`,其中包含将自定义对象(DataSets、DataReaders、YourFacyCoolTypes)的内容转换为简单的 HTML 用户友好呈现 `string` 的逻辑。
代码
该代码仅由一个简单的控件组成,你可以简单地复制粘贴到你的 *App_Code* 文件夹中(并可能重命名命名空间 ;)。 这是它
//For docs, read the comments. For legal disclaimer, see the bottom of this file
using System;
using System.Web;
namespace GenApp.Gui.Controls
{
/// <summary>
/// A simple control to present debug data.
/// Note it inherits the Control class
/// </summary>
public class DebugDumper : System.Web.UI.Control
{
#region Text
/// <summary>
/// the debugging text to present
/// </summary>
private string _Text;
public string Text
{
get
{
//use ViewState if you want also
return System.Convert.ToString(
HttpContext.Current.Session["global." +
this.ID + "Text"]);
} //eof get
set
{
_Text = value;
HttpContext.Current.Session["global." +
this.ID + "Text"] = value;
}
} //eof prop
#endregion Text
public DebugDumper(string msg, string debugString)
{
this.Text = " " + msg + " " + debugString;
}
/// <summary>
/// Renders the contents of the control
/// by using the debug string passed on creation
/// </summary>
/// <param name="writer">The <see cref="HtmlTextWriter"/> to write to.</param>
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
//todo: add here logging according to log level
writer.Write("<div><p>");
string echo = this.Text ?? " null debug string passed " ;
writer.Write( echo );
writer.Write("</p></div>");
base.Render(writer);
} //eof method
} //eof class
} //eof namespace
//Legal disclaimer:
//You could do anything you want with this code.
//Everything is on your own responsibility.
//All trademarks belong to their respective owners.
示例 HtmlDebugger 类
现在的关键是要意识到,这里我正在使用我的 `HtmlDebugger` 类,该类“知道”如何调试 `DataSet` 对象、`IDataReader` 对象以及我的应用程序的自定义列表,其中包含一个自定义 `Msg` 对象,仅用于演示目的。(复制粘贴时删除 `DebugMsgList`,因为你不会知道我的应用程序的 `Msg` 类是什么)。很可能你确实有自己的调试类,这些类将特定对象作为参数并返回显示其内容的 `string`。一个很好的例子是 Piero Viano 的 Object Dumper。很可能你的调试类可以更好地调试你的应用程序中的对象,但为了本文的目的,我必须展示我的 `HtmlDebugger` 类
using System;
using System.Text.RegularExpressions;
using System.Data;
using System.Collections.Specialized;
using System.Text;
namespace GenApp.Utils.Log
{
/// <summary>
///Debugs passed objects and returns ready formatted HTML with the objects values
/// </summary>
public class HtmlDebugger
{
public static string DebugDataSet(string msg, DataSet ds)
{
StringBuilder sb = new StringBuilder();
sb.Append("<p> START " + msg + "</p>");
if (ds == null)
return msg + " null ds passed ";
if (ds.Tables == null || ds.Tables.Count == 0)
return msg + " no tables in ds ";
sb.Append("<p> DEBUG START --- " + msg + "</p>");
foreach (System.Data.DataTable dt in ds.Tables)
{
sb.Append("================= My TableName is " +
dt.TableName + " ========================= START");
sb.Append("<table>\n");
int colNumberInRow = 0;
foreach (System.Data.DataColumn dc in dt.Columns)
{
sb.Append(" <th> ");
sb.Append(" |" + colNumberInRow + "| ");
sb.Append(dc.ColumnName + " </th> ");
colNumberInRow++;
} //eof foreach (DataColumn dc in dt.Columns)
int rowNum = 0;
foreach (System.Data.DataRow dr in dt.Rows)
{
string strBackGround = String.Empty;
if (rowNum% 2 == 0)
strBackGround = " bgcolor=\"#D2D2D2\" ";
sb.Append("\n " + rowNum + "<tr " + strBackGround + " >");
int colNumber = 0;
foreach (System.Data.DataColumn dc in dt.Columns)
{
sb.Append("<td> |" + colNumber + "| ");
sb.Append(dr[dc].ToString() + " </td>");
colNumber++;
} //eof foreach (DataColumn dc in dt.Columns)
rowNum++;
sb.Append("</tr>");
} //eof foreach (DataRow dr in dt.Rows)
sb.Append(" \n");
sb.Append("</table>");
} //eof foreach (DataTable dt in sb.Append.Tables)
sb.Append("<p> DEBUG END--- " + msg + "</p>");
return sb.ToString();
}//eof method
public static string DebugMsgList
(string msg, System.Collections.Generic.List<GenApp.Dh.Msg> listMsgs)
{
System.Text.StringBuilder echo = new System.Text.StringBuilder();
if (listMsgs == null)
return "null listMsgs passed for debugging ";
if (listMsgs.Count == 0)
return "listMsgs.Count == 0";
echo.Append("<table>");
for (int msgCounter = 0; msgCounter < listMsgs.Count; msgCounter++)
{
GenApp.Dh.Msg objMsg = listMsgs[msgCounter];
string strBackGround = String.Empty;
if (msgCounter % 2 == 0)
strBackGround = " bgcolor=\"#D2D2D2\" ";
echo.Append("<tr" + strBackGround + ">");
echo.Append("<td>msg.MsgKey</td> <td> " + objMsg.MsgKey + "</td>");
echo.Append("<td>msg.MsgId</td><td>" + objMsg.MsgId + "</td>");
echo.Append("</tr>");
} //eof foreach
echo.Append("</table>");
return echo.ToString();
} //eof method
public static string DebugIDataReader(string msg, IDataReader rdr)
{
StringBuilder sb = new StringBuilder();
if (rdr == null)
return " <p> IDataReader rds is null </p>";
sb.Append("DEBUG START ---" + msg);
sb.Append("<table>");
int counter = 0;
while (rdr.Read() )
{
string strBackGround = String.Empty;
if (counter % 2 == 0)
strBackGround = " bgcolor=\"#3EBDE8\" ";
sb.Append("<tr" + strBackGround + ">");
for (int i = 0; i < rdr.FieldCount; i++)
{
sb.Append("<td>");
sb.Append(rdr[i].ToString() + " ");
sb.Append("<td>");
} //eof for
sb.Append("</br>");
sb.Append("</tr>");
counter++;
}
sb.Append("<table>");
sb.Append("DEBUG END ---" + msg);
return sb.ToString();
} //eof method
public static string DumpListDictionary(string msg , ListDictionary list)
{
if (list == null)
return "<p> null list passed </p>";
if (list.Count == 0)
return "<p> list.Count = 0 </p> ";
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("<p> START DUMP " + msg + " </p>");
sb.Append("<table>");
int counter = 0;
foreach (object key in list.Keys)
{
string strBackGround = String.Empty;
if (counter % 2 == 0)
strBackGround = " bgcolor=\"#D2D2D2\" ";
sb.Append("<tr" + strBackGround + "><td> key - </td><td> " + key.ToString());
sb.Append("</td><td>===</td><td>value - </td><td> " + list[key] + "</td></br></tr>");
counter++;
} //eof foreach
sb.Append("</table>");
sb.Append("<p> END DUMP " + msg + " </p>");
return sb.ToString();
} //eof method
} //eof class
} //eof namespace
Using the Code
你可以按照以下方式使用页面代码隐藏中的代码
protected override void CreateChildControls()
{
base.CreateChildControls();
//... Populating the dataset object 2
panTxtHolder.Controls.Add(
new GenApp.Gui.Controls.DebugDumper("some debug msg ",
GenApp.Utils.Log.HtmlDebugger.DebugDataSet(
"another debug msg", dsForUserDetails)));
} //eof method
这里 `panTextHolder` 是我已经在此代码隐藏页面的页面中填充的 `Panel`。
`dsForUserDetails` 是我想在调用期间调试的 `DataSet`。
关注点
如果能实现一个控件,该控件位于站点母版页的顶部,并具有 true/false 单选按钮来查看调试信息以及相应的事件处理程序,以便有权查看调试信息的用户能够在常规视图和调试视图之间切换,那就太好了。我还想知道 `OnDataBind` 是否是实现将文本添加到控件的更好位置... 如果你有一些想法,请在下面评论。如果能基于这种方法实现自定义 log4net appender,那就太好了。如果这能由动态日志记录设置触发(例如,当应用程序处于生产环境中并且出现严重错误时,管理员可以模拟遇到问题的人,动态启用调试,并为维护该应用程序的软件开发人员提供有价值的信息),那就更好了。如果你已经实现了类似的东西,我很高兴听到你解决这个问题的方法。
历史
- 2009 年 6 月 29 日:首次发布