Frage

Als ich das Standardmodell verwenden Bindung an Formparameter für ein komplexes Objekt zu binden, das ein Parameter auf eine Aktion ist, erinnert sich der Rahmen, um die auf die erste Anforderung übergebenen Werte, was bedeutet, dass jede nachfolgende Anforderung zu dieser Aktion, die gleichen Daten wird als Der Erste. Die Parameterwerte und Überprüfungszustand zwischen nicht verwandten Web-Anfragen bestehen bleiben.

Dies ist mein Controller-Code (service stellt den Zugang zum hinteren Ende der app):

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

Meine ASPX- Ansicht (stark als ViewPage<RunTime getippt>) enthält Richtlinien wie:

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

Das die DefaultModelBinder-Klasse verwendet, die ist bedeutete mein Modell die Eigenschaften autoBind.

Ich schlug die Seite, geben Sie eine gültige Daten (zum Beispiel Zeit = 1). Die App speichert richtig das neue Objekt mit der Zeit = 1. ich es dann wieder treffen, geben Sie unterschiedliche gültige Daten (zum Beispiel Zeit = 2). Jedoch die Daten, die gespeichert wird ist die ursprüngliche (z.B. Zeit = 1). Dies wirkt sich auch auf die Validierung, so dass, wenn meine ursprünglichen Daten ungültig war, dann sind alle Daten, die ich in der Zukunft geben wird als ungültig betrachtet. IIS oder den Wiederaufbau meinen Code leert den beibehaltenen Zustand neu gestartet wird.

Ich kann das Problem beheben, indem Sie mein eigenes hartcodierte Modell Bindemittel, ein Grund naives Beispiel davon zu schreiben unten gezeigt.

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

Bin ich etwas fehlt? Ich glaube nicht, es ist ein Browser-Session Problem ist, wie ich das Problem reproduzieren kann, wenn die ersten Daten in einem Browser und die zweiten in einem anderen eingegeben werden.

War es hilfreich?

Lösung

Es stellt sich heraus, dass das Problem war, dass meine Controller zwischen den Anrufen wiederverwendet wurden. I Eines der Details wählten aus meinem ursprünglichen Beitrag zu verzichten ist, dass ich die Castle.Windsor Container bin mit meinem Controller zu erstellen. Ich hatte es versäumt, meinen Controller mit dem Transient Lebensstil zu markieren, so dass ich war immer die gleiche Instanz auf jeder Anforderung zurück. So der Kontext durch das Bindemittel verwendet wird, wurde wiederverwendet und natürlich darin enthaltenen veraltete Daten.

Ich entdeckte das Problem während sorgfältig den Unterschied zwischen Eilon Code und meine Analyse, alle anderen Möglichkeiten zu beseitigen. Da die Castle Dokumentation sagt, ist dies ein „schwerer Fehler“ ! Lass dir das eine Warnung für andere sein!

Vielen Dank für Ihre Antwort Eilon -. Entschuldigung Ihrer Zeit in Anspruch nehmen

Andere Tipps

Ich habe versucht, dieses Problem zu reproduzieren, aber ich bin nicht das gleiche Verhalten zu sehen. Ich habe fast genau dem gleichen Controller und Ansichten, die Sie (mit einigen Annahmen) haben und jedes Mal habe ich ein neues „runtime“ ich seinen Wert in TempData setzen und schickte es durch die Redirect ab. Dann auf der Zielseite packte ich den Wert und es war immer der Wert ich auf diese Anforderung eingegeben -. Nie ein schaler Wert

Hier ist mein Controller:

public class Homecontroller: Controller {     public Action Index () {         Viewdata [ "Titel"] = "Home";         string message = "Willkommen:" + TempData [ "Message"];         if (TempData.ContainsKey ( "value")) {             int theValue = (int) TempData [ "value"];             Nachricht + = "" + theValue.ToString ();         }         Viewdata [ "Message"] = Nachricht;         return View ();     }

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

}

Und hier ist meine Ansicht (Create.aspx):

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

Auch war ich nicht sicher, was die „runtime“ Typ aussah, also machte ich diese:

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

        public RunTime() {
        }

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

        public int TheValue {
            get;
            set;
        }
    }

Ist es möglich, dass Ihre Implementierung von RunTime einige statische Werte oder etwas enthält?

Danke,

Eilon

