ユーザーコントロールを動的に読み込んで、WebフォームのControlsコレクションに追加しています。
レンダリング中に未処理の例外が発生した場合は、ユーザーコントロールを非表示にします。
そこで、各UserControlのErrorイベントにフックしてみましたが、Pageクラスの場合のように、このイベントがUserControlsに対して発生することはないようです。
いくつかグーグルをしましたが、それは有望ではないようです。ここに何かアイデアはありますか?
ユーザーコントロールを動的に読み込んで、WebフォームのControlsコレクションに追加しています。
レンダリング中に未処理の例外が発生した場合は、ユーザーコントロールを非表示にします。
そこで、各UserControlのErrorイベントにフックしてみましたが、Pageクラスの場合のように、このイベントがUserControlsに対して発生することはないようです。
いくつかグーグルをしましたが、それは有望ではないようです。ここに何かアイデアはありますか?
追加のロジックは必要ありません!それがポイントです。問題のクラスには何もせず、インスタンス化のバブルラップでラップするだけです。:)
OK、箇条書きにするつもりでしたが、この作品を自分で見たかったので、非常に大まかなコードをまとめましたが、コンセプトはそこにあり、うまくいくようです。
長い投稿をお詫びします
これは基本的に私が言及した「バブル」になります。それはコントロール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>";
}
}
}
ここで2つのコントロールをモックアップしました。一方はスローし、もう一方はジャンクをレンダリングします。ここを指して、私はがらくたを与えません。これらは、カスタムコントロールに置き換えられます。
public class BadControl : WebControl
{
protected override void Render(HtmlTextWriter writer)
{
throw new ApplicationException("Rob can't program controls");
}
}
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);
}
OK、私はあなたが考えていることを知っています、「これらのコントロールはプログラムでインスタンス化されます、デザイナーのサポートはどうですか?私はこれらのコントロールをデザイナーにとって良いものにするために何時間も費やしました、今あなたは私のモジョをいじっています」。
OK、私はまだこれを実際にテストしていません(おそらく数分で実行されます!)が、ここでのアイデアは、ページの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>");
}
}
}
楽しみ!
これは興味深い問題です..カスタムコントロールなどに関してはまだかなり新鮮ですが、ここに私の考えがあります(コメント/訂正してください!)..(私はちょっと考えています/ここで大声で書きます!)
ただ私の考え、炎上!: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*/ }
}
}
次に、UserControl の代わりに SilentErrorControl を継承します。
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();
}
また、Karl Seguin(Hi Karl!)には、代わりにHttpHandlerの使用に関する投稿がありました。
http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx
(それを複製する許可が何であるかはわかりませんが、答えを書きたい場合は、私の賛成票を受け取ります☺)
レンダリングメソッドとロードメソッドをエラー処理する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));
}
}