Tuesday, 22 June 2010

iPhone 0S4 update is taking too long to backup

So after downloading the iPhone OS 4, the backup begins – and well – is still around 5% after 3 1/2 hours.

I read a suggestion from the Apple Forums, to do the following:

  1. Cancel the backup
  2. Manually backup your iphone through itunes.
  3. Then restore iphone. An option will come up that says "restore and update".
  4. Iphone reboots after installation of os4. Then restore from backup in itunes

That process took about 30 minutes and I was done.

And I can verify that it does indeed!

Monday, 21 June 2010

Why and where is my XmlSerializer failing?

Overview

The XmlSerializer is an excellent utility to turn your classes into XML and vice versa very very easily. So long as you aren't using it on dynamically generated types, but are repeatedly on the same types, then the XML serializer is quite performant.

However, as great as the XmlSerializer is, I’ve found it a pain to debug. You have 2 choices:

  1. Look through the XML and find the invalid field
  2. Step through every property in the debugger for your class and find the property that is failing

Problem

When your XML document gets to the point of having hundreds of elements, this becomes a real problem. You’ll see output like this:

Writing to Application log: System.InvalidOperationException: There is an error in XML document (1, 10433). 
---> System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
at System.Xml.XmlConvert.ToDecimal(String s)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderCustomer.Read3_Customer(Boolean isNullable, Boolean checkType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderCustomer.Read4_Customer()
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
at BusinessObjects.SerializationUtils.Deserialize[T](T& typeOfObject, String xml)
at WebServices.Customer.ImportCustomer(XmlDocument xml) in C:\MyWebServices\CustomerTest\Customer.asmx.cs:line 43

Now you see the problem. The XML indicates (1, 10433) , which would indicate my XML has be reduced to 1 line, trimming the spaces. and then column 10433. It turns out this is incorrect as my XML at this location was this:

</ns0:CreditLimit><ns0:LastPaymentDate>

..And obviously this is wrong. Totally valid XML wise, its just something cannot be parsed to a Decimal.


Solution


Since XmlSerializer generates physical code, which is then compiled on-the-fly, I was looking for a way to gain access to this file. Upon finding the location, Visual Studio 2008 doesn't realise that the file links to the PDB generated by the XmlSerializer compilation proces.


After a little digging, I've found that Scott Hanselman blogged about debugging the XmlSerializer serialization process in .NET, which is exactly what I was looking to do.


He says to add some entries to the related configuration file like:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
...
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="1" />
</switches>
</system.diagnostics>
...
</configuration>


This then allows you to open the file from the temporary directory C:\Documents And Settings\[user]\Local Settings\Temp after the new XmlSerializer(Type t) is executed. When the .Deserialize() method is called, you will find you can Step Into (!!) the code generated. The best thing about this, is that since the PDB is now linked together with the code, your stack traces are much more helpful:

.... as before but ....
at MS.XML.GeneratedAssembly.XmlSerializationReaderCustomer.Read3_Customer(Boolean isNullable, Boolean checkType)      in c:\Documents and Settings\ASPNET\Local Settings\Temp\dcvrxygb.0.cs:line 985
at MS.XML.GeneratedAssembly.XmlSerializationReaderCustomer.Read4_Customer()      in c:\Documents and Settings\ASPNET\Local Settings\Temp\dcvrxygb.0.cs:line 163
.........

Woohoo!! So we now have line numbers, and if we open up the source code we see .....


else if (.....)
{
o.@TaxRate = System.Xml.XmlConvert.ToDecimal(Reader.ReadElementString());
}
...


And we look at my XML element TaxRate (as they are a direct mapping and we find ....


<ns0:TaxRate>??</ns0:TaxRate>


Spot the problem ;-)

How to find out if there are items for an EnumerableRowCollection

Unfortunately, this class doesn’t have a .Count() method, so you have to do a horrible hack to find out if there are items.

I suppose you could implement it as an Extension method, but as it is, this is what you need to do:

// Create table and populate data
DataTable dt = new DataTable();
dt.Columns.Add("Column1",typeof(string));
dt.Rows.Add( new object[] { "sample1" });
 
// Filter by a non-existent field, so now dataRows is now an 
// EnumerableRowCollection with no rows
var dataRows = from row in dt.AsEnumerable()
               where row.Field<string>("Column1") eq "FAKE DATA"
               select row;
 
// This next line throws an InvalidOperationException 
//// DataTable filteredResults = dataRows.CopyToDataTable();   
 
// Instead, create a new IEnumerator and iterate through it manually.
IEnumerator iEnum = dataRows.GetEnumerator();
 
if(!iEnum.MoveNext())
{     
     ShowMessage("No data available");}
} 
else
{
     DataTable filteredResults = dataRows.CopyToDataTable();   
 
     //   More DataTable processing here ....     
     MyGridView1.DataSource = filteredResults;     
     MyGridView1.DataBind();}
}


It is nasty, but it works :-)