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

| 我有一个带有许多ASCX控件的ASPX大页面。如果控件引发异常,则应记录该异常并仅隐藏自身。所有其他控件仍应呈现。 如何处理从前端文件引发的单个ASCX的异常(ASCX,而不是背后的代码)?例如:试图使用
<%= MethodThatThrowsANullReferenceException() %>
语法引用无效属性的控件。 显然,在Global.asax中使用通用错误处理程序方法无法解决问题。我需要处理单个控件上的异常。     
已邀请:
使您的所有UserControls都从自定义基类继承,例如:
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.\");
        }
    }
}
    
我尝试覆盖Render方法,但这并不涵盖所有异常。 例如,如果在Page_Init,Load或Render期间引发某种异常,则将阻止页面呈现。 我们有不同的人员在不同的模块(控件)上工作,这些模块可以加载到单个页面中,但是我对每个模块的代码质量不承担任何责任,因此即使不是最佳实践,我也需要捕获异常并确定哪个控件无法加载,因为应用程序不能仅仅因为一个模块就失败了。 对于当今并不常见的这种特殊情况,自定义,应用程序或页面错误处理都无法正常工作。 我提出的解决方案是: 每个模块(Control.ascx)需要加载到Page(aspx)时,都包含在ModuleShell中,该Shell具有一些特定功能,并负责帮助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;
    }
模块是用于存储会话变量的静态类。 CurrentState是ModuleShell用来记录其名称的变量。 我们唯一获得的aspx中的Page_Error,将获得尝试加载的最后记录的ModuleShell。由于任何异常都会停止页面呈现,因此最后一个将其名称记录到主页面的ModuleShell可能是无法正确加载的页面。 这是一个草率的解决方案,但对模块开发人员是透明的。     
AFAIK,这是不可能的(至少以一种简单的方式)。 使用ASP.NET的丰富自定义错误处理:   当错误发生时,例外是   提出或抛出。有三种   可能会诱捕和处理的层   例外:   
try...catch...finally
街区   
Page
级或or5ѭ   水平。前两个发生的正确   在页面的代码中,以及   应用程序事件保存在内部   
global.asax
。      
Exception
对象包含   有关错误的信息,以及   事件在整个过程中冒泡   层,它进一步包裹   详情。粗略地说,   
Application_Error
例外包含   
Page_Error
例外   在ѭ7的基础上扩展,   在第一时间触发了起泡   地点。 如果用户控件内发生异常,则将其捕获到用户控件内的唯一方法是在
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
。但是不幸的是,随后引发了未处理的异常,页面处理停止,并且错误处理开始了。这意味着页面的渲染也将停止(因此,您不会在导致此异常的控件旁边看到控件)。     
您可以将尝试调用的方法包装在自己的方法中,该方法将返回相同的类型,但带有
try {} catch {}
块。
public string MethodWrapper()
{
    try
    {
         return MethodThatCanThrowException();
    }
    catch (SomeExceptionType)
    {
         //log exception
         return string;
    }
}
    
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
这样可以解决任何前端渲染问题。如果父页面希望显示一个不错的错误消息,则可以处理ControlCrashed事件。     

要回复问题请先登录注册