ASP .NET MVC datetime editor template using jQuery datepicker

This post details how to create an ASP .NET MVC editor template for the DateTime? datatype which will show a jQuery datepicker: 

  1.  Add the the jQuery UI js, css and Images to solution:
    • I have put the jQuery images and css under /Content/Images and /Content respectively.  
    • and the js under the /Scripts
  2. Update your site.master to reference the js and css:
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <link href="../../Content/jquery.ui.all.css" rel="stylesheet" type="text/css" />

    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"/>
    <script src="../../Scripts/jquery.ui.core.js" type="text/javascript" />
    <script src="../../Scripts/jquery.ui.widget.js" type="text/javascript" />
    <script src="../../Scripts/jquery.ui.datepicker.js" type="text/javascript" />

    <script src="/Scripts/MicrosoftAjax.js" type="text/javascript" />
    <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript" />
    <script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript" /> 
  1. Create the Editor Template:
    • Create the following folder structure if not already present Views/Shared/EditorTemplates/
    • Add a ViewUserControl called DateTime.ascx and use the following as thee content.
  2. Note: the htmlAttributes parameter is new { @class = “dp”}) this will render a class=’dp’ attribute for later use by jQuery to identify where datepickers are required

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>" %>
<%: Html.TextBox("",  String.Format("{0:yyyy-MM-dd}", Model.HasValue ? Model : DateTime.Today), new { @class = "dp"})%>
    Note: that I have set this to accept a nullable datetime – DateTime? and provided the default value of today if the date way empty.

  1.  JQuery datepicker script:
    • Place the following jQuery preferably in a Common.js or in the head of your page or site master wher ethe control will be displayed.
<script  type="text/javascript">
 $(document).ready(function () {
 $(".dp").datepicker({
             changeMonth: true,
             changeYear: true,
             dateFormat: 'yy-mm-dd'
                   });
                });
  </script>

Note: that we are searching for anything using the “.dp” class and supplying arguments to the datepicker to format the date and provide dropdowns for year and month.

At this point any defined view that you have that is rendering the DateTime should now show the editor as the DateTime.ascx editor template

E.g I have a view binding to my Model and one of the properties called StartDate is DateTime?  the view for displaying this model contains
    

  
<div class="editor-label">
    <%: Html.LabelFor(model => model.StartDate) %>
</div>
<div class="editor-field">
    <%: Html.EditorFor(model => model.StartDate)%>
    <%: Html.ValidationMessageFor(model => model.StartDate) %>
</div>

Upon rendering the DateTime editor template is applied and hey presto:

Nick

Debugging WCF Data Services – An error occured while processing this request

Issue: I was calling my WCF Data Service today and was getting a NotSupportedException with an InnerException of “An error occured while processing this request” and no further detail which is not very helpful for debugging.

Solution: So the workaround for this is to set UseVerboseErrors in your service in your service initialization method like so

public static void InitializeService(DataServiceConfiguration config)
{
   config.UseVerboseErrors = true;

  .....

}

This however is not good for production scenarios as we don’t want Verbose errors going back to clients.  Given that it is hardcoded to true ideally you would want to be able to configure this in your web.config.  There is no section within the web.config supported out of the box for this.  So you can use an appSetting or create your own custom section.

after building and deploying the service you should now get the full detail in your exception.InnerException.Message E.g

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-AU">An error occurred while processing this request.</message>
  <innererror>
    <message>Unable to update the EntitySet 'Posts' because it has a DefiningQuery and no &lt;InsertFunction&gt; element exists in the &lt;ModificationFunctionMapping&gt; element to support the current operation.</message>
    <type>System.Data.UpdateException</type>
    <stacktrace>   at System.Data.SqlClient.SqlGen.DmlSqlGenerator.ExpressionTranslator.Visit(DbScanExpression expression)&#xD;
   at System.Data.SqlClient.SqlGen.DmlSqlGenerator.GenerateInsertSql(DbInsertCommandTree tree, SqlVersion sqlVersion, List`1&amp; parameters)&#xD;
   at System.Data.SqlClient.SqlProviderServices.CreateCommand(DbProviderManifest providerManifest, DbCommandTree commandTree)&#xD;
   at System.Data.Mapping.Update.Internal.UpdateTranslator.CreateCommand(DbModificationCommandTree commandTree)&#xD;
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.CreateCommand(UpdateTranslator translator, Dictionary`2 identifierValues)&#xD;
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)&#xD;
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)&#xD;
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)&#xD;
   at System.Data.Services.Providers.ObjectContextServiceProvider.SaveChanges()&#xD;
   at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)&#xD;
   at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
  </innererror>
