Перехват необработанных исключений в ASP.NET UserControls - PullRequest
5 голосов
/ 14 августа 2008

Я динамически загружаю пользовательские элементы управления, добавляя их в коллекцию элементов управления веб-формы.

Я хотел бы скрыть пользовательские элементы управления, если они вызывают необработанное исключение при рендеринге.

Итак, я попытался подключиться к событию Error каждого UserControl, но кажется, что это событие никогда не срабатывает для UserControls, как для класса Page.

Пытался немного погуглить, и это не кажется многообещающим. Есть идеи?

Ответы [ 7 ]

12 голосов
/ 15 августа 2008

mmilic, после ваш ответ на мою предыдущую идею ..

Никакой дополнительной логики не требуется! В том-то и дело, что вы ничего не делаете с этими классами, просто заключаете их в какую-то инстанцированную пузырчатую пленку! :)

Ладно, я собирался просто поставить точку, но я хотел увидеть эту работу для себя, поэтому я собрал немного очень грубого кода, но концепция есть, и, кажется, работает.

Извинения за длинный пост

SafeLoader

По сути, это будет тот «пузырь», о котором я упоминал. Он получит элементы управления HTML, улавливающие любые ошибки, возникающие во время рендеринга.

public class SafeLoader
{
    public static string LoadControl(Control ctl)
    {
        // In terms of what we could do here, its down
        // to you, I will just return some basic HTML saying
        // I screwed up.
        try
        {
            // Get the Controls HTML (which may throw)
            // And store it in our own writer away from the
            // actual Live page.
            StringWriter writer = new StringWriter();
            HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
            ctl.RenderControl(htmlWriter);

            return writer.GetStringBuilder().ToString();
        }
        catch (Exception)
        {
            string ctlType = ctl.GetType().Name;
            return "<span style=\"color: red; font-weight:bold; font-size: smaller;\">" + 
                "Rob + Controls = FAIL (" + 
                ctlType + " rendering failed) Sad face :(</span>";
        }
    }
}

И некоторые элементы управления ..

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

BadControl

public class BadControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        throw new ApplicationException("Rob can't program controls");
    }
}

GoodControl

public class GoodControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        writer.Write("<b>Holy crap this control works</b>");
    }
}

Страница

ОК, так что давайте посмотрим на «тестовую» страницу. Здесь я просто создаю экземпляры элементов управления, беру их html и выводю их, я буду следить за мыслями о поддержке дизайнеров и т. Д.

Код страницы за

    protected void Page_Load(object sender, EventArgs e)
    {
        // Create some controls (BadControl will throw)
        string goodHtml = SafeLoader.LoadControl(new BadControl());
        Response.Write(goodHtml);

        string badHtml = SafeLoader.LoadControl(new GoodControl());
        Response.Write(badHtml);
    }

Мысли

Хорошо, я знаю, о чем вы думаете: «Эти элементы управления создаются программно, а как насчет поддержки дизайнера? Я потратил чертовы часы на то, чтобы сделать эти элементы управления приятными для дизайнера, теперь вы возитесь с моим моджахедом».

ОК, так что я пока еще не проверял это (вероятно, сделаю это через минуту!), Но идея здесь в том, чтобы переопределить метод CreateChildControls для страницы, взять экземпляр каждого элемента управления, добавленного в форму, и запустить его через SafeLoader. Если код проходит, вы можете добавить его в коллекцию Controls как обычно, если нет, то вы можете создавать ошибочные литералы или что-то еще, до вас, мой друг.

Наконец ..

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

Обновление

Протестировано путем добавления элемента управления в конструктор и переопределения метода CreateChildControls с этим, работает нормально, может потребоваться некоторая очистка, чтобы улучшить внешний вид, но я оставлю это вам;)

protected override void CreateChildControls()
{
    // Pass each control through the Loader to check
    // its not lame
    foreach (Control ctl in Controls)
    {
        string s = SafeLoader.LoadControl(ctl);
        // If its bad, smack it downnnn!
        if (s == string.Empty)
        {
            ctl.Visible = false; // Prevent Rendering
            string ctlType = ctl.GetType().Name;
            Response.Write("<b>Problem Occurred Rendering " + 
                ctlType + " '" + ctl.ID + "'.</b>");
        }
    }
}

Наслаждайтесь!

4 голосов
/ 14 августа 2008

