Archive for June, 2012|Monthly archive page
Syncronizing time/watch between javascript client and ASP.NET MVC 4 server
I just wished working with Date() in Javascript was easier.
Recently here at 4N1, we had to make some date-time calculations on the client-side of a web application. The app arquitecture is:
Client side
- Html 5: Just added the 5 to look cool.
- Javascript: You know what that is.
- Knockout JS: An amazing Javascript MVVM framework.
- TaffyDB: An awesome Javascript database.
Server side
- ASP.NET MVC 4 Beta: ASP.NET MVC 3 with some new stuff.
- SQL Server 2008 R2: A relational database server.
Since the client and the server time can be different, we need a way to know exactly how much it is to adjust our calculation. Talking with an old colleague, Rui Milagaia, the following solution came up:

A device is anything that has a time. This logic can be applied in any scenario(computer A to computer B, client to server, server to server, device A to device B, etc).
NOTE: This solution will not guarantee milliseconds precision but if your doing calculations that go only down to the second, it should be ok.
Back to our app, since is javascript, I’ve used jquery to perform an ajax request to an MVC Action. I just needed to guarantee that the request was synchronous, otherwise the asynchronous behavior adds noise to the calculated value. I’ve created a github repository with the source code here. You can use it with another server side technologies (JSP, Rails, Grails, etc). Here is the important steps:
Client side
- Add a reference to the 4n1.timesync.js file:
<!-- jQuery 1.5 or later is a foranyone.timeSync dependency --> <script src="jquery-1.6.4.js" type="text/javascript"></script> <!-- Reference to the 4n1.timeSync script file--> <script src="4n1.timeSync.js" type="text/javascript"></script> <!-- To make it easier to parse date strings with the ISO 8601 format, we use the library from https://github.com/csnover/js-iso8601 --> <script src="iso8601.js" type="text/javascript"></script>
-
Set the expected properties, in my case only the url to the MVC Action, and call the foranyone.timeSync.getTimeDifference function to retrieve the time difference in milliseconds.
foranyone.timeSync.url = "www.somedomain.com/ServerTime/GetServerTime"; // This variable now has the difference from the client and the server in milliseconds. It can be positive or negative. var timeDifference = foranyone.timeSync.getTimeDifference();
- Just implement an action that returns the server time:
public class ServerTimeController { public ActionResult GetServerTime() { // Using a Iso format string without all the milliseconds because IE cannot process it :p. return Json(DateTime.Now.ToString("yyyy'-'MM'-'ddTHH':'mm':'ss.fff%K"), JsonRequestBehavior.AllowGet); } }
Server side
And thats it. Happy syncronization!
Kockout performance tip #1
After reading two posts(#1 and #2) from one of the Knockout project members, Ryan Niemeyer, I did some performance testing on my application and found an improvement:
The Problem: Rendering and processing elements that are not visible
<table data-bind="foreach: Items"> <tr> <td data-bind="text: Name"></td> <td> <input type="button" data-bind="click: edit" value="Edit"/> </td> </tr> <tr data-bind="visible: IsEditing()"> <td colspan="4" style="background: white;"> <table data-bind="foreach: SubItems"> <tr> <td data-bind="text: Name"></td> </tr> </table> </td> </tr> </table>
This code creates a table with two rows for each Master Item (in the Items list):
- A row with the Item name and an Edit button
- Another row with an inner table with the list of the child items (in the SubItems list).
The thing is, the second row only appears when the user clicks on the Edit button (callind the Item.edit method, witch in turn changes the IsEditing to true). With the code above, Knockout was processing all bindings (even the ones from the SubItems). Depending on the amount of SubItems or bindings, the ko.applyBindings method can taking quite some time.
The Solution: Using an If binding to control witch elements are rendered and processed
<table data-bind="foreach: Items"> <tr> <td data-bind="text: Name"></td> <td> <input type="button" data-bind="click: edit" value="Edit"/> </td> </tr> <tr data-bind="if: IsEditing()"> <td colspan="4" style="background: white;"> <table data-bind="foreach: SubItems"> <tr> <td data-bind="text: Name"></td> </tr> </table> </td> </tr> </table>
Changing the visible binding to the if binding, made Knockout only process and render the details row (with all the SubItems) when the Master Item was being edited. In my page (a lot more complex than the example above and with a lot of data), it made the ko.applyBindings execution 2 seconds faster.
ASP.NET MVC4/Upshot/Knockout: Remote + Local DataSource
The SPA (Single Page Application) I’m creating, does not sync all the changes with the server using the upshot RemoteDataSource. Because of that, I had to use a mixed approach, that is, a RemoteDataSource plus a LocalDataSource. The remote one brings the data from the server, while the local is used to make changes that will update the User interface, without going to the server. Here is the code:
// Used to "construct" new Model instances
function Model(data) {
var self = this;
// add properties from the JSON data result
upshot.map(data, "SomeEntity:#SomeNamespace", self);
// add properties managed by upshot
upshot.addEntityProperties(self);
};
function ViewModel() {
self = this;
// Code generated by the Server-side @(Html.UpshotContext(true).DataSource<SomeNamespace.EntitiestController>(x => x.GetEntities()))
upshot.metadata({ "SomeEntity:#SomeNamespace": { "key": ["Id"], "fields": { "Id": { "type": "String:#System" } }, "rules": {}, "messages": {}} });
upshot.dataSources.Items = upshot.RemoteDataSource({
providerParameters: { url: "/api/Entities?action=", operationName: "GetEntities" },
entityType: "SomeEntity:#SomeNamespace",
bufferChanges: true,
dataContext: undefined,
mapping: {}
});
// Creates a reference to the items returned by the remote datasource
self.dataSource = upshot.dataSources.Items.refresh();
// Create a local datasource that references the items returned by the remote datasource
self.localDataSource = upshot.LocalDataSource({ source: self.dataSource,
autoRefresh: true, allowRefreshWithEdits: true
});
// Creates a filter to hide the deleted items (Since we are not syncing the changes, they will keep appearing on the UI otherwise)
self.localDatasource.setFilter({ property: 'IsDeleted', value: false, operator: '==' });
// Creates a reference to the local datasource entities to use it on the UI binds
self.items = self.localDataSource.getEntities();
self.addNew = function () {
// Create a new item
var item = new Model(null);
// Pushes the item to the list of items. This will update the UI
self.items.push(item);
}
self.remove = function (item) {
// Removes the item from the localDatasource
self.localDataSource.deleteEntity(item);
// To update the UI, you have to refresh the local datasource (don't know why it does not do automatically)
self.localDataSource.refresh();
}
}
The autoRefresh property is self-explanatory. The important one here is the allowRefreshWithEdits property, that will allow us to execute refresh’s on the local datasource while having changes (it triggers an error if you don’t do this).
Another important thing is to add a filter to hide all the deleted entities at the local datasource. Since they are not commited to the server, they will still appear if you delete them and don’t apply a filter
Now, the user interface just have to bind to the items from the localDataSource, not the remote one:
<script type="text/javascript">
$(function () {
// Creates a ViewModel instance
viewModel = new ViewModel();
ko.applyBindings(viewModel);
}
</script>
<div data-bind="foreach: items">
<!-- Do something to show the items -->
<div>
ASP.NET MVC 4 ApiController serialization error: No readonly properties serialized and System.Runtime.Serialization.InvalidDataContractException
I’m starting a new project using the ASP.NET MVC 4 runtime. I’m using the new ApiController feature, really cool indeed. I had a business entity (domain class) that I wished to expose. But since the ApiController default serializer don’t write readonly properties, the best answer I found on the net was to create a ViewModel wrapping an instance of my business entity exposing the properties I wanted. Check the code:
public class DomainEntityApiController : ApiController
{
// GET /api/domainentityapi
public IEnumerable<DomainEntityViewModel> Get()
{
List<DomainEntityViewModel> list = new List<DomainEntityViewModel>();
for (int i = 0; i < 10; i++)
{
list.Add(new DomainEntityViewModel(new DomainEntity(i + "Id", i + "Name")));
}
return list;
}
}
public class DomainEntityViewModel
{
private DomainEntity _entity;
public DomainEntityViewModel(DomainEntity entity)
{
_entity = entity;
}
public String Id
{
get { return _entity.Id; }
set
{
// Do nothing
}
}
public String Name
{
get { return _entity.Name; }
set
{
// Do nothing
}
}
}
public class DomainEntity
{
private String _id;
private String _name;
public DomainEntity(String id, String name)
{
this._id = id;
this._name = name;
}
public String Id
{
get { return _id; }
}
public String Name
{
get { return _name; }
}
}
Compiled. Went to http://localhost/api/DomainEntityApi and BANG! Error:
Type ‘TakeThatTime.Web.Models.DomainEntityViewModel’ cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
body {font-family:”Verdana”;font-weight:normal;font-size: .7em;color:black;}
p {font-family:”Verdana”;font-weight:normal;color:black;margin-top: -5px}
b {font-family:”Verdana”;font-weight:bold;color:black;margin-top: -5px}
H1 { font-family:”Verdana”;font-weight:normal;font-size:18pt;color:red }
H2 { font-family:”Verdana”;font-weight:normal;font-size:14pt;color:maroon }
pre {font-family:”Lucida Console”;font-size: .9em}
.marker {font-weight: bold; color: black;text-decoration: none;}
.version {color: gray;}
.error {margin-bottom: 10px;}
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
After a long research about the error and nothing found (at least not on google first result page
) I tried something: Putting a no args constructor on the ViewModelClass, like this:
public class DomainEntityViewModel
{
private DomainEntity _entity;
// Constructor that does nothing just because of the serialization issue
public DomainEntityViewModel()
{
throw new NotSupportedException();
}
public DomainEntityViewModel(DomainEntity entity)
{
_entity = entity;
}
public String Id
{
get { return _entity.Id; }
set
{
// Do nothing
}
}
public String Name
{
get { return _entity.Name; }
set
{
// Do nothing
}
}
}
And tried again. And it worked. Here is the response now:
<?xml version="1.0" encoding="utf-8"?><ArrayOfDomainEntityViewModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><DomainEntityViewModel><Id>0Id</Id><Name>0Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>1Id</Id><Name>1Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>2Id</Id><Name>2Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>3Id</Id><Name>3Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>4Id</Id><Name>4Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>5Id</Id><Name>5Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>6Id</Id><Name>6Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>7Id</Id><Name>7Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>8Id</Id><Name>8Name</Name></DomainEntityViewModel><DomainEntityViewModel><Id>9Id</Id><Name>9Name</Name></DomainEntityViewModel></ArrayOfDomainEntityViewModel>
Leave a Comment