Ich bin mir nicht sicher, ob dies verwandt ist oder nicht, aber auf Ihren Anruf <% = Html.TextBox ( "newRunTime.Time", ViewData.Model.Time)%> könnte in der Tat die falsche Überlastung wählen (da Zeit eine ganze Zahl ist, wird es die object htmlAttributes Überlastung holen, anstatt string value.

das gerenderte HTML-Überprüfung werden Sie wissen lassen, wenn dies auftritt. int ViewData.Model.Time.ToString() Wechsel wird die richtige Überlastung erzwingen.

Es klingt wie das Problem etwas anders, aber ich bemerkte, dass und in der Vergangenheit verbrannt.

Seb, ich bin nicht sicher, was Sie durch ein Beispiel bedeuten. Ich weiß nichts über Unity-Konfiguration. Ich werde die Situation mit Castle.Windsor erklären und vielleicht die Ihnen helfen, mit Unity richtig zu konfigurieren.

Standardmäßig gibt Castle.Windsor das gleiche Objekt jedes Mal, wenn Sie einen bestimmten Typen anfordern. Dies ist der Singleton Lebensstil. Es gibt eine gute Erklärung der verschiedenen Lifestyle-Optionen in der Castle.Windsor Dokumentation .

In ASP.NET MVC, jede Instanz einer Controller-Klasse ist mit dem Kontext der Web-Anfrage gebunden, die sie geschaffen wurde zu dienen. Also, wenn Ihr IoC Behälter jedes Mal der gleiche Instanz der Controller-Klasse zurückgibt, werden Sie immer einen Controller auf den Kontext der ersten Web-Anfrage gebunden bekommen, die diese Controller-Klasse verwendet. Insbesondere die ModelState und andere von der DefaultModelBinder verwendeten Objekte werden wieder verwendet, so dass Ihr gebundenes Modellobjekt und die Überprüfungsmeldungen im ModelState wird abgestanden sein.

Deshalb müssen Sie Ihre IoC eine neue Instanz zurück jedes Mal MVC eine Instanz Ihrer Controller-Klasse anfordert.

In Castle.Windsor, ist dies der vorübergehende Lebensstil genannt. So konfigurieren Sie es, haben Sie zwei Möglichkeiten:

  1. XML-Konfiguration: Sie fügen lifestlye = "transient" auf jedes Element in der Konfigurationsdatei, die einen Controller darstellt.
  2. In-Code-Konfiguration: Sie können den Behälter sagen können, den transienten Lebensstil zu dem Zeitpunkt verwenden Sie den Controller registrieren. Dies ist, was die MvcContrib Helfer, dass Ben erwähnt automatisch für Sie tut - einen Blick auf die Methode RegisterControllers in der MvcContrib Quellcode .

Ich könnte mir vorstellen, dass die Einheit ein ähnliches Konzept auf den Lebensstil in Castle.Windsor bietet, so dass Sie Unity konfigurieren müssen ihr Äquivalent des transienten Lifestyle für Ihre Controller zu verwenden. MvcContrib zu haben scheint einige Unity Unterstützung - vielleicht könnte man es sehen.

Hope, das hilft.

Nachdem über ähnliche Probleme kommen, wenn die Windsor IoC-Container in einer ASP.NET MVC-Anwendung verwenden ich versuche, durch die gleiche Entdeckungsreise gehen musste, um es zu arbeiten. Hier sind einige der Details, die jemand anderes helfen könnte.

Mit diesem wird die Ersteinrichtung in der Global.asax:

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

Und mit einem WindsorControllerFactory die, wenn sie für eine Controller-Instanz gefragt hat:

  return (IController)_container.Resolve(controllerType);

Während Windsor richtig war alles Controller Verknüpfung aus irgendeinem Grunde Parameter wurden aus der Form nicht auf die entsprechende Controller-Aktion übergeben werden. Stattdessen waren sie alle null, obwohl es die richtige Aktion ruft.

Der Standard ist für die Behälter Singletons passieren zurück, offensichtlich eine schlechte Sache für Controller und die Ursache des Problems:

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

Allerdings hat die Dokumentation weist darauf hin, dass der Lebensstil des Controller auf transientes geändert werden kann, wenn man davon eigentlich nicht sagen, wie das tun, wenn Sie eine Konfigurationsdatei verwenden. Stellt sich heraus, es ist einfach genug:

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

Und ohne Code-Änderungen sollte nun wie erwartet (d einzigartige Controller jedes Mal, durch die eine Instanz des Behälters vorgesehen ist). Anschließend können Sie alle Ihre IoC Konfiguration in der Konfigurationsdatei tun, anstatt den Code wie der gute Junge / Mädchen, das ich weiß, Sie sind.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top