Я динамически загружаю пользовательские элементы управления, добавляя их в коллекцию элементов управления веб-формы.
Я хотел бы скрыть пользовательские элементы управления, если они вызывают необработанное исключение при рендеринге.
Итак, я попытался подключиться к событию Error каждого UserControl, но кажется, что это событие никогда не срабатывает для UserControls, как для класса Page.
Поискали, и это не кажется многообещающим. Есть идеи здесь?
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>");
}
}
}
Наслаждайтесь!
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 ☺)
Как насчет добавления нового подкласса UserControl, который обрабатывает ошибки в своих методах рендеринга и загрузки (чтобы они прятались по вашему желанию), а затем наследует их для пользовательских элементов управления?
Это интересная проблема ... Я все еще довольно свеж, когда дело доходит до пользовательских элементов управления и т. Д., Но вот мои мысли (не стесняйтесь комментировать / исправлять людей!) .. (Я вроде думаю / пишу вслух здесь!)
- Если во время рендеринга возникает ошибка, не слишком ли поздно? (поскольку некоторые элементы управления HTML, возможно, уже были отправлены в Writer и выведены).
- Следовательно, было бы не лучше обернуть метод Render пользовательского элемента управления, но вместо того, чтобы передать ему ссылку на «Live» HtmlTextWriter, вы пропустите свой собственный, перехватите любые исключения, возникшие в этом маленьком «пузыре» безопасности, если все пойдет хорошо , вы тогда передаете свой результирующий HTML фактическому HtmlTextWriter?
- Эта логика, вероятно, может быть перенесена в общий класс-обертку, который вы будете использовать для динамической загрузки / рендеринга элементов управления во время выполнения.
- Если какие-либо ошибки происходят, у вас есть вся необходимая информация! (т.е. контрольные ссылки и т. д.).
Просто мои мысли, пламя прочь! : D;)
В зависимости от того, где происходят ваши ошибки, вы можете сделать что-то вроде ...
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.
Я не уверен, что понимаю ваш ответ . Как вы загружаете свои элементы управления и добавляете их в свою коллекцию элементов управления?
В этом весь смысл бита, добавленного в раздел «Обновление». У вас есть возможность использовать SafeLoader где угодно.
Я не уверен, почему вы чувствуете, что не имеете доступа / контроля над Html? Цель SafeLoader заключается в том, что вам все равно, что такое html, вы просто пытаетесь «вывести» элемент управления (внутри «пузыря») и определить, нормально ли он загружается в текущем состоянии.
Если это так (т. Е. Возвращается html), вы можете делать с ним все что угодно, выводить html, добавлять элемент управления в коллекцию элементов управления, что угодно!
Если нет, то, опять же, вы можете делать то, что вам нравится, выдавать сообщение об ошибке, выдавать пользовательское исключение ... Выбор за вами!
Я надеюсь, что это поможет прояснить для вас вещи, если нет, то, пожалуйста, кричите :)
Я использовал подход @ Keith, но проблема в том, что элемент управления обрабатывается до тех пор, пока не будет сгенерировано исключение, что может привести к открытию HTML-тегов. Я также отображаю информацию об исключении в элементе управления, если в режиме отладки.
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}</pre>", message, ex.Message, ex.StackTrace));
}
}