65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 中的有效调试

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.64/5 (4投票s)

2009年6月29日

BSD

3分钟阅读

viewsIcon

15663

一种在 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 日:首次发布
© . All rights reserved.