2012-02-27 29 views
8

Khi chơi xung quanh với công cụ ứng dụng trang đơn mới của MVC 4, tôi nhận thấy rằng không có ví dụ nào tôi tìm thấy chứa ví dụ về DateTime được cập nhật trở lại thông qua WebApi. Tôi sớm tìm ra lý do.MVC 4 Single Page Application và DateTime

Tôi bắt đầu bằng cách tạo SPA tiêu chuẩn từ mẫu được cung cấp. Sau đó tôi mở TodoItem.cs và thêm một trường DateTime. Sau đó, tôi tạo ra bộ điều khiển theo hướng dẫn của các bình luận. (Nếu không có trường datetime, mọi thứ hoạt động tốt).

Sau khi mọi thứ được tạo, tôi khởi động ứng dụng và điều hướng đến chỉ mục bộ điều khiển (tôi gọi là bộ điều khiển "tác vụ"). Tôi đã nhận được trang lưới với 0 hồ sơ như mong đợi và nhấp vào nút thêm. Tôi đã được đưa đến trang chỉnh sửa như mong đợi và đã nhập một số dữ liệu bao gồm một ngày trong trường datetime mới sáng bóng của tôi. Sau đó nhấp vào lưu.

lỗi được sản xuất mà nói:

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.

Nó sẽ xuất hiện mà các dụng cụ không hỗ trợ DateTime được nêu ra. Tôi chắc chắn rằng tôi có thể trải qua và dành một chút thời gian để tìm ra và làm cho nó hoạt động, nhưng tôi nghĩ tôi có thể thấy một chút may mắn ở đây với một người đã khắc phục vấn đề này và có thể cung cấp thông tin chi tiết.

Bất kỳ ai đã chiến đấu với điều này?

Cập nhật: Tôi đang thêm nhiều thông tin hơn mà tôi đã tìm thấy kể từ khi yêu cầu điều này. Tôi đã thử sử dụng JSON.Net làm Formatter của tôi như được đề xuất bên dưới. Tôi nghĩ rằng đó sẽ là giải pháp cuối cùng, tuy nhiên, chỉ cần làm như áp phích dưới đây được đề nghị là không đủ.

Khi sử dụng serializer JSON.Net, tôi nhận được lỗi sau:

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

Lý do là JSON.Net không cư đầy đủ các đối tượng formatter đang cố gắng để deserailize (System. Web.Http.Data.ChangeSet).

Các json được gửi trong là:

[{"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} 
}] 

Việc xây dựng trong Json Formatter có thể để pha Json này vào một đối tượng changeset với các đối tượng TodoItem embeded trong Entity và các lĩnh vực OriginalEntity.

Có ai đã nhận JSON.Net để deserialize điều này đúng không?

+0

Hiển thị một số mã cho phép tái tạo sự cố của bạn sẽ thực sự hữu ích. –

+0

Thật không may, tôi sẽ cần phải cung cấp toàn bộ giải pháp. Mẫu Ứng dụng Trang Đơn được cung cấp tạo ra rất nhiều mã. Để tạo lại lỗi, chỉ cần tạo một ứng dụng MVC 4 Single Page mới. Khi công cụ kết thúc, hãy mở TodoItem.cs và thêm trường datetime. Sau đó kích chuột phải vào "Bộ điều khiển" và thêm một bộ điều khiển. Chọn tên cho nó, chọn TodoItem làm mẫu của bạn, mẫu SPA và để cho nó tạo ra một ngữ cảnh mới. Khi đã xong, chạy ứng dụng và điều hướng đến bộ điều khiển mới. Nhấp vào thêm và nhập dữ liệu, sau đó nhấp vào lưu. –

Trả lời

3

Vấn đề là trong bản beta hiện tại, ASP.NET Web API sử dụng DataContractJsonSerializer, trong đó có các vấn đề nổi tiếng với việc tuần tự hóa DateTime. Here is một lỗi nghiêm trọng gần đây đã được nâng lên trên Microsoft Connect cho vấn đề; MS trả lời rằng họ đã có một lỗi theo dõi vấn đề nhưng nó sẽ không được cố định trong khung thời gian .Net 4.5/VS11.