Это интересная проблема. Я все еще довольно свеж, когда дело доходит до пользовательских элементов управления и т. Д., Но вот мои мысли (не стесняйтесь комментировать / исправлять людей!) .. (Я вроде думаю / пишу вслух здесь !)

  • Если во время рендеринга произойдет ошибка, не будет ли это слишком поздно? (поскольку некоторые элементы управления HTML, возможно, уже были отправлены в Writer и выведены).
  • Следовательно, было бы не лучше обернуть метод Render пользовательского элемента управления, но вместо того, чтобы передавать ему ссылку на «Live» HtmlTextWriter, вы передаете свой собственный, перехватывая любые исключения, возникающие в этом маленьком «безопасном пузыре», если все идет хорошо, затем вы передаете полученный HTML-код фактическому HtmlTextWriter?
  • Эта логика, вероятно, может быть передана универсальному классу-обертке, который вы будете использовать для динамической загрузки / рендеринга элементов управления во время выполнения.
  • Если какие-либо ошибки происходят, у вас есть вся необходимая информация! (т.е. контрольные ссылки и т. д.).

Только мои мысли, пламя прочь! : D;)

1 голос
/ 14 августа 2008

В зависимости от того, где происходят ваши ошибки, вы можете сделать что-то вроде ...

public abstract class SilentErrorControl : UserControl
{
    protected override void Render( HtmlTextWriter writer )
    {
        //call the base's render method, but with a try catch
        try { base.Render( writer ); }
        catch ( Exception ex ) { /*do nothing*/ }
    }
}

Затем наследуйте SilentErrorControl вместо UserControl.

0 голосов
/ 08 июня 2012

Я использовал подход @ Keith, но проблема в том, что элемент управления обрабатывается до тех пор, пока не будет сгенерировано исключение, что может привести к открытию HTML-тегов. Я также отображаю информацию об исключении в элементе управления, если в режиме отладки.

<code>  protected override void Render(System.Web.UI.HtmlTextWriter writer)
  {
     try
     {
        // Render the module to a local a temporary writer so that if an Exception occurs
        // the control is not halfway rendered - "it is all or nothing" proposition
        System.IO.StringWriter sw = new System.IO.StringWriter();
        System.Web.UI.HtmlTextWriter htw = new System.Web.UI.HtmlTextWriter(sw);
        base.Render(htw);

        // We made it!  Copy the Control Render over
        writer.Write(sw.GetStringBuilder().ToString());
     }
     catch (System.Exception ex)
     {
        string message = string.Format("Error Rendering Control {0}\n", ID);
        Log.Error(message, ex);
        if (Page.IsDebug)
           writer.Write(string.Format("{0}<br>Exception:<br><pre>{1}\n{2}
", message, ex.Message, ex.StackTrace)); } }
0 голосов
/ 20 августа 2008

Я не уверен, что понимаю ваш ответ .. Как вы загружаете свои элементы управления и добавляете их в свою коллекцию элементов управления?

В этом весь смысл бита, добавленного в раздел «Обновление». У вас есть возможность использовать SafeLoader где угодно.

Я не уверен, почему вы чувствуете, что не имеете доступа / контроля над Html? Цель SafeLoader состоит в том, что вы не заботитесь , что такое html, вы просто пытаетесь «вывести» элемент управления (внутри «пузыря») и определить, загружается ли он в текущем состоянии. *

Если это так (то есть возвращается html), вы можете делать с ним все что угодно, выводить html, добавлять элемент управления в коллекцию элементов управления, что угодно!

Если нет, то, опять же, вы можете делать то, что вам нравится, выдавать сообщение об ошибке, выдавать пользовательское исключение ... Выбор за вами!

Надеюсь, это поможет вам кое-что прояснить, если нет, то, пожалуйста, кричите:)

0 голосов
/ 14 августа 2008

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

0 голосов
/ 14 августа 2008

Global.asax и Application_Error?

http://www.15seconds.com/issue/030102.htm

Или событие Page_Error только на отдельной странице:

http://support.microsoft.com/kb/306355

void Page_Load(object sender, System.EventArgs e)
{
    throw(new ArgumentNullException());
}

public void Page_Error(object sender,EventArgs e)
{
    Exception objErr = Server.GetLastError().GetBaseException();
    string err =    "<b>Error Caught in Page_Error event</b><hr><br>" + 
                    "<br><b>Error in: </b>" + Request.Url.ToString() +
                    "<br><b>Error Message: </b>" + objErr.Message.ToString()+
                    "<br><b>Stack Trace:</b><br>" + 
                      objErr.StackTrace.ToString();
    Response.Write(err.ToString());
    Server.ClearError();
}

Кроме того, Карл Сегуин (Привет, Карл!) Написал пост об использовании HttpHandler:

http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx

(Не уверен, что разрешение на его воспроизведение, но если вы хотите написать ответ, вы получили мое Upvote ☺)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...