</error>

Nick

Windows Phone 7 OData CTP, nope use REST and JSON

First things first the OData CTP for Windows Phone 7 is exactly that – a CTP - therefore expectations are not high but I did have them set at medium.  Due to the issues I was hitting i ended up having to use REST to consume my WCF data service then write a bunch of code to deserialize the JSON response. So this post will take you through how to do this.

But first, what issues did I encounter – well after being able to successfully to generate the service reference using dataservice util I was able to consume the service successfully and i felt yay big ticks this is awesome.  I then went to implement serverside paging, cool serverside paging out of the box thats pretty neat.  However when I went to then consume the next page from the service the Continuation Token was always null.  This ment that I was unable to navigate to the next page set with the link that should have been returned.  Note:  - if anyone managed to get this working with the CTP please drop me a link and/or some sample code.

Checking out Nicks blog – Windows Phone 7 Data: Json WCF Data Service with IIS 7 Compression I figured out how to construct a REST request using fiddler i tried by application/atom+xml and application/json – both results had the next link :( … With this in mind i really had to narrow down if it was the CTP or not – I created a .NET 4.0 win forms app and generated the service reference  and used the same code to consumed it successfully with the Continuation token being set.  At this point i settled that it is something was from the CTP….

After spending a lot of time trying to get to the bottom of it – using reflector to figure out where the error was coming from because the OData CTP source was not available i finaly decided to cut my losses and just consume the feed using REST.  Funnyly enough, but not so funny at the time, it took less time to write the data contract to deserialize the JSON then I spent fighting the CTP.  To do this I did the following:

Consume your service using REST to see the format of the returned data:

1. Construct request in this case to Posts and add the header accept: application/json then press execute

REST request to WCF Data service

REST request to WCF Data service

2. Take the response and format it using an online formatter to make life easy on yourself

{
   “d”:{
      “results”:[
         {
            "__metadata":{
               "uri":"http://localhost/TestServices/SomeService.svc/Posts(guid'ccb89791-a49b-4224-924d-0e0bc56d8ffb')",
               "type":"Model.Post"
            },
            "Id":"ccb89791-a49b-4224-924d-0e0bc56d8ffb",
            "Title":"some title",
            "Content":"Some long content"
         }
      ],
      “__next”:”http://localhost/TestServices/SomeService.svc/Posts?$skiptoken=guid’ccb89791-a49b-4224-924d-0e0bc56d8ffb’”
   }
}

3. Create your DataContract so that it can consume a response of the format above.  Note the nasty bit about this is the nesting of the above.  I created a generic representation of this so that this can be re-used for your other tables.  So this is what we are building:

Datacontract for JSON deserialization of WCF Data Service

Datacontract for JSON deserialization of WCF Data Service

and the code is as follows: Note the name of the data member maps to the sample JSON for the serialization / deserialization of the JSON content:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace JSON.DataContract
{
    [DataContract] //Gets the outer {}
    public class BaseJSONResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "d")] //Gets the d
        public ComposedResult<T> Result { get; set; }
    }

    [DataContract]
    public partial class ComposedResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "results")]
        public List<T> MyContents { get; set; }

        [DataMember(Name = "__next")]
        public string NextLinkUri { get; set; }
    }

    [DataContract(Name = "__metadata")]
    public class MetaData
    {
        [DataMember(Name = "uri")]
        public string Uri { get; set; }

        [DataMember(Name = "type")]
        public string Type { get; set; }
    }

    [DataContract]
    public abstract class BaseMyContent
    {
        [DataMember(Name = "__metadata")]
        public MetaData MetaData { get; set; }
    }

    public partial class Post : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Content { get; set; }
    }

    [DataContract]
    public partial class SomeOtherClass : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Caption { get; set; }
    }
}

Note: if you have more types you would like to extend the support to just derrive them from BaseMyContent like SomeOtherClass above.
Note: this example does not currently take care of Links between types… but you should easily be able to add support for this.

4. Usage: Use a web request and response and Json serializer to process the results

   HttpWebRequest request = HttpWebRequest.Create("http://localhost/TestServices/SomeService.svc/Posts") as HttpWebRequest;
   request.Accept = "application/json"; //atom+xml";
   request.BeginGetResponse(RequestCallback, request);

