More .NET Compact Framework Woes

by Geoff Lane on February 4th, 2007

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 for XML serialization, it plain just doesn’t work.

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 will work. Unfortunately this does not work in the Compact Framework.

[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:

  1. Use Arrays
  2. 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.

From → .NET

5 Comments
  1. Grant permalink

    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.

  2. 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)

  3. 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.

  4. 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.

  5. Tim permalink

    Here’s an alternative work-around which works now, but is easy to remove if the CF ever serializes generic lists properly:

    
            /// 
            /// This property is just a work-around for a bug in CF serialization. Never use it from 
            /// your own code.
            /// 
            public string[] SerializeResults
            {
                get{return _results.ToArray();}
                set{_results = new List(value);}
            }
    
            [XmlIgnore]
            public List Results
            {
                get{return _results;}
                set { _results = value; }
            }
    

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS