Okay so I have a simple chat app set up in an ASP.NET MVC 4 project using SignalR, Web API 2, JQuery and KnockoutJS.
A user must be logged in to post or comment. When they post or comment on a post, it goes to the Hub and inserts that post/comment into the database. On load of the chat page, a $.get call is made to the API controller to get all posts and comments related to each post.
public IList<Post> Get()
{
var allPosts = this.db.Posts.OrderByDescending(x => x.DatePosted).ToList();
foreach (var post in allPosts)
{
var allCommentsOnThisPost = db.Comments.Where(p => p.ParentPost.PostId == post.PostId).ToList();
foreach (var comment in allCommentsOnThisPost)
{
post.Comments.Add(comment);
}
return allPosts;
}
}
This works fine and all of the past posts and comments are added to the view. However, if a user tries to to add a comment to a post previous post (one that was posted before refreshing the page), the call to the Hub fails with this detailed error message:
[17:10:30 GMT-0500 (Central Daylight Time)] SignalR: Error converting value {null} to type 'System.Int32'. Path '', line 1, position 4.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Microsoft.AspNet.SignalR.Json.JRawValue.ConvertTo(Type type)
at Microsoft.AspNet.SignalR.Hubs.DefaultParameterResolver.ResolveParameter(ParameterDescriptor descriptor, IJsonValue value)
at System.Linq.Enumerable.<ZipIterator>d__7a`3.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Microsoft.AspNet.SignalR.Hubs.DefaultParameterResolver.ResolveMethodParameters(MethodDescriptor method, IList`1 values)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.InvokeHubPipeline(IHub hub, IJsonValue[] parameterValues, MethodDescriptor methodDescriptor, HubRequest hubRequest, StateChangeTracker tracker). jquery.signalR-2.0.3.js:76
[17:10:30 GMT-0500 (Central Daylight Time)] SignalR: chathub.AddComment failed to execute. Error: Error converting value {null} to type 'System.Int32'. Path '', line 1, position 4.
My classes are fairly simple:
public class Post
{
[Key]
public int PostId { get; set; }
public string Message { get; set; }
public string Username { get; set; }
public DateTime DatePosted { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
[Key]
public int CommentId { get; set; }
public string Message { get; set; }
public string Username { get; set; }
public DateTime DatePosted { get; set; }
public virtual Post ParentPost { get; set; }
}
ChatHub:
public class ChatHub : Hub
{
public void WritePost(string username, string message)
{
var db = new DBContext();
var post = new Post { Message = message, Username = username, DatePosted = DateTime.Now };
db.Posts.Add(post);
db.SaveChanges();
Clients.All.receivedNewPost(post.PostId, post.Username, post.Message, post.DatePosted);
}
public void AddComment(int postId, string comment, string username)
{
var db = new DBContext();
var post = db.Posts.FirstOrDefault(p => p.PostId == postId);
if (post != null)
{
var newComment = new Comment { ParentPost = post, Message = comment, Username = username, DatePosted = DateTime.Now };
db.Comments.Add(newComment);
db.Entry(post).State = EntityState.Modified;
db.SaveChanges();
Clients.All.receivedNewComment(newComment.ParentPost.PostId, newComment.CommentId, newComment.Message, newComment.Username, newComment.DatePosted);
}
}
}
So what I'm wondering is if the error has something to do with the Newtonsoft JSON serialization/deserialization. As you can see in the code above, Posts and Comments are stored in the database with a DateTime.Now timestamp, and so I'm not sure what the issue is?
Any help would be appreciated!
EDIT: Here is the relevant Client-Side code:
(function () {
var hub = $.connection.chatHub;
$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
loadPosts();
});
function loadPosts() {
$.get('/api/posts', function (data) {
var postsArray = [];
for (var i = 0; i < data.length; i++) {
var newPost = new post(data[i].Id, data[i].Message, data[i].Username, data[i].DatePosted);
if (data[i].Comments != null) {
for (var j = 0; j < data[i].Comments.length; j++) {
var newComment = new comment(data[i].Comments[j].Id, data[i].Comments[j].Message, data[i].Comments[j].Username, data[i].Comments[j].DatePosted);
newPost.comments.push(newComment);
}
}
vm.posts.push(newPost);
}
});
}
var vm = {
posts: ko.observableArray([]),
notifications: ko.observableArray([]),
username: ko.observable('@SignalR.UsersDictionary.UsersDict[User.Identity.Name]'),
writePost: function () {
if ($('#message').val().length > 0) {
$.connection.chatHub.server.writePost(vm.username(), $('#message').val()).done(function () {
$('#message').val('');
});
}
},
}
var post = function (id, message, username, date) {
this.id = id;
this.message = message;
this.username = username;
this.date = date;
this.comments = ko.observableArray([]);
this.addComment = function (context) {
var comment = $('input[name="comment"]', context).val();
if (comment.length > 0) {
$.connection.chatHub.server.addComment(this.id, comment, vm.username())
.done(function () {
$('input[name="comment"]', context).val('');
});
}
};
}
var comment = function (id, message, username, date) {
this.id = id;
this.message = message;
this.username = username;
this.date = date;
}
$(function () {
ko.applyBindings(vm);
});
}());