ASP.NET MVC Beta 1:DefaultModelBinder erróneamente persiste de parámetros y validación del estado de entre los no relacionados solicitudes

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

Pregunta

Al utilizar el modelo de enlace predeterminada para enlazar parámetros de formulario un objeto complejo que es un parámetro a una acción, el marco recuerda los valores que se pasan a la primera solicitud, lo que significa que cualquier solicitud posterior en que la acción se presenta los mismos datos que la primera.Los valores de los parámetros y validación del estado se mantiene entre los no relacionados de peticiones web.

Aquí está mi código de controlador (service representa el acceso a la parte final de la aplicación):

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

Mi .aspx view (inflexible como ViewPage<RunTime>) contiene directivas como:

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

Para ello se utiliza el DefaultModelBinder la clase, que es destinado a autobind mis propiedades del modelo.

Entré a la página, introduzca datos válidos (por ejemplo,tiempo = 1).La aplicación guarda correctamente el nuevo objeto con el tiempo = 1.Entonces me golpeó de nuevo, introducir diferentes datos válidos (por ejemplo,tiempo = 2).Sin embargo los datos que se guarda el original (por ejemplo,tiempo = 1).Esto también afecta a la validación, por lo que si mis datos no es válida, entonces todos los datos que introduzca en el futuro no se considera válido.Reiniciar IIS o la reconstrucción de mi código vacía el estado persistente.

Me pueden solucionar el problema por escrito mi propia rígida modelo de cuaderno, un básico ingenuo como el del ejemplo que se muestra a continuación.

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

Me estoy perdiendo algo?Yo no creo que sea una sesión de navegador problema como puedo reproducir el problema, si el primero se introducen los datos en un navegador, y el segundo en el otro.

¿Fue útil?

Solución

Resulta que el problema era que mis controladores estaban siendo reutilizado entre llamadas.Uno de los detalles, me eligió para omitir de mi post original es que estoy usando el Castillo.Windsor container para crear mis controladores.Yo no había podido marcar mi controlador con el Transitorio estilo de vida, por lo que estaba recibiendo la misma instancia de la espalda en cada solicitud.Así, el contexto está siendo usado por el cuaderno estaba siendo re-utilizados y, por supuesto, que contenía datos obsoletos.

He descubierto el problema al analizar detalladamente la diferencia entre Eilon del código y de la mina, la eliminación de todas las otras posibilidades.Como el El castillo de la documentación dice, este es un "terrible error"!Que esto sea una advertencia para los demás!

Gracias por tu respuesta Eilon - lo siento a tomar su tiempo.

Otros consejos

He intentado reproducir este problema, pero yo no estoy viendo que el comportamiento de la misma.He creado casi exactamente el mismo controlador y las vistas que tiene (con algunas suposiciones) y cada vez que he creado un nuevo "tiempo de ejecución" poner su valor en TempData y la envió a través de la Redirección.A continuación, en la página de destino me agarró el valor y fue siempre el valor que he escrito en esta solicitud - nunca un rancio valor.

Aquí está mi Controlador:

clase pública HomeController :Controlador { public ActionResult Index() { ViewData["Title"] = "Página De Inicio";string mensaje = "Bienvenido:"+ TempData["Mensaje"];si (TempData.ContainsKey("valor")) { int valor = (int)TempData["valor"];mensaje += "" + valor.ToString();} ViewData["Mensaje"] = mensaje;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);
}

}

Y aquí está mi punto de Vista (Crear.aspx):

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

Además, no estaba seguro de lo que el "tiempo de ejecución" tipo parecía, así que hice este:

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

        public RunTime() {
        }

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

        public int TheValue {
            get;
            set;
        }
    }

Es posible que su aplicación de tiempo de ejecución incluye algunos valores estáticos o algo?

Gracias,

Eilon

