Datetime MVC4单页应用程序和日期时间

Datetime MVC4单页应用程序和日期时间,datetime,asp.net-mvc-4,asp.net-web-api,asp.net-spa,Datetime,Asp.net Mvc 4,Asp.net Web Api,Asp.net Spa,在使用MVC4新的单页应用程序工具时,我注意到我发现的示例中没有一个包含通过WebApi更新回日期时间的示例。我很快发现了原因 我首先从提供的模板生成标准SPA。然后我打开TodoItem.cs并添加了一个DateTime字段。然后,我按照注释的指示生成控制器。(没有datetime字段,一切正常) 生成所有内容后,我启动应用程序并导航到控制器索引(我称控制器为“任务”)。我得到了0条记录的网格页面,点击了添加按钮。我按预期进入了编辑页面,并在我闪亮的新datetime字段中输入了一些数据,包括

在使用MVC4新的单页应用程序工具时,我注意到我发现的示例中没有一个包含通过WebApi更新回日期时间的示例。我很快发现了原因

我首先从提供的模板生成标准SPA。然后我打开TodoItem.cs并添加了一个DateTime字段。然后,我按照注释的指示生成控制器。(没有datetime字段,一切正常)

生成所有内容后,我启动应用程序并导航到控制器索引(我称控制器为“任务”)。我得到了0条记录的网格页面,点击了添加按钮。我按预期进入了编辑页面,并在我闪亮的新datetime字段中输入了一些数据,包括日期。然后单击保存

产生了一个错误,表示:

服务器错误:HTTP状态代码:500,消息:反序列化System.Web.HTTP.Data.ChangeSetEntry[]类型的对象时出错。DateTime内容“01/01/2012”不是JSON所要求的以“/Date(”开头,以“/”结尾

该工具似乎还不支持DateTime。我相信我可以经历,花一点时间去弄清楚它,让它发挥作用,但我想我可能会在这里找到一点运气,有人已经解决了这个问题,可以提供见解

有人已经为此奋斗过了吗

更新:我添加了更多的信息,因为我问了这个问题。我尝试使用JSON.Net作为我的格式化程序,如下所示。我认为这将是最终的解决方案,然而,仅仅按照下面的海报建议做是不够的

使用JSON.Net序列化程序时,出现以下错误:

此DataController不支持实体“JObject”的操作“Update”

原因是JSON.Net没有完全填充格式化程序试图反序列化到的对象(System.Web.Http.Data.ChangeSet)

发送的json是:

[{"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}
}]
内置的Json格式化程序能够将此Json重新构造为一个变更集对象,该变更集对象在Entity和OriginalEntity字段中嵌入了TodoItem对象


有人让JSON.Net正确地反序列化了吗?

问题是,在当前的测试版中,ASP.Net Web API使用了
DataContractJsonSerializer
,它在
DateTime
的序列化方面存在众所周知的问题。最近,微软Connect因该问题引发了一个安静的bug;MS回应说,他们已经有一个跟踪问题的bug,但它不会在.Net 4.5/VS11时间范围内修复

幸运的是,您可以替换另一个JSON序列化程序,例如优秀的

ASP.NET团队的Henrik Nielsen发布了一篇文章,展示了如何将JSON.NET与ASP.NET Web API结合使用。下面是他使用JSON.Net实现的
MediaTypeFormatter
(它还需要连接到ASP.NETWebAPI配置,Henrik的博客也演示了这一点)

公共类JsonNetFormatter:MediaTypeFormatter
{
私有只读JsonSerializerSettings设置;
公共JsonNetFormatter(JsonSerializerSettings=null)
{
this.settings=设置??新建JsonSerializerSettings();
添加(新的MediaTypeHeaderValue(“应用程序/json”);
编码=新的UTF8编码(假、真);
}
受保护的覆盖布尔CanReadType(类型)
{
返回类型!=typeof(IKeyValueModel);
}
受保护的覆盖布尔CANRITETYPE(类型)
{
返回true;
}
受保护的重写任务OnReadFromStreamAsync(类型类型、流、HttpContentHeaders contentHeaders、FormatterContext FormatterContext)
{
var ser=JsonSerializer.Create(设置);
返回Task.Factory.StartNew(()=>{
使用(var strdr=newstreamreader(stream))
使用(var jtr=new JsonTextReader(strdr))
{
var deserialized=ser.Deserialize(jtr,类型);
返回反序列化;
}
});
}
受保护的重写任务OnWriteToStreamAsync(类型类型、对象值、流、HttpContentHeaders contentHeaders、FormatterContext FormatterContext、TransportContext TransportContext)
{
JsonSerializer ser=JsonSerializer.Create(设置);
返回Task.Factory.StartNew(()=>
{
使用(JsonTextWriter w=newjsontextwriter(newstreamwriter(流,编码)){CloseOutput=false})
{
序列序列化(w,值);
w、 冲洗();
}
});
}
}    

JSON.NET需要$type,而您有u type来指定实体类型,以便它将其转换为JObject

我用下面这句废话说服了他

首先确保
JsonSerializerSettings
具有
.typenameholling=Newtonsoft.Json.typenameholling.Objects

然后编写自己的```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;
        }
    }
}

并使用它通过使用(MyJsonTextReader jsonTextReader=new MyJsonTextReader(streamReader))反序列化Json,我遇到了完全相同的问题。我花了太多时间试图让json.net正常工作。我最终提出了一个解决方案,您可以在示例项目中坚持使用doitemsviewmodel.js:

    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);
周围的代码行包含在上下文中。我在以下评论中发现了这一点:

您还需要将_Editor.cshtml中的html更改为绑定到“DateJson”,而不是“EnterDate”


这当然是一个难题,但它有工作的优点,这不是一个小壮举。

您还可以通过添加以下代码使JQuery日历弹出窗口工作

在示例MVC 4 SPA项目中的TodoItemsViewModel.js底部添加以下内容:

    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) {
    }
};
这是绑定到日期选择器的_Editor.cshtml代码的外观

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

输入日期:
@*
*@

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