如何捕获ASCX控件上引发的异常(不是代码隐藏)?

时间:2011-04-14 12:40:17

标签: asp.net user-controls error-handling

我有一个包含许多ASCX控件的大型ASPX页面。如果控件抛出异常,它应该记录异常并仅隐藏自身。所有其他控件仍应呈现。

如何处理从前端文件(ASCX而不是代码隐藏)引发的单个ASCX的异常?例如:控件试图使用<%= MethodThatThrowsANullReferenceException() %>语法引用无效属性。

显然,使用Global.asax中的通用错误处理程序方法无法解决问题。我需要处理各个控件的异常。

5 个答案:

答案 0 :(得分:4)

使所有UserControl继承自定义基类,如:

public class CustomUserControl : UserControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        try
        {
            base.Render(writer);
        }
        catch (Exception e)
        {
            writer.Write("Could not load control. Sad face.");
        }
    }
}

答案 1 :(得分:2)

我尝试重写Render方法,但这并不涵盖所有异常。

例如,如果在Page_Init,Load或Render期间抛出某种异常,这将阻止页面呈现。

我们有不同的人在处理可以加载到单个页面的不同模块(控件),但我不负责每个模块的代码质量,所以即使它不是最佳实践,我需要捕获异常并确定哪个控件无法加载,因为应用程序只能因为一个模块而失败。

对于现在不那么罕见的特定情况,自定义,应用程序或页面错误处理都不会很好。

我提出的解决方案是:

需要加载到Page(aspx)中的每个模块(Control.ascx)都包含在ModuleShell中,该模块将包含一些特定功能,并将负责帮助Page_Error处理正常工作。

此ModuleShell不是试图捕获失败的子控件的异常,而是在每个生命周期阶段监视它是否设法正确加载。

以下是它的片段:

    protected void Page_Init(object sender, EventArgs e)
    {
        Modules.CurrentState = _mod;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        Modules.CurrentState = _mod;
    }

    protected void Page_PreRender(object sender, EventArgs e)
    {
        Modules.CurrentState = _mod;
    }

Modules是一个用于存储会话变量的静态类。 CurrentState是ModuleShell用来记录其名称的变量。

位于我们得到的唯一aspx中的Page_Error将获得最后记录的试图加载的ModuleShell。由于任何异常将停止页面呈现,最后一个ModuleShell将其名称记录到主页面,它可能是无法正确加载的那个。

这是一个草率的解决方案,但它对模块开发人员来说是透明的。

答案 2 :(得分:1)

AFAIK,这是不可能的(至少以一种简单的方式)。

Rich Custom Error Handling with ASP.NET

  

发生错误时,会出现异常   提出或抛出。有三种   你可以陷阱和交易的层次   有一个例外:在一个   try...catch...finally阻止,在   Page级别,或Application   水平。前两个发生了   在页面的代码和代码中   应用程序事件保存在里面   global.asax

     

Exception对象包含   有关错误的信息,以及   事件冒泡了   层,它进一步包裹   详情。粗略地说,   Application_Error例外包含   Page_Error例外,其中   扩展基础Exception,其中   在第一次引发冒泡   的地方。

如果用户控件内发生异常,唯一能够捕获内部的用户控件是在try { } catch { }块内处理它。

我认为最低级别可以捕获这样的异常是下一个 - Page_Error 级别,如下所示:

protected void Page_Error(object sender, EventArgs e)
{
    // the control which throw an exception
    var control = (Control)sender;
    control.Visible = false;

    // the exception itself
    var exception = Server.GetLastError();
    Context.ClearError();
}

Context.ClearError()方法甚至可以阻止异常进一步冒泡到Application_Error。但不幸的是,然后抛出未处理的异常,页面处理停止并启动错误处理。这意味着页面渲染也将停止(因此您不会看到导致此异常的控件旁边的控件)。

答案 3 :(得分:0)

您可以在自己的方法中包装您尝试调用的方法,该方法将返回相同的类型,但使用try {} catch {}块。

public string MethodWrapper()
{
    try
    {
         return MethodThatCanThrowException();
    }
    catch (SomeExceptionType)
    {
         //log exception
         return string;
    }
}

答案 4 :(得分:0)

Jim Bolla建议的一个选项是使所有控件都从相同的基类继承并在Render方法中使用Try / Catch。这本来有用。不幸的是,我正在处理的许多控件已经有了不同的基类。

这个解决方案对我有用:

我在每个用户控件中添加了以下代码(我确信可以进一步重构以减少重复):

#region Error Handling

public event EventHandler ControlCrashed;
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

protected override void RenderChildren(HtmlTextWriter writer)
{
    try
    {
        base.RenderChildren(writer);
    }
    catch (Exception exc)
    {
        Logger.Error("Control failed to load. Hiding control. Message: " + exc, exc);
        //Ignore and hide the control.
        this.Visible = false;
        if (ControlCrashed != null)
            ControlCrashed(this, EventArgs.Empty);
    }
}

#endregion

这可以捕获任何前端渲染问题。如果父页面希望显示一个很好的错误消息,它可以处理它。