Microsoft Live Spaces goes to WordPress

I've used and hated Microsoft Live Spaces for blogging.

Finally Microsoft did what they should have done years ago, they killed it.

 

They are getting WordPress to do all the blogs, migration path starts here:

http://windowsteamblog.com/windows_live/b/windowslive/archive/2010/09/27/wordpress-com-and-windows-live-partnering-together-and-providing-an-upgrade-for-30-million-windows-live-spaces-customers.aspx

http://en.blog.wordpress.com/2010/09/27/welcome-windows-live-spaces-bloggers/

 

As long as they retain the Windows Messenger Live integration (which is the only thing I liked about Live Spaces), and give me the extensibility and tools of WordPress.  This is just great for their customers.

Silverlight - gzip compression for your Domain and Duplex service(s)

I've been pretty unhappy with the data that's been thrown back and forth between a RIA DomainService and the Silverlight client.  I feel it's too big, too heavy.  When everything is running on localhost, you can't feel this.  But the moment you put everything onto an external staging server (or the production) - it's suddenly painfully obvious that perhaps some of the services are just way too chatty, that's a post for another day.

While thinking about this on my way to the car after work, I suddenly remembered that I still haven't turned on gzip compression for the production server.  A grin comes to my face, cause I know this would be an easy win.

 

Profile

First, measure your existing service calls.  You can do this easily with Fiddler 2, which lets you watch all HTTP traffic between your browser (where the Silverlight client sits), and the server, where the Domain and Duplex services are busily responding.

image

Figure: Domain Service request call

image

Figure: Duplex service call

 

Enable GZIP compression on Server

Enabling GZIP compression on the server, in 3 easy steps

1. The server has to know explicitly you want to compress these new content types.

Open C:\Windows\System32\inetsrv\config\applicationHost.config  as administrator

Modify the dynamic compression section, add our two new content types

Note; the exact mimetype depends on your service binding, and whether you choose to use text encoding.  Check Fiddler data if you aren't sure.

 

image

 

2. Enable dynamic compression via IIS on your web application

image

 

3. Recycle the AppPool (or the more catastrophic IISRESET, if you prefer that)

 

Result - test again

Refresh your Silverlight page, and let's see the new data

image

Figure: compressed domain service

 

image

Figure: compressed duplex service

 

Conclusion

I've picked a service call that's only returning 1 row of data, and it's shrunk the data size by 60%.  Over large chunks of data, I expect the compression to be significantly higher.

Smaller messages means less time for the browser to receive the response message, and since browsers already take care of compression and encoding, Silverlight doesn't even need to know that the message has arrived compressed.

Result is a small speed improvement over external sites with only a few simple steps Smile

Silverlight GeocodeLocation SerializationException when calling RouteService CalculateRoute

I was doing some geocoding via the Bingmap service, and received results of GeocodeLocation type.  I incorrectly assumed that since these GeocodeLocation inherits from the base Location class, I could just pass them back into the RouteService.

The exception is basically telling me that it doesn't know how to serialize a GeocodeLocation type, it wants just a plain Location type.

 

Fix:

waypoint.Description = loc.Result.DisplayName;
var geocodeLocation = loc.Result.Locations[0];
waypoint.Location = new Microsoft.Maps.MapControl.Location(geocodeLocation.Latitude, geocodeLocation.Longitude);

 

Note:

If you stick with the Microsoft supplied example code, you won't see this problem

private Waypoint GeocodeResultToWaypoint(GeocodeResult result)
{
    Waypoint waypoint = new Waypoint();
    waypoint.Description = result.DisplayName;
    waypoint.Location = new Microsoft.Maps.MapControl.Location();
    waypoint.Location.Latitude = result.Locations[0].Latitude;
    waypoint.Location.Longitude = result.Locations[0].Longitude;
    return waypoint;
}

 

 

(warning scary looking exception below).

{System.Runtime.Serialization.SerializationException: Type 'DispatchKing.Silverlight.GeocodeService.GeocodeLocation' with data contract name 'GeocodeLocation:http://dev.virtualearth.net/webservices/v1/common' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteWaypointToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteArrayOfWaypointToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
   at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteRouteRequestToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)}

MVC2 - File upload with HttpPostedFileBase

 

ASP.NET MVC2 makes it extremely easy to upload a file to your controller. 

Scott Hanselman blogged about this in detail