May mắn là bạn có thể thay thế trình nối tiếp JSON thay thế, chẳng hạn như James Newton-King's xuất sắc JSON.Net.

Henrik Nielsen trong nhóm ASP.NET có bài đăng excellent blog hiển thị cách bạn có thể sử dụng JSON.Net với ASP.NET Web API. Dưới đây là việc triển khai thực hiện một số MediaTypeFormatter sử dụng JSON.Net (nó cũng cần được kết nối với cấu hình ASP.NET Web API, blog của Henrik cũng chứng tỏ điều đó).

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

Tôi nghĩ rằng điều này có thể gần với câu trả lời đúng, nhưng nó dường như không hoạt động với kịch bản tôi đã nêu ở trên. Sau khi sử dụng mã này và cắm nó vào webapi, lỗi sau được kích hoạt khi cố lưu: Lỗi máy chủ: Mã trạng thái HTTP: 500, thông báo: DataController này không hỗ trợ thao tác 'Cập nhật' cho thực thể 'JObject'. Điều này có thể là một vấn đề với upshot.js, nhưng tôi đã không thể tìm ra nơi mà lỗi này đến từ chưa. –

+0

Tôi đã phải từ bỏ toàn bộ con đường này. Json.Net dường như là câu trả lời, nhưng nhận được nó để tôn trọng các định dạng Json upshot.js muốn trong mã ví dụ ứng dụng đơn trang là một nhức đầu lớn mà tôi chỉ không thể làm việc khá đúng. Vào cuối ngày, tôi có thể làm điều đó nhanh hơn trong MVC3 và chờ đợi MVC4 để hy vọng thêm hỗ trợ cho những điều này. Tôi sẽ đánh dấu đây là câu trả lời vì poster đã chứng tỏ nó là một lỗi. –

+0

Cảm ơn bạn đã chấp nhận câu trả lời; có thể đáng để tiếp cận với JSON.Net và đưa ra một lỗi để xem liệu JSON.Net có thể được sửa hay không (hoặc gửi một bản vá!) –

0

JSON.NET mong đợi $ type trong khi bạn có __type để sopecify loại thực thể để nó chuyển đổi nó thành một JObject.

tôi đã tròn nó với klunk sau

trước tiên hãy chắc chắn rằng JsonSerializerSettings has .TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects;

sau đó viết riêng của bạn `` `` 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; 
     } 
    } 
} 

và sử dụng nó để deserialise các Json với `` `` sử dụng (MyJsonTextReader jsonTextReader = new MyJsonTextReader (StreamReader))

1

Tôi đã có chính xác cùng một vấn đề. Tôi đã dành quá nhiều thời gian cố gắng để có được json.net để làm việc. Cuối cùng tôi đã đưa ra cách giải quyết này mà bạn sẽ dính vào TodoItemsViewModel.js trong dự án ví dụ:

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

Các dòng mã xung quanh được bao gồm trong ngữ cảnh. Tôi thấy điều này trong các ý kiến ​​tại địa chỉ: http://blog.stevensanderson.com/2012/03/06/single-page-application-packages-and-samples/

Bạn cũng muốn thay đổi html trong _Editor.cshtml để ràng buộc vào "DateJson", không phải "EnterDate"

Điều này chắc chắn là một kludge nhưng nó có đức hạnh của làm việc không phải là một công việc nhỏ.

1

Bạn cũng có thể làm cho cửa sổ bật lên lịch JQuery hoạt động bằng cách thêm mã sau đây.

Thêm này ở dưới cùng của TodoItemsViewModel.js trong ví dụ MVC dự án 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) { 
    } 
}; 

Đây là những gì đang _Editor.cshtml của bạn sẽ trông giống như liên kết với các 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> 

Ngoài ra, bạn muốn thay đổi biến được hiển thị trên trang _Grid.cshtml từ "EnterDate" thành "DateJson".

Các vấn đề liên quan