2012-02-27 19 views
8

Mentre giocavo con i nuovi strumenti di applicazione di pagine singole di MVC 4, ho notato che nessuno degli esempi che ho trovato contiene un esempio di un DateTime in fase di aggiornamento tramite WebApi. Ho presto scoperto perché.MVC 4 Pagina singola Applicazione e DateTime

Ho iniziato generando la SPA standard dal modello fornito. Ho quindi aperto TodoItem.cs e aggiunto un campo DateTime. Quindi ho generato il controller come indicato dai commenti. (Senza il campo datetime, tutto funziona bene).

Dopo aver generato tutto, ho avviato l'app e sono passato all'indice del controller (ho chiamato il controller "tasks"). Ho ottenuto la pagina della griglia con 0 record come previsto e ho fatto clic sul pulsante Aggiungi. Sono stato portato alla pagina di modifica come previsto e inserito alcuni dati tra cui una data nel mio nuovo campo datetime brillante. Quindi fatto clic su Salva.

Un errore è stato prodotto che ha detto:

Server error: HTTP status code: 500, message: There was an error deserializing the object of type System.Web.Http.Data.ChangeSetEntry[]. DateTime content '01/01/2012' does not start with '/Date(' and end with ')/' as required for JSON.

sembrerebbe che l'attrezzatura non supporta DateTime ancora. Sono sicuro che potrei passare e passare un po 'di tempo a capire come farlo funzionare, ma ho pensato che potrei trovare un po' di fortuna qui con qualcuno che ha già risolto questo problema e può fornire informazioni utili.

Chiunque ha già combattuto questo?

Aggiornamento: Sto aggiungendo ulteriori informazioni che ho trovato da quando ho chiesto questo. Ho provato a utilizzare JSON.Net come mio Formatter come suggerito di seguito. Penso che questa sarà la soluzione finale, tuttavia, solo facendo come suggerito il poster qui sotto non è sufficiente.

Quando si utilizza il serializzatore JSON.Net, ottengo il seguente errore:

This DataController does not support operation 'Update' for entity 'JObject'.

La ragione è che JSON.Net non inserisce pienamente l'oggetto che il formattatore sta cercando di deserailize a (Sistema. Web.Http.Data.ChangeSet).

Il json che viene inviato in è:

[{"Id":"0", 
    "Operation":2, 
    "Entity": 
    {"__type":"TodoItem:#SPADateProblem.Models", 
    "CreatedDate":"/Date(1325397600000-0600)/", 
    "IsDone":false, 
    "Title":"Blah", 
    "TodoItemId":1}, 
    "OriginalEntity": 
    {"__type":"TodoItem:#SPADateProblem.Models", 
    "CreatedDate":"/Date(1325397600000-0600)/", 
    "IsDone":false, 
    "Title":"Blah", 
    "TodoItemId":1} 
}] 

costruito in JSON Formatter è in grado di ricostituire questo JSON in un oggetto changeset con oggetti TodoItem embeded nell'entità e campi OriginalEntity.

Qualcuno ha ottenuto che JSON.Net lo deserializzi correttamente?

+0

Mostrare del codice che consente di riprodurre il problema potrebbe essere davvero utile. –

+0

Purtroppo, avrei bisogno di fornire l'intera soluzione. Il modello di applicazione per pagina singola fornito genera molto codice. Per riprodurre l'errore, è sufficiente creare una nuova applicazione per pagina singola MVC 4. Una volta terminata la lavorazione, aprire TodoItem.cs e aggiungere un campo datetime. Quindi fai clic con il pulsante destro del mouse su "Controller" e aggiungi un controller. Scegli un nome per esso, seleziona TodoItem come modello, il modello SPA e lascia che crei un nuovo contesto. Una volta fatto, esegui l'applicazione e vai al nuovo controller. Fare clic su Aggiungi e immettere i dati, quindi fare clic su Salva. –

risposta

3

