Как поймать исключение, возникшее в моем элементе управления ASCX (не в коде)? - PullRequest
2 голосов
/ 14 апреля 2011

У меня есть большая страница ASPX со многими элементами управления ASCX.Если элемент управления генерирует исключение, он должен регистрировать исключение и скрывать только себя.Все остальные элементы управления должны по-прежнему визуализироваться.

Как мне обрабатывать исключения для отдельных ASCX, поднятых из файла переднего плана (ASCX, а не код позади)?Например: элемент управления пытается сослаться на недопустимое свойство с использованием синтаксиса <%= MethodThatThrowsANullReferenceException() %>.

Очевидно, что использование общего метода обработки ошибок в Global.asax не решит проблему.Мне нужно обработать исключения для отдельных элементов управления.

Ответы [ 5 ]

4 голосов
/ 14 апреля 2011

Сделайте все ваши 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.");
        }
    }
}
2 голосов
/ 08 января 2013

Я попытался переопределить метод Render, но он не охватывает все исключения.

Например, если во время Page_Init, Load или Render генерируется какое-то исключение, это предотвратит отображение страницы.

У нас есть разные люди, работающие над разными модулями (элементами управления), которые могут быть загружены на одну страницу, но я не отвечаю за качество кода каждого модуля, поэтому даже если это не лучшая практика, мне нужно было перехватывать исключения и определять, какой элемент управления не загружается, потому что приложение не может завершиться сбоем только потому, что один модуль не работает.

Для этого конкретного сценария, который не так уж редок в наше время, ни пользовательская, ни прикладная, ни обработка ошибок страницы не будут работать хорошо.

Решение, которое я придумал, было:

Каждый модуль (Control.ascx), когда его необходимо загрузить на страницу (aspx), содержится в 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;
    }

Модули - это статический класс, используемый для хранения переменных сеанса. CurrentState - это переменная, которую ModuleShell использует для записи своих имен.

Page_Error, расположенная в единственном aspx, который мы получили, получит последний записанный ModuleShell, который пытался загрузить. Так как любое исключение прекратит рендеринг страницы, последняя из которых ModuleShell записала свое имя на главную страницу, вероятно, она не была загружена должным образом.

Это небрежное решение, но оно прозрачно для разработчика модулей.

1 голос
/ 14 апреля 2011

AFAIK, это невозможно (по крайней мере, простым способом).

Обработка пользовательских ошибок в ASP.NET :

Когда происходят ошибки, исключение поднят или брошен. Есть три слои, на которых вы можете ловить и иметь дело за исключением: в try...catch...finally блок, на Page уровень или на Application уровень. Первые два случаются прямо внутри кода страницы и кода для события приложения хранятся внутри global.asax.

Объект Exception содержит информация об ошибке, а так же событие вспыхивает через слои, это обернуто в дальнейшем подробно. Грубо говоря, Application_Error исключение содержит исключение Page_Error, которое расширяется на базе Exception, что вызвало пузырение в первом место.

Если внутри пользовательского элемента управления возникло исключение, единственный способ перехватить его внутри пользовательского элемента управления - обработать его внутри блока try { } catch { }.

Я думаю, что самый низкий уровень, когда может быть поймано исключение, подобное следующему - Page_Error level вот так:

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. Но, к сожалению, тогда возникает исключение необработанное , обработка страниц останавливается, и вместо этого запускается обработка ошибок. Это означает, что рендеринг страницы также будет остановлен (поэтому вы не увидите элементы управления рядом с тем, что вызвало это исключение).

0 голосов
/ 14 апреля 2011

Один вариант, предложенный Джимом Боллой, заключался в том, чтобы все элементы управления наследовали от одного базового класса и использовать Try / Catch в методе Render. Это бы сработало. К сожалению, многие элементы управления, с которыми я имею дело, уже имеют разные базовые классы.

Это решение сработало для меня:

Я добавил следующий код для каждого пользовательского элемента управления (я уверен, что это может быть изменено, чтобы уменьшить дублирование):

#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, если она хочет отобразить красивое сообщение об ошибке.

0 голосов
/ 14 апреля 2011

Вы можете заключить метод, который вы пытаетесь вызвать, в свой собственный метод, который будет возвращать тот же тип, но с блоком try {} catch {}.

public string MethodWrapper()
{
    try
    {
         return MethodThatCanThrowException();
    }
    catch (SomeExceptionType)
    {
         //log exception
         return string;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...