我有一个包含许多ASCX控件的大型ASPX页面。如果控件抛出异常,它应该记录异常并仅隐藏自身。所有其他控件仍应呈现。
如何处理从前端文件(ASCX而不是代码隐藏)引发的单个ASCX的异常?例如:控件试图使用<%= MethodThatThrowsANullReferenceException() %>
语法引用无效属性。
显然,使用Global.asax中的通用错误处理程序方法无法解决问题。我需要处理各个控件的异常。
答案 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
这可以捕获任何前端渲染问题。如果父页面希望显示一个很好的错误消息,它可以处理它。