Pergunta

Eu gostaria de escrever meu próprio fichário de modelo para DateTime modelo. Em primeiro lugar, eu gostaria de escrever um novo atributo que eu possa anexar à minha propriedade modelo como:

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

Esta é a parte fácil. Mas a parte do fichário é um pouco mais difícil. Eu gostaria de adicionar um novo fichário de modelo para tipo DateTime. Eu também posso

  • implemento IModelBinder Interface e escreva o meu próprio BindModel() método
  • herdar de DefaultModelBinder e substituir BindModel() método

Meu modelo tem uma propriedade como visto acima (Birth). Então, quando o modelo tenta vincular dados de solicitação a esta propriedade, o meu modelo de fichário BindModel(controllerContext, bindingContext) é invocado. Tudo bem, mas. Como obtenho atributos de propriedade do controlador/bindingContext, para analisar minha data corretamente? Como posso chegar ao PropertyDesciptor de propriedade Birth?

Editar

Devido à separação de preocupações, minha classe de modelo é definida em uma montagem que não existe (e não deveria) System.web.mvc Assembly. Definindo ligação personalizada (semelhante a Exemplo de Scott Hanselman) Atributos é um não-vá aqui.

Foi útil?

Solução

Eu não acho que você deva colocar atributos específicos de localidade em um modelo.

Duas outras soluções possíveis para esse problema são:

  • Faça com que suas páginas o transliterar datas do formato específico da localidade para um formato genérico como AAAA-MM-DD em JavaScript. (Funciona, mas requer JavaScript.)
  • Escreva um fichário de modelo que considere a cultura atual da interface do usuário ao analisar as datas.

Para responder à sua pergunta real, a maneira de obter atributos personalizados (para o MVC 2) é para Escreva um AssociatedMetAdataProvider.

Outras dicas

Você pode alterar o fichário do modelo padrão para usar a cultura do usuário usando 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);
    }
}

E no global.asax adicione o seguinte a appold_start ():

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

Leia mais em Este excelente blog Isso descreve por que a equipe da MVC Framework implementou uma cultura padrão para todos os usuários.

Eu mesmo tive esse grande problema e, depois de horas de tentativa, obtive uma solução de trabalho como você pediu.

Primeiro de tudo, já que ter um fichário em apenas uma propriedade não é possível que você tenha que implementar um Modelbinder completo. Como você não deseja o vínculo toda a propriedade, mas apenas a que você se importa, pode herdar do DefaultModelBinder e depois vincular a propriedade ú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);
        }
    }
}

No meu exemplo, estou analisando a data com uma cultura fiexed, mas o que você quer fazer é possível. Você deve criar um atributo personalizado (como o DateTimeFormatattribute) e colocá -lo sobre sua propriedade:

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

Agora, no método BindProperty, em vez de procurar uma propriedade DateTime, você pode procurar uma propriedade com você DATETIMEFORMATATTIBUTIVE, pegue o formato que você especificou no construtor e, em seguida, analise a data com o DateTime.parseExact

Espero que isso ajude, demorei muito para vir com esta solução. Foi realmente fácil ter essa solução depois que eu sabia como pesquisá -la :(

Você pode implementar um aglutinante personalizado do DateTime como assim, mas precisa cuidar da cultura e valor assumidos da solicitação real do cliente. Que você obtenha uma data como MM/DD/AYYYY no EN-US e deseja que ela seja convertida na cultura de sistemas EN-GB (que seria como DD/MM/AYYYY) ou uma cultura invariante, como nós, então você tem que analisá -lo antes e usar a fachada estática converter para alterá -lo em seu comportamento.

    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 qualquer forma, a cultura depende da análise DateTime em um aplicativo sem estado pode por uma crueldade ... especialmente quando você trabalha com o JSON no JavaScript Customerside e para trás.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top