Il problema è che nella versione beta corrente, l'API Web ASP.NET utilizza DataContractJsonSerializer, che presenta noti problemi con la serializzazione di DateTime. Here is un bug di recente insuccesso di Microsoft Connect per il problema; MS risponde che hanno già un bug che tiene traccia del problema ma che non verrà risolto nel timeframe .Net 4.5/VS11.

Fortunatamente è possibile sostituire un serializzatore JSON alternativo, ad esempio James Newton-King's eccellente JSON.Net.

Henrik Nielsen sul team ASP.NET ha un post excellent blog che mostra come utilizzare JSON.Net con l'API Web ASP.NET. Ecco la sua implementazione di un MediaTypeFormatter che utilizza JSON.Net (dovrebbe anche essere collegato alla configurazione API Web ASP.NET, anche il blog di Henrik lo dimostra).

public class JsonNetFormatter : MediaTypeFormatter 
{ 
    private readonly JsonSerializerSettings settings; 

    public JsonNetFormatter(JsonSerializerSettings settings = null) 
    { 
     this.settings = settings ?? new JsonSerializerSettings(); 

     SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); 
     Encoding = new UTF8Encoding(false, true); 
    } 

    protected override bool CanReadType(Type type) 
    { 
     return type != typeof(IKeyValueModel); 
    } 

    protected override bool CanWriteType(Type type) 
    { 
     return true; 
    } 

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext) 
    { 
     var ser = JsonSerializer.Create(settings); 

     return Task.Factory.StartNew(() => { 
      using (var strdr = new StreamReader(stream)) 
      using (var jtr = new JsonTextReader(strdr)) 
      { 
       var deserialized = ser.Deserialize(jtr, type); 
       return deserialized; 
      } 
     }); 
    } 

    protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext) 
    { 
     JsonSerializer ser = JsonSerializer.Create(settings); 

     return Task.Factory.StartNew(() => 
     { 
       using (JsonTextWriter w = new JsonTextWriter(new StreamWriter(stream, Encoding)) { CloseOutput = false}) 
       { 
        ser.Serialize(w, value); 
        w.Flush(); 
       } 
     }); 
    } 
}  
+0

Penso che questo potrebbe essere vicino alla risposta corretta, ma non sembra funzionare con lo scenario che ho esposto sopra. Dopo aver utilizzato questo codice e averlo collegato a webapi, viene generato il seguente errore durante il tentativo di salvataggio: Errore del server: codice di stato HTTP: 500, messaggio: questo DataController non supporta l'operazione "Aggiorna" per l'entità "JObject". Questo potrebbe essere un problema con upshot.js, ma non sono stato in grado di capire da dove arriva ancora questo errore. –

+0

Ho dovuto abbandonare praticamente tutto questo percorso. Json.Net sembrava essere la risposta, ma far sì che rispettasse il formato Json che upshot.js voleva nel codice di esempio di Applicazione per pagina singola era un grosso problema che non riuscivo proprio a funzionare correttamente. Alla fine della giornata, posso farlo più velocemente in MVC3 e aspettare che MVC4 aggiunga il supporto per queste cose. Segnalo come risposta poiché il poster lo ha dimostrato come un errore. –

+0

Grazie per aver accettato la risposta; potrebbe valere la pena raggiungere JSON.Net e generare un bug per vedere se JSON.Net può essere corretto (o inviare una patch!) –

0

JSON.NET si aspetta $ type mentre hai __type per sopprimere il tipo di entità in modo che lo converta in un JObject.

ho ottenuto intorno ad esso con il seguente Klunk

prima assicurarsi che il JsonSerializerSettings has .TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects;

quindi scrivere il proprio `` `` JsonTextReader

public class MyJsonTextReader : JsonTextReader 
{ 
    public MyJsonTextReader(TextReader reader) 
     : base(reader) 
    { } 