http://www.hanselman.com/blog/ABackToBasicsCaseStudyImplementingHTTPFileUploadWithASPNETMVCIncludingTestsAndMocks.aspx

The article is a bit dated, so there are a few small tweaks for MVC 2.

 

View

    <% using(Html.BeginForm("Upload CSV", "Customer", FormMethod.Post, new { enctype = "multipart/form-data" })){ %>
    <%: Html.ValidationSummary() %>
    
        <input type="file" id="fileUpload" name="fileUpload"/>

        <input type="submit" value="Upload file" />

    <% } %>

  • You need to use FormMethod.Post
  • You need to make sure encoding type is multipart/form-data - otherwise the server will not receive anything!
  • ValidationSummary isn't really needed, but you might want some place to throw up some errors.
  • The name of the input type="file" needs to match the argument in the controller, in my case "fileUpload"

 

Controller

        [HttpPost]
        public virtual ActionResult UploadCustomer(HttpPostedFileBase fileUpload)
        {
            if (fileUpload == null)
            {
                // problem
                return;
            }
            if (fileUpload.ContentLength == 0)
           {
                // problem
                return;
           }

           var reader = new StreamReader(fileUpload.InputStream);

 

  • The binder will automatically take the uploaded file and stick it in the HttpPostedFileBase class.
  • You can check it's status, and then grab the InputStream and read (and write out) the stream if you need to.

 

Gotchas

 

Some AJAX libraries sometimes like to hijack a Form submit.  In our case, it was turning the multipart/form-data back to application/x-www-form-urlencoded - which will not work.

If fileUpload remains null in the controller, check your Request object on the controller and see what's the encoding used on the post back.

VB.NET - there are times when you have to keep an open mind

I suddenly found myself needing to write a very simple upload method in our MVC controller.  The uploaded file is in CSV format.

The question that immediately jumps to mind is, where do I find a CSV parser?

  • Do I write my own - it may sound simple, but as soon as you start to think about all the various permutations of splitting a line of text, with all the escape characters as well as error handling, actually, it is NOT simple at all
    1. How do you handle the case where the user actually wants to use comma (,) in a field
    2. Do you include or exclude header row
    3. How do you handle line numbers and parse error
    4. What do you do with extra spaces or tabs
    5. How do you handle line breaks
    6. What about lines that didn't have matching number of fields
  • Do I then, just re-use someone else's example
  • Or do I use the one that's supplied and supported by Microsoft, and already installed on your machine as part of the .NET Framework since .NET 2.0?

The answer is blindingly obvious - use Microsoft's one - move on, next problem.

  • Well, it's in Microsoft.VisualBasic.FileIO

Hmm, let me think about this again…

 

This is where I find many C# developers suffer, we would rather build our own untested, unsupported code to do CSV parsing, either with String.Split(',') or with clever Regex.  We're convinced that our own code is better somehow than the Microsoft supplied library.  We're so convinced, we'll run off and spend 30minutes building our own parser, we'll follow by spending 2 hours on testing our parser and making sure it's bulletproof.

So I took a good look at myself and stopped.  I said to myself. 

No, I'll be pragmatic.

 

The Microsoft VB.NET CSV parser is part of the TextFieldParser class.  Documented here:

http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx

 

Through the Common Language Runtime (CLR) we can include Microsoft.VisualBasic library easily in our C# project.  Here they are, happily (may be) next to each other, and doing work for me.

image

 

The actual code is extremely simple:

using (TextFieldParser reader = new TextFieldParser(fileUpload.InputStream))
{
    reader.TextFieldType = FieldType.Delimited;
    reader.Delimiters = new[] { "," };

    while (!reader.EndOfData)
    {
        string[] parts = reader.ReadFields();

        if (parts.Length != 5)
        {
            // problem.
            continue;
        }
        string customerCode = parts[0];
        string customerName = parts[1];
        string contact = parts[2];
        string phone = parts[3];
        string information = parts[4];

        var customer = customers.FirstOrDefault(c => c.CustomerCode == customerCode);
        if (customer == null)
        {
            // new
            customer = new Customer()
            {
                CustomerCode = customerCode,
            };
            Entities.Customers.AddObject(customer);
        }

        customer.CustomerName = customerName;
        customer.CustomerPhone = phone;
        customer.CustomerContact = contact;
        customer.Information = information;
    }
    Entities.SaveChanges();
}