The callback where deserialization occurs, post will contain the deserialize content

private void OnRequestCallback(IAsyncResult result)
{
     var request = result.AsyncState as HttpWebRequest;
     var response = request.EndGetResponse(result);
     if (response != null)
     {
          var jsonSerializer = new DataContractJsonSerializer(typeof(BaseJSONResult<Post>)); //Note: DataContractJsonSerializer requires a reference to System.Servicemodel.Web

          BaseJSONResult<Post> post = null;
          using (var stream = response.GetResponseStream())
          {
               post = jsonSerializer.ReadObject(stream) as BaseJSONResult<Post>;
          }

          this.Dispatcher.BeginInvoke(() =>
             PostRetrieved(Post));
     }
}

Now finally you can get at a populated Next link with post.NextLinkUri *wipes teary eyes*

5. If you are having troubles and the deserialization is not working the best tip i can give you is to try serializing your datacontract and cross check it with the data returned from your service. To serialize it do the following:

                
var test = new BaseJSONResult<Post>()
{
    // Make sure you set all the properties
};
using (MemoryStream ms = new MemoryStream())
{
    jsonSerializer.WriteObject(ms, test);
    byte[] bytes = ms.ToArray();
    string json = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
    Console.WriteLine(json); //Compare this with your fiddler result
}

I really can’t wait until the CTP comes out full form with the bugs fixed, it will save a lot of time, but for now I am keeping a wide berth with the intention of returning once it is out of CTP.

Some first time Azure deployment findings

Today I completed my first couple of deployments of an OData service to Windows Azure hosted in the Geographic Location of “Anywhere Asia”.

What I liked about the dev experience – As  a complete Azure newb I was able to do the following in under a day :

  1. Migrate an existing SQL 2008 R2 DB to Azure SQL
  2. Connect remotely to the new Azure SQL DB using SQL Management Studio
  3. Create and deploy an OData Service that is fed from the Azure SQL DB
  4. Get some blobs into the Blob storage
  5. Consumed the OData service and Blob from a Windows Phone 7 Application – All I had to do was update the client I created here Consuming an OData service from windows phone 7 to point my TestClient URI to the cloud service rather then my local. 
  6. 16 Months Free*

What I didn’t like:

  1. Could not find support in Visual Studio 2010 to deploy to SQL Azure directly.  If anyone is aware how please throw a link at me.  I had to gen deployment scripts using SQL Management Studio.  VS 2010 Data compare did not work when compairing between SQL Server 2008 R2 and SQL Azure. Note I did find this project on codeplex which might be handy – SQL Azure Migration Wizard v3.3.5

  2. newsequentialid() is not supported in SQL Azure - makes sense given that the sequential id is partially generated based on the network card therefore does not fit well with sql azure running across multiple machines.  But the issue is to get it up and running quickly i had to update to use newid() so now not sure how fragmented my indexes are going to become and this will have an impact on my insert performance so I will need to revise this.

  3. Unable to upload to blob storage from within Server Explorer.  You can only view your blobs as per image

    below and I had to code a client application just to upload the simple HelloWorld test file you see below :( … Would be great to see an Upload option in the context menu.

    Server explorer azure no blob upload
    Server explorer azure no blob upload
  4. Time to taken to deploy the service that contained only a simple Entity Model and single OData service was quite a while.  But when you look at whats going on under the hood its pretty damn quick althought it would be great to have the output window for the azure deployment indicate exactly whats happening rather then just a few initialising, busy, running statements etc.
  5. If anyone has any helpful links to the issues above – please feel free to post the links in the comments.
Nick

Change firewall rule for sql management studio connection to SQL Azure

To connect to SQL Azure from Sql Management Studio you need to change your SQL Azure firewall settings to allow access for your public IP. You can do this as follows: 1. Login to your Azure account Select SQL Azure –> Database
2. Select the Firewall Settings Tab
3. Select Add Rule and enter your IP address (in place of the 000.000.000.000). Note: your IP will be displayed under the IP Range text boxes

Azure SQL DB Firewall Rules

Azure SQL DB Firewall Rules

4. Press submit

5. Withing  ~5minutes you should be able to connect directly to your SQL Azure instance.  Note: the server name you supply to SQL management studio can be found in the Server Information Group of the image above.

Nick