Domanda

Vorrei scrivere il mio legante modello per il tipo DateTime. Prima di tutto mi piacerebbe scrivere un nuovo attributo che posso allegare alla mia proprietà modello come:

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

Questa è la parte facile. Ma la parte legante è un po 'più difficile. Vorrei aggiungere un nuovo legante modello di tipo DateTime. Ho possibile

  • implementare l'interfaccia IModelBinder e scrivere il mio metodo BindModel()
  • metodo di ereditare da DefaultModelBinder e sovrascrivere BindModel()

Il mio modello ha una proprietà come visto sopra (Birth). Così, quando il modello tenta di associare dati di richiesta a questa proprietà, BindModel(controllerContext, bindingContext) il mio modello di legante viene invocato. Tutto ok, ma. Come faccio ad avere proprietà attributi dal regolatore / BindingContext, per analizzare correttamente la mia data? Come posso ottenere l'PropertyDesciptor della proprietà Birth?

Modifica

A causa della separazione degli interessi mia classe modello è definito in un assembly che non fa (e non dovrebbe) riferimento System.Web.Mvc assemblaggio. Impostazione personalizzata vincolante (simile a Scott Hanselman ) attribuisce è un no-go qui.

È stato utile?

Soluzione

Non credo che si dovrebbe mettere gli attributi specifici della locale su un modello.

altre due possibili soluzioni a questo problema sono:

  • avere le pagine traslitterano risale al formato specifico delle impostazioni internazionali in un formato generico come aaaa-mm-gg in JavaScript. (Opere, ma richiede JavaScript.)
  • Scrivi un modello legante che considera la lingua dell'interfaccia utente corrente durante l'analisi di date.

Per rispondere alla tua domanda attuale, il modo per ottenere attributi personalizzati (per MVC 2) è quello di scrivere un AssociatedMetadataProvider .

Altri suggerimenti

è possibile modificare il legante modello predefinito per utilizzare la cultura dell'utente utilizzando 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 nel Global.asax aggiungere quanto segue al Application_Start ():

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

Per saperne di più a questo ottimo blog che Spiega perché MVC quadro squadra ha implementato una cultura di default a tutti gli utenti.

Ho avuto questo problema molto grande di me e dopo ore di prova e fallisco ho ottenuto una soluzione di lavoro come hai chiesto.

Prima di tutto in quanto avere un legante solo su una proprietà non è possibile yuo devono implementare un ModelBinder pieno. Dal momento che non si vuole la si legano tutta la singola proprietà, ma solo quello che ti interessa è possibile ereditare da DefaultModelBinder e quindi associare la singola proprietà:

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

Nel mio esempio ho data parsing con una cultura fiexed, ma ciò che si vuole fare è possibile. È necessario creare un CustomAttribute (come DateTimeFormatAttribute) e metterlo su di voi proprietà:

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

Ora nel metodo BindProperty, invece di cercare una proprietà DateTime si può cercare una proprietà con voi DateTimeFormatAttribute, afferra il formato specificato nel costruttore e poi analizzare la data con DateTime.ParseExact

Spero che questo aiuta, mi c'è voluto molto tempo per venire con questa soluzione. In realtà è stato facile avere questa soluzione una volta sapevo come cercatela: (

Si potrebbe implementare un personalizzato DateTime Binder in questo modo, ma si deve prendere a cuore la cultura e il valore assunto dalla richiesta del client vero e proprio. Che tu possa avere una data come gg / mm / aaaa in it-IT e lo vogliono convertire nella cultura sistemi it-IT (che sarebbe come gg / mm / aaaa) o di una cultura invariante, come facciamo noi, allora si devono analizzarlo prima e utilizzare Converti facciata statica di cambiarlo nel suo 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;
        }
    }

In ogni caso, la cultura dependend DateTime analisi in un'applicazione senza stato possibile da una crudeltà ... Soprattutto quando si lavora con JSON su JavaScript lato client e indietro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top