More .NET Compact Framework Woes
I posted previously on a Bug in the .NET Compact Framework with the XmlEnum Attribute with whitespace in the name. Well I’ve run into some other interesting “features”.
The first thing to realize is that the things that work on the Full framework don’t work on the Compact Framework.
What Works With Serialization
First thing is the good news: Arrays work everywhere. They work on the Compact Framework and the Full Framework. The downside of course is that Arrays are very inconvenient. You have to manually resize them yourself for example.
The second thing that works are regular ILists. When you map them a collection will be used. Of course one of the big improvements in .NET 2.0 was the introduction of Generics. Generics allow you to have strongly-typed collections without manually implementing them for each specific type.
More Generics Problems
Xml Serialization works if you use IList. But there are problems with Generics interfaces. I guess there are more than problems. Short story is that you can not use IList
Here’s where the difference between the Full Framework and the Compact Framework come into play. On the Full Framework, you can map to a concrete collection wether it’s generic or not. So List
[XmlElement(Name="foo")] public List<string> Foos { get { return this.foos } set { this.foos = value; } }
You end up with an exception:
Two mappings for string[]. Stack Trace: at System.Xml.Serialization.TypeContainer.AddType() at System.Xml.Serialization.TypeContainer.AddType() at System.Xml.Serialization.XmlSerializationReflector.AddIXmlSerializableType() at System.Xml.Serialization.XmlSerializationReflector.AddType() at System.Xml.Serialization.XmlSerializationReflector.FindType() at System.Xml.Serialization.XmlSerializationReflector.FindType() .....
So What Do You Do About It
You basically have 2 options:
- Use Arrays
- Create your own custom, strongly-typed collections classes
Arrays
The advantage is that this is very simple and requires no extra code. The downside as I said above is that you have to write code to manually do array resizing.
public T[] AppendItem<T>(T[] theArray, T newItem) { T[] newArray = new T[theArray.Length + 1]; Array.Copy(theArray, newArray, theArray.Length); newArray[newArray.Length - 1] = newItem; return newArray; }
Custom, Strongly-Typed Collections
If you don’t want to use Arrays and deal with manually resizing them, you can build your own Collections classes for each of your types.
Create your strongly-typed collection:
[Serializable] [EditorBrowsable(EditorBrowsableState.Advanced)] public class EmployeeCollection : ArrayList { public Employee Add(Employee obj) { base.Add(obj); return obj; } public Employee Add() { return Add(new Employee()); } public void Insert(int index, Employee obj) { base.Insert(index, obj); } public void Remove(Employee obj) { base.Remove(obj); } new public Employee this[int index] { get { return (Employee) base[index]; } set { base[index] = value; } } }
Then use that collection in your class to map:
[XmlRoot("company")] class Company { private EmployeeCollection employees; [XmlElement(Type=typeof(Employee),ElementName="employee",IsNullable=false)] [EditorBrowsable(EditorBrowsableState.Advanced)] public EmployeeCollection Employees { get { return this.employees; } set { this.employees = value; } } }
Pick Your Poison
The problems with Generics collections in the .NET Compact Framework seem like yet another bug. So, pick your poison and choose a workaround. Whichever seems simpler to you. Hope that helps someone out.
Geoff your post raises two points.
The first is a question. What was a fundamental design flaw in the C# language implementation that resulted in the manifestation of the error that occurred?
The second is a comment. What you’ve encountered, or more spefically how you had to deal with it, is a development taboo. I will talk to you about that part sometime at work.
The above solution solution does offer some workaround the problem, however
generally XMLSerializer is the least useful thing for the object exchange systems.
Have a look http://www.dotnetremoting.com there are some solutions (and some free)
Alex,
Calling Dotnetremoting platform neutral is a bit of a stretch. If you can run .NET or Mono then you can use .NET Remoting? Different OSes are not different platforms. When you’re trying to integrate a .NET CF handheld application with an existing Java service implemented as SOAP (XML), .NET Remoting is pretty useless. Integration is really only an interesting discussion when you don’t have control over both the client and the server. If you have control over both, it’s usually not a very difficult endeavor.
Geoff,
Running remoting on different platforms using standard MS Remoting is impossible for the simple reason: the serialization will fail. SOAP is so slow that sometimes it is unusable. Also all SOAP based
systems are unidirectional. Generally we are talking about very different systems.
You are focusing on the XML type systems that are platform neutral but they are useless when you need a performance and duplex.
Also everybody is missing the point that XML style communication is a result of inabilty of the software (and hardware) vendors to come to some reasonable binary standard. It is a miracle that they agreed on ASCII code and that enables to use XML based comms now.
Here’s an alternative work-around which works now, but is easy to remove if the CF ever serializes generic lists properly: