اصطياد الاستثناءات غير المعالجة في ASP.NET UserControls

StackOverflow https://stackoverflow.com/questions/10793

سؤال

أقوم بتحميل عناصر تحكم المستخدم ديناميكيًا وإضافتها إلى مجموعة عناصر التحكم في نموذج الويب.

أرغب في إخفاء عناصر تحكم المستخدم إذا تسببت في استثناء غير معالج أثناء العرض.

لذا، حاولت ربط حدث الخطأ لكل UserControl ولكن يبدو أن هذا الحدث لا يتم تشغيله أبدًا لـ UserControls كما يحدث مع فئة الصفحة.

لقد قمت ببعض البحث على Google ولا يبدو الأمر واعدًا.أي أفكار هنا؟

هل كانت مفيدة؟

المحلول

mmilic، متابعة من ردك لي الفكرة السابقة..

لا يوجد منطق إضافي مطلوب!هذه هي النقطة، أنت لا تفعل شيئًا للفصول الدراسية المعنية، فقط قم بتغليفها في بعض غلاف الفقاعات المثيلي!:)

حسنًا، كنت سأشير إلى النقطة فقط ولكني أردت أن أرى هذا العمل بنفسي، لذلك جمعت بعضًا منه جداً كود تقريبي ولكن المفهوم موجود ويبدو أنه يعمل.

الاعتذار لمنصب طويلة

المحمل الآمن

سيكون هذا في الأساس "الفقاعة" التي ذكرتها ..سيحصل على عناصر التحكم 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>";
        }
    }
}

وبعض الضوابط..

حسنًا، لقد سخرت للتو من عنصري تحكم هنا، أحدهما سوف يرمي الآخر وسيجعل غير المرغوب فيه.أشير هنا، أنا لا أهتم.سيتم استبدالها بعناصر التحكم المخصصة الخاصة بك.

التحكم السيئ

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);
    }

أفكار

حسنًا، أعلم ما الذي تفكر فيه، "يتم إنشاء عناصر التحكم هذه برمجيًا، فماذا عن دعم المصمم؟لقد أمضيت ساعات طويلة في تحسين عناصر التحكم هذه للمصمم، والآن أنت تعبث بسحري".

حسنًا، لم أختبر هذا بالفعل بعد (ربما سأفعل ذلك خلال دقيقة!) ولكن الفكرة هنا هي تجاوز طريقة CreateChildControls للصفحة، وأخذ مثيل كل عنصر تحكم تمت إضافته في النموذج وتشغيله من خلال SafeLoader.إذا نجح الرمز، يمكنك إضافته إلى مجموعة عناصر التحكم كالمعتاد، وإذا لم يكن الأمر كذلك، فيمكنك إنشاء بيانات حرفية خاطئة أو شيء من هذا القبيل، الأمر متروك لك يا صديقي.

أخيراً..

مرة أخرى ، آسف على المنشور الطويل ، لكنني أردت الحصول على الكود هنا حتى نتمكن من مناقشة هذا :) آمل أن يساعد هذا في إظهار فكرتي :)

تحديث

تم الاختبار عن طريق وضع عنصر تحكم في المصمم وتجاوز طريقة 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>");
        }
    }
}

يتمتع!

نصائح أخرى

هذا هو مشكلة مثيرة للاهتمام..ما زلت جديدًا تمامًا عندما يتعلق الأمر بعناصر التحكم المخصصة وما إلى ذلك، ولكن إليك أفكاري (لا تتردد في التعليق/تصحيح الأشخاص!)..(أنا أفكر نوعًا ما/أكتب بصوت عالٍ هنا!)

  • إذا حدث خطأ أثناء العرض، في بعض الحالات، ألن يكون الوقت قد فات؟(نظرًا لأن بعض عناصر التحكم بتنسيق HTML ربما تم إرسالها بالفعل إلى الكاتب وإخراجها).
  • لذلك، أليس من الأفضل تغليف طريقة العرض الخاصة بعنصر تحكم المستخدم، ولكن بدلاً من تمرير الإشارة إلى HtmlTextWriter "المباشر"، يمكنك تمرير الخاص بك، ومحاصرة أي استثناءات مرفوعة في "فقاعة الأمان" الصغيرة هذه، إذا سارت الأمور على ما يرام ، ثم تقوم بتمرير HTML الناتج إلى HtmlTextWriter الفعلي؟
  • من المحتمل أن يكون هذا المنطق متدليًا إلى فئة مجمعة عامة يمكنك استخدامها لتحميل/عرض عناصر التحكم ديناميكيًا في وقت التشغيل.
  • في حالة حدوث أي أخطاء، لديك كل المعلومات التي تحتاجها تحت تصرفك!(أي مراجع التحكم وما إلى ذلك).

مجرد أفكاري، لهب بعيدا!:د ؛)

اعتمادًا على مكان حدوث الأخطاء، يمكنك القيام بشيء مثل...

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.

Global.asax وApplication_Error؟

http://www.15thans.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 (مرحبًا كارل!) منشور حول استخدام HttpHandler بدلاً من ذلك:

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

(لست متأكدًا من الإذن بإعادة إنتاجه، ولكن إذا كنت تريد كتابة إجابة، فقد حصلت على التصويت الإيجابي ☺)

ماذا عن إضافة فئة فرعية جديدة من UserControl تتعامل مع الأخطاء في طرق العرض والتحميل الخاصة بها (بحيث يتم إخفاؤها كما تريد) ثم ترث منها عناصر تحكم المستخدم الخاصة بك؟

لست متأكد أنني أفهم ردك..كيف تقوم بتحميل عناصر التحكم الخاصة بك وإضافتها إلى مجموعة عناصر التحكم الخاصة بك؟

كان هذا هو المغزى من الجزء الذي تمت إضافته في قسم "التحديث".لديك المرونة في استخدام أداة التحميل الآمن أينما تريد.

لست متأكدًا من سبب شعورك بأنه لا يمكنك الوصول/التحكم في ملف 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));
     }
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top