Pregunta

Me gustaría escribir mi propio modelo para el tipo de aglutinante DateTime. En primer lugar me gustaría escribir un nuevo atributo que pueda adjuntarse a la propiedad de modelo como:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

Esta es la parte fácil. Pero la parte de aglutinante es un poco más difícil. Me gustaría añadir una nueva carpeta de modelo para el tipo DateTime. Me Puede

  • implementar la interfaz IModelBinder y escribir mi propio método BindModel()
  • Heredar del DefaultModelBinder y método de reemplazo BindModel()

Mi modelo tiene una propiedad como se ha visto anteriormente (Birth). Por eso, cuando los intentos de los modelos a los datos de solicitud de enlace a esta propiedad, BindModel(controllerContext, bindingContext) mi modelo de ligante se invoca. Todo muy bien, pero. propiedad ¿Cómo consigo los atributos del controlador / BindingContext, para analizar correctamente la fecha de mi ? ¿Cómo puedo llegar al PropertyDesciptor de Birth propiedad?

Editar

Debido a la separación de las preocupaciones de mi clase de modelo se define en un conjunto que no lo hace (y no debería) de referencia System.Web.Mvc montaje. Configuración personalizada de unión (similar a la de de Scott Hanselman ejemplo ) atributos es un no-ir aquí.

¿Fue útil?

Solución

No creo que se debería poner atributos específicos de la localidad en un modelo.

Otros dos posibles soluciones a este problema son:

  • Haga que sus páginas transliteran data del formato de la configuración regional específica a un formato genérico como aaaa-mm-dd en JavaScript. (funciona, pero requiere JavaScript.)
  • Escribir un ligante modelo que considera la cultura actual interfaz de usuario al analizar fechas.

Para responder a su pregunta real, la manera de conseguir los atributos personalizados (por MVC 2) es escribir un AssociatedMetadataProvider .

Otros consejos

Puede cambiar la carpeta de modelo por defecto a utilizar la cultura del usuario utilizando IModelBinder

public class DateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

public class NullableDateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

        return value == null
            ? null 
            : value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

Y en el Global.asax añadir lo siguiente a Application_Start ():

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder());

Más en este excelente blog que describir por qué MVC equipo implementó un marco predeterminado cultura a todos los usuarios.

Yo tenía este problema muy grande a mí mismo y después de horas de intentar y fallar Tengo una solución de trabajo como lo pediste.

En primer lugar, ya que tiene un aglutinante en apenas una propiedad no es possibile yuo tener que implementar un ModelBinder completa. Puesto que usted no desea que la unen toda la propiedad única, pero sólo el que la atención que puede heredar de DefaultModelBinder y luego enlazar la propiedad única:

public class DateFiexedCultureModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        if (propertyDescriptor.PropertyType == typeof(DateTime?))
        {
            try
            {
                var model = bindingContext.Model;
                PropertyInfo property = model.GetType().GetProperty(propertyDescriptor.Name);

                var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);

                if (value != null)
                {
                    System.Globalization.CultureInfo cultureinfo = new System.Globalization.CultureInfo("it-CH");
                    var date = DateTime.Parse(value.AttemptedValue, cultureinfo);
                    property.SetValue(model, date, null);
                }
            }
            catch
            {
                //If something wrong, validation should take care
            }
        }
        else
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }
}

En mi ejemplo estoy analicen fechas con una cultura fiexed, pero lo que quiere hacer es posible. Se debe crear un CustomAttribute (como DateTimeFormatAttribute) y lo puso sobre la propiedad que:

[DateTimeFormat("d.M.yyyy")]
public DateTime Birth { get; set,}

Ahora en el método BindProperty, en lugar de buscar una propiedad DateTime se puede buscar una propiedad con que DateTimeFormatAttribute, agarra el formato especificado en el constructor y luego analizar la fecha con DateTime.ParseExact

Espero que esto ayude, que me llevó mucho tiempo para venir con esta solución. En realidad fue fácil tener esta solución una vez que supe cómo buscarla: (

Se podría aplicar una costumbre DateTime Carpeta como tal, pero hay que tener cuidado acerca de la cultura y el valor asumido por la petición de un cliente real. Puede usted obtener una fecha como dd / mm / aaaa en es-ES y quiere que se convierta en la cultura de los sistemas en-GB (que sería como dd / mm / aaaa) o una referencia cultural de todos, como lo hacemos, entonces que analizar antes y utilizando la fachada Convertir estática para cambiarlo en su comportamiento.

    public class DateTimeModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var valueResult = bindingContext.ValueProvider
                              .GetValue(bindingContext.ModelName);
            var modelState = new ModelState {Value = valueResult};

            var resDateTime = new DateTime();

            if (valueResult == null) return null;

            if ((bindingContext.ModelType == typeof(DateTime)|| 
                bindingContext.ModelType == typeof(DateTime?)))
            {
                if (bindingContext.ModelName != "Version")
                {
                    try
                    {
                        resDateTime =
                            Convert.ToDateTime(
                                DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture,
                                    DateTimeStyles.AdjustToUniversal).ToUniversalTime(), CultureInfo.InvariantCulture);
                    }
                    catch (Exception e)
                    {
                        modelState.Errors.Add(EnterpriseLibraryHelper.HandleDataLayerException(e));
                    }
                }
                else
                {
                    resDateTime =
                        Convert.ToDateTime(
                            DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture), CultureInfo.InvariantCulture);
                }
            }
            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
            return resDateTime;
        }
    }

De todos modos, la cultura dependend DateTime análisis en una lata de aplicaciones sin estado por una crueldad ... Especialmente cuando se trabaja con JSON en JavaScript del lado del cliente y hacia atrás.

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