ASP.NET MVC بيتا 1:DefaultModelBinder يستمر بشكل خاطئ في المعلمة وحالة التحقق من الصحة بين الطلبات غير المرتبطة

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

سؤال

عندما أستخدم ربط النموذج الافتراضي لربط معلمات النموذج بكائن معقد يمثل معلمة لإجراء ما، يتذكر إطار العمل القيم التي تم تمريرها إلى الطلب الأول، مما يعني أن أي طلب لاحق لهذا الإجراء يحصل على نفس البيانات مثل الأول.يتم الحفاظ على قيم المعلمات وحالة التحقق من الصحة بين طلبات الويب غير ذات الصلة.

هذا هو رمز وحدة التحكم الخاصة بي (service يمثل الوصول إلى النهاية الخلفية للتطبيق):

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Create()
    {
        return View(RunTime.Default);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(RunTime newRunTime)
    {
        if (ModelState.IsValid)
        {
            service.CreateNewRun(newRunTime);
            TempData["Message"] = "New run created";
            return RedirectToAction("index");
        }
        return View(newRunTime);
    }

طريقة عرض .aspx الخاصة بي (المكتوبة بقوة كـ ViewPage<RunTime>) يحتوي على توجيهات مثل:

<%= Html.TextBox("newRunTime.Time", ViewData.Model.Time) %>

يستخدم هذا DefaultModelBinder الطبقة، وهي من المفترض أن يتم ربط خصائص النموذج الخاص بي تلقائيًا.

لقد قمت بالنقر على الصفحة، وأدخلت بيانات صالحة (على سبيل المثال.الوقت = 1).يحفظ التطبيق الكائن الجديد بشكل صحيح مع مرور الوقت = 1.ثم قمت بالضغط عليه مرة أخرى، وأدخل بيانات صالحة مختلفة (على سبيل المثال:الوقت = 2).ومع ذلك، فإن البيانات التي يتم حفظها هي الأصلية (على سبيل المثال.الوقت = 1).يؤثر هذا أيضًا على التحقق من الصحة، لذلك إذا كانت بياناتي الأصلية غير صالحة، فإن جميع البيانات التي أدخلها في المستقبل تعتبر غير صالحة.تؤدي إعادة تشغيل IIS أو إعادة إنشاء الكود الخاص بي إلى مسح الحالة المستمرة.

يمكنني حل المشكلة عن طريق كتابة رابط النموذج المشفر الخاص بي، والذي يظهر مثال ساذج أساسي أدناه.

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create([ModelBinder(typeof (RunTimeBinder))] RunTime newRunTime)
    {
        if (ModelState.IsValid)
        {
            service.CreateNewRun(newRunTime);
            TempData["Message"] = "New run created";
            return RedirectToAction("index");
        }
        return View(newRunTime);
    }


internal class RunTimeBinder : DefaultModelBinder
{
    public override ModelBinderResult BindModel(ModelBindingContext bindingContext)
    {
        // Without this line, failed validation state persists between requests
        bindingContext.ModelState.Clear();


        double time = 0;
        try
        {
            time = Convert.ToDouble(bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"]);
        }
        catch (FormatException)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName + ".Time", bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"] + "is not a valid number");
        }

        var model = new RunTime(time);
        return new ModelBinderResult(model);
    }
}

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

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

المحلول

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

واكتشفت مشكلة أثناء تحليل بعناية الفرق بين كود إيلون والألغام، والقضاء على جميع الاحتمالات الأخرى. كما تقول الوثائق قلعة ، وهذا هو "خطأ فادحا" ! فليكن هذا تحذير للآخرين!

وشكرا لردكم إيلون - آسف أن تأخذ وقتك

نصائح أخرى

وحاولت استخراج هذه المشكلة ولكن أنا لا نرى أن نفس السلوك. أنا خلقت بالضبط نفس وحدة تحكم وجهات النظر التي لديك (مع بعض الافتراضات) تقريبا، وفي كل مرة أنا خلقت "وقت التشغيل" جديد أضع قيمته في TempData وأرسلت تشغيله من خلال إعادة توجيه. ثم على الصفحة الهدف أمسكت قيمة وكان دائما قيمة I كتبته في على هذا الطلب - لا قيمة قديمة

إليك المراقب بلدي:

والطبقة العامة HomeController: {المراقب المالي     مؤشر ActionResult الجمهور () {         ViewData [ "العنوان"] = "الصفحة الرئيسية".         رسالة سلسلة = "مرحبا" + TempData [ "رسالة"]؛         إذا (TempData.ContainsKey ( "قيمة")) {             الباحث theValue = (الباحث) TempData [ "قيمة"]؛             رسالة + = "" + theValue.ToString ()؛         }         ViewData [ "رسالة"] = الرسالة؛         العودة مشاهدة ()؛     }

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create() {
    return View(RunTime.Default);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(RunTime newRunTime) {
    if (ModelState.IsValid) {
        //service.CreateNewRun(newRunTime);
        TempData["Message"] = "New run created";
        TempData["value"] = newRunTime.TheValue;
        return RedirectToAction("index");
    }
    return View(newRunTime);
}

و}

وهنا وجهة نظري (Create.aspx):

<% using (Html.BeginForm()) { %>
<%= Html.TextBox("newRunTime.TheValue", ViewData.Model.TheValue) %>
<input type="submit" value="Save" />
<% } %>

وأيضا، لم أكن متأكدا ما بدا "وقت التشغيل" نوع مثل، لذلك جعل هذا واحد:

   public class RunTime {
        public static readonly RunTime Default = new RunTime(-1);

        public RunTime() {
        }

        public RunTime(int theValue) {
            TheValue = theValue;
        }

        public int TheValue {
            get;
            set;
        }
    }

هل من الممكن أن التطبيق الخاص بك من وقت التشغيل ويشمل بعض قيم ثابتة أو شيء من هذا؟

شكرا،

وإيلون

ولست متأكدا إذا كان هذا هو ذات الصلة أو لا، ولكن دعوتكم ل <٪ = Html.TextBox ( "newRunTime.Time"، ViewData.Model.Time)٪> قد التقطت في الواقع الزائد الخطأ (منذ وقت هو عدد صحيح، فإنه سيتم اختيار الزائد object htmlAttributes، بدلا من string value.

والتحقق من HTML المقدمة سوف تتيح لك معرفة ما إذا كان هذا يحدث. سوف تغير كثافة لViewData.Model.Time.ToString() إجبار الزائد الصحيح.

وهذا يبدو وكأنه مشكلتك هو شيء مختلف، لكني لاحظت أن وقد أحرقت في الماضي.

سيب، لست متأكدًا مما تقصده بمثال.لا أعرف شيئًا عن تكوين الوحدة.سأشرح الموقف مع Castle.Windsor وربما يساعدك ذلك في تكوين Unity بشكل صحيح.

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

في ASP.NET MVC، يرتبط كل مثيل لفئة وحدة التحكم بسياق طلب الويب الذي تم إنشاؤه لخدمته.لذا، إذا قامت حاوية IoC الخاصة بك بإرجاع نفس مثيل فئة وحدة التحكم الخاصة بك في كل مرة، فستحصل دائمًا على وحدة تحكم مرتبطة بسياق طلب الويب الأول الذي استخدم فئة وحدة التحكم هذه.على وجه الخصوص، ModelState وغيرها من الأشياء التي يستخدمها DefaultModelBinder سيتم إعادة استخدامه، لذا فإن كائن النموذج المنضم الخاص بك ورسائل التحقق من الصحة في ملف ModelState سوف تكون قديمة.

لذلك تحتاج إلى IoC الخاص بك لإرجاع مثيل جديد في كل مرة يطلب فيها MVC مثيلًا لفئة وحدة التحكم الخاصة بك.

وهذا ما يسمى في Castle.Windsor بأسلوب الحياة العابر.لتكوينه، لديك خياران:

  1. تكوين XML:يمكنك إضافة lifestlye = "transient" إلى كل عنصر في ملف التكوين الخاص بك والذي يمثل وحدة التحكم.
  2. التكوين في التعليمات البرمجية:يمكنك إخبار الحاوية باستخدام نمط الحياة العابر في وقت تسجيل وحدة التحكم.هذا هو ما يفعله لك مساعد MvcContrib الذي ذكره Ben تلقائيًا - ألق نظرة على طريقة RegisterControllers في MvcContrib كود المصدر.

أتصور أن Unity تقدم مفهومًا مشابهًا لأسلوب الحياة في Castle.Windsor، لذلك ستحتاج إلى تكوين Unity لاستخدام ما يعادله من نمط الحياة العابر لوحدات التحكم الخاصة بك.يبدو أن MvcContrib لديه بعض دعم الوحدة - ربما يمكنك أن تنظر هناك.

أتمنى أن يساعدك هذا.

وبعد أن تأتي عبر مشاكل مماثلة عند محاولة استخدام الحاوية وندسور اللجنة الاولمبية الدولية في التطبيق ASP.NET MVC اضطررت للذهاب من خلال نفس رحلة استكشافية للحصول على عمل. وفيما يلي بعض التفاصيل التي قد تساعد شخص آخر.

وباستخدام هذا الإعداد الأولي في Global.asax:

  if (_container == null) 
  {
    _container = new WindsorContainer("config/castle.config");
    ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container)); 
  }

وباستخدام WindsorControllerFactory الذي عندما سئل عن مثيل تحكم به:

  return (IController)_container.Resolve(controllerType);

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

والافتراضي هو الحاوية لتمرير العودة سنغلتونس، ومن الواضح أن شيئا سيئا عن وحدات تحكم وسبب المشكلة:

http://www.castleproject.org/monorail/documentation /trunk/integration/windsor.html

ولكن الوثائق تشير بالفعل إلى أن نمط الحياة من وحدات التحكم يمكن تغيير إلى عابرة، على الرغم من أنه لا يقول في الواقع كنت كيفية القيام بذلك إذا كنت تستخدم ملف التكوين. وتبين أنه من السهل بما فيه الكفاية:

<component 
  id="home.controller" 
  type="DoYourStuff.Controllers.HomeController, DoYourStuff" 
  lifestyle="transient" />

ودون تغيير أي رمز يجب الآن تعمل كما هو متوقع (أي وحدات تحكم فريدة في كل مرة المقدمة من مثيل واحد من حاوية). ثم يمكنك أن تفعل كل تكوين اللجنة الاولمبية الدولية الخاصة بك في ملف التكوين بدلا من رمز مثل الصبي جيدة / فتاة وأنا أعلم أنك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top