No estoy seguro de si esto es o no, pero su llamada para <%= Html.Cuadro de texto("newRunTime.Tiempo", ViewData.Modelo.Tiempo) %> en realidad podría elegir el mal de sobrecarga (ya que el Tiempo es un número entero, se recogerá la object htmlAttributes la sobrecarga, en lugar de string value.

Comprobar el renderizado de HTML le permite saber si esto está ocurriendo.el cambio de int a ViewData.Model.Time.ToString() será la fuerza de la correcta sobrecarga.

Parece que tu problema es algo diferente, pero me di cuenta de eso y se han quemado en el pasado.

Seb, no estoy seguro de lo que quieres decir con un ejemplo.No sé nada acerca de la Unidad de configuración.Voy a explicar la situación con el Castillo.Windsor y tal vez eso le ayudará a configurar la Unidad correctamente.

De forma predeterminada, el Castillo.Windsor devuelve el mismo objeto cada vez que se solicita un tipo dado.Este es el singleton estilo de vida.Hay una buena explicación de las distintas opciones de estilo de vida en el Castillo.Windsor documentación.

En ASP.NET MVC, cada instancia de una clase de controlador está enlazado el contexto de la solicitud web que fue creado para servir.Así que si su contenedor de IoC devuelve la misma instancia de la clase de controlador cada vez que, siempre vas a obtener un controlador enlazado el contexto de la primera solicitud de web que utiliza ese controlador de clase.En particular, la ModelState y otros objetos utilizados por el DefaultModelBinder va a ser reutilizado, por lo que su obligación objeto del modelo y la validación de los mensajes en el ModelState va a ser obsoletos.

Por lo tanto, usted necesita su Coi para devolver una nueva instancia cada vez MVC solicita una instancia de la clase de controlador.

En El Castillo.Windsor, esto se llama el estilo de vida ambulante.Para configurar esto, usted tiene dos opciones:

  1. XML de configuración:agregar lifestlye="transitoria" a cada elemento en su archivo de configuración que representa un controlador.
  2. En la configuración de código:se puede decir que el contenedor de uso transitorio de estilo de vida en el momento de registrar el controlador.Esto es lo que el MvcContrib ayudante que Ben se mencionó hace automáticamente para usted toma una mirada en el método RegisterControllers en el MvcContrib código fuente.

Me imagino que la Unidad ofrece un concepto similar a la forma de vida en el Castillo.Windsor, así que usted necesitará para configurar la Unidad para su uso equivalente de la transitoria estilo de vida para sus controladores.MvcContrib parece tener algunas La unidad de apoyo - tal vez usted podría mirar allí.

Espero que esto ayude.

Habiendo llegado a través de problemas similares al intentar utilizar el Windsor contenedor de IoC en un ASP.NET aplicación MVC tuve que pasar por el mismo viaje de descubrimiento para que funcione.Aquí están algunos de los detalles que puedan ayudar a alguien más.

El uso de este es la configuración inicial en el Mundial.asax:

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

Y el uso de un WindsorControllerFactory que cuando se le preguntó por una instancia de controlador hace:

  return (IController)_container.Resolve(controllerType);

Mientras Windsor fue correctamente la vinculación de todos los controladores, por alguna razón, los parámetros no se pasa de la forma a la pertinente acción del controlador.En lugar de que todos ellos fueron nulos, a pesar de que estaba llamando a la acción correcta.

El valor predeterminado es para el contenedor para pasar los embarazos únicos, obviamente es una mala cosa para los controladores y la causa del problema:

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

Sin embargo, la documentación de señalar que el estilo de vida de los controladores puede ser cambiado transitorias, aunque realmente no pueden decir cómo hacer que si usted está usando un archivo de configuración.Resulta que es bastante fácil:

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

Y sin cambios en el código ahora debería funcionar como se esperaba (es decir,único de los controladores de cada hora proporcionada por la instancia de uno de los contenedores).A continuación, puede hacer todos los de la Coi de configuración en el archivo de configuración en lugar de que el código como el buen chico/chica que sé que son.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top