    public override object Value 
    { 
     get 
     { 
      var o = new ActivityManager.Models.Sched_ProposedActivities(); 

      if (TokenType == JsonToken.PropertyName && base.Value.ToString() == "__type") 
       return "$type"; 
      if (TokenType == JsonToken.String && Path.ToString().EndsWith(".__type")) 
      { 
       string s = base.Value.ToString(); 
       var typeName = Regex.Match(s, ":#.*").ToString().Substring(2) + "." + Regex.Match(s, "^.*:#").ToString().Replace(":#", ""); 

       return 
        typeName + ", ActivityManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; 
      } 

      return base.Value; 
     } 
    } 
} 

e utilizzarlo per deserialise JSON con `` `` utilizzando (MyJsonTextReader jsonTextReader = new MyJsonTextReader (StreamReader))

1

stavo avendo lo stesso identico problema Ho passato troppo tempo a cercare di far funzionare json.net. Ho finalmente si avvicinò con questa soluzione che si sarebbe attaccare in TodoItemsViewModel.js nel progetto di esempio:

self.IsDone = ko.observable(data.IsDone); 
    self.EnterDate = ko.observable(data.EnterDate); 
    self.DateJson = ko.computed({ 
     read: function() { 
      if (self.EnterDate() != undefined) { 
       var DateObj = new Date(parseInt(self.EnterDate().replace("/Date(", "").replace(")/", ""), 10)); //.toDateString(); 
       var ret = DateObj.getMonth() + 1 + "/" + DateObj.getDate() + "/" + DateObj.getFullYear(); 
       return ret; 
      } 
      else { 
       return self.EnterDate(); 
      } 
     }, 
     write: function (value) { 
      var formattedDate = "\/Date(" + Date.parse(value) + ")\/" 
      self.EnterDate(formattedDate); 
     } 
    }); 
    upshot.addEntityProperties(self, entityType); 

Le linee circostanti di codice sono stati inclusi per il contesto. Ho trovato questo nei commenti a: http://blog.stevensanderson.com/2012/03/06/single-page-application-packages-and-samples/

Si vuole anche cambiare il codice HTML in _Editor.cshtml di legarsi a "DateJson", non "EnterDate"

Questo è certamente un ripiego, ma ha la virtù di lavorare che non è un'impresa da poco.

1

È anche possibile far funzionare il popup del calendario JQuery aggiungendo il seguente codice.

Aggiungere questo in fondo TodoItemsViewModel.js nell'esempio MVC progetto di 4 SPA:

ko.bindingHandlers.datepicker = { 
    init: function (element, valueAccessor, allBindingsAccessor) { 
     //initialize datepicker with some optional options 
     var options = allBindingsAccessor().datepickerOptions || {}; 
     $(element).datepicker(options); 

     //handle the field changing 
     ko.utils.registerEventHandler(element, "change", function() { 
      var observable = valueAccessor(); 
      observable($(element).datepicker("getDate")); 
     }); 

     //handle disposal (if KO removes by the template binding) 
     ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 
      $(element).datepicker("destroy"); 
     }); 

    }, 
    update: function (element, valueAccessor) { 
     var value = ko.utils.unwrapObservable(valueAccessor()), 
     current = $(element).datepicker("getDate"); 

     if (value - current !== 0) { 
      //$(element).datepicker("setDate", value); 
      $(element).val(value.toString()); 
     } 
    } 
} 

ko.bindingHandlers.date = { 
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
     var jsonDate = "/Date(12567120000-1000)/"; 
     var value = new Date(parseInt(jsonDate.substr(6))); 
     var ret = value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear(); 
     element.innerHTML = ret; 
    }, 

    update: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
    } 
}; 

Questo è ciò che il codice _Editor.cshtml sarà simile a legarsi al datepicker

<p> 
    EnterDate: 
    @*<input name="EnterDate" data-bind="value: EnterDate, autovalidate: true" /> 
    <span class="error" data-bind="text: EnterDate.ValidationError"></span>*@ 
    <input name="DateJson" data-bind="datepicker: DateJson, datepickerOptions: { minDate: new Date() }" /> 
    <span class="error" data-bind="text: DateJson.ValidationError"></span> 

</p> 

Inoltre, si desidera modificare la variabile visualizzata nella pagina _Grid.cshtml da "EnterDate" a "DateJson".

Problemi correlati