Unable to generate code for a value of type... Can you tell what's wrong?
I recently got this error when trying to run a WebControl that had a property that is a collection of objects that have a property that uses a type converter to convert its string persistence format to its real type (still following me?). Here's what the persisted format looks like:
<sample:CallbackProxy runat=server ID="AddProxy" EventTriggerID="AddButton" ClientEventName="onclick"><Parameters>
<sample:CallbackParameter ControlID="Number1" ClientType="integer" />
</Parameters>
</sample:CallbackProxy>
Here's what the ClientType property declaration looks like:
[TypeConverter(typeof(ClientTypeConverter))]public IClientType ClientType {
This error is very puzzling because if you debug into the type converter, everything seems to be working fine: the string in the persisted format gets correctly converted into an IClientType. The error is difficult to track because it doesn't happen technically at run-time but just before that, at parse-time. To find out what's wrong, you've got to attach a debugger to the web server and break on exceptions. It's quite surprising to see that the problem is not with the type converter converting from string, but converting from IClientType to InstanceDescriptor.
Why is the parser trying to do that with our type?
The parser's job is to construct C# or VB source code for a procedural version of your declarative aspx file. Once it's done, the page is a program that constructs the control tree and sets control properties. To do that efficiently, type-converting from string is out of the question because that would have an unacceptable performance hit every time the page runs. What the parser does is get a much more efficient way to get the right instance from each property type. It does have an instance that it got by type-converting from string (which is okay: parsing happens only once), but it can get some factory code (the InstanceDescriptor). In essence, it's asking the type convertor "how do I get code that will recreate this instance at run-time". That's what the InstanceDescriptor does. You build that InstanceDescriptor usually from a ConstructorInfo, but it can also be some static member's MemberInfo (some kind static factory is typically what you're looking for here if a constructor is impractical).
In our case, the type convertor must be able to convert from and to string, but also to InstanceDescriptor. Here, we're building the InstanceConvertor from the parameterless constructor of the concrete type of the instance:
if (destinationType == typeof(InstanceDescriptor)) {return new InstanceDescriptor(valueType.GetConstructor(new Type[] { }), new object[] { });
}
Once it has this information, the parser is able to CodeGen something like this:
CallbackParameter1.ClientType = new IntegerClientType();
which is exactly what you would write if you were writing the page procedurally.