Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

April 2005 - Posts

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.

Fun with callbacks, Part 2: The RefreshPanel

ASP.NET 2.0 makes it easier to develop Web applications that do out-of band calls, as we saw in the previous post. But one thing I noticed is that most applications will really just want to refresh and update some part of the page without touching the rest of it. In this case, the second client-side script, the one that receives the results from the callback and updates the page is always the same, something like element.innerHTML = response;.

So I wrote this little control that inherits from Panel and just adds the necessary refresh features. What makes it different from the usual panel is that it of course implements ICallbackEventHandler so that it can handle callback events, but it also implements ICallbackContainer so that it is easy to get a client-side refresh event reference from it. ICallbackContainer only has one method, GetCallbackScript, which returns a callback script for a button control (this can be null) and a string event argument.

Here's a very simple example that uses the RefreshPanel to rebind a treeview without refreshing the rest of the page:

<script runat="server">
protected void OnTreeBind(object sender, TreeNodeEventArgs e) {
 
if (IsCallback) {
   
e.Node.Text += " Updated by callback";
 
}
}

public void ReBindTree(object sender, RefreshingEventArgs e) {
 
MyTreeView.DataBind();
}
</script>

<asp:SiteMapDataSource runat="server" ID="xml" />
<sample:RefreshPanel runat="server" ID="MyPanel" OnRefreshing="ReBindTree">
 
<asp:TreeView runat="server" ID="MyTreeView" DataSourceID="xml" OnTreeNodeDatabound=OnTreeBind>
   
<SelectedNodeStyle BackColor=Blue Font-Bold=true ForeColor=White />
 
</asp:TreeView>
</sample:RefreshPanel>
<sample:RefreshButton runat="server" Text="Refresh" id="RefreshButton" RefreshPanelID="MyPanel" />

Here, we only have an ordinary TreeView in a RefreshPanel. Just to show that something happened even if the underlying site map data has not changed, we handle TreeNodeDataBound on callbacks and add some text to each node.

The RefreshPanel has an OnRefreshing event that we handle to rebind the tree.

Finally, the RefreshButton is a control that goes hand in hand with the RefreshPanel. It derives from Button and has this additional RefreshPanelID property that enables you to point to the RefreshPanel that it's going to refresh when clicked.

Notice how we didn't write a single line of client-side code in this case because both RefreshPanel and RefreshButton already know exactly what to do.

Actually, we've only just scratched the surface of what you can do with these controls. In future posts, I'll explain how to build a ComboBox, a Chat room and other fun stuff with them. Stay tuned!

Update: The source code for this is at http://www.gotdotnet.com/workspaces/workspace.aspx?id=cb2543cb-12ec-4ea1-883f-757ff2de19e8

Back to the first part:
http://weblogs.asp.net/bleroy/archive/2005/04/08/397761.aspx

Forward to the third part:
http://weblogs.asp.net/bleroy/archive/2005/05/19/407539.aspx

And the fourth:
http://weblogs.asp.net/bleroy/archive/2005/06/27/415936.aspx

Fun with callbacks Part 1: What's in the ASP.NET box?

There's a lot of buzz currently around Web out-of-band calls, aka XmlHttp, aka AJAX (the guy who coined this term must be some kind of marketing genius for imposing a new acronym for a technique that's been used for many years). It seems like the world is suddenly discovering that it is possible to get an update to a Web page from the server without posting back. Many techniques have been used for that purpose: Java applets (ASP classic was using this technique in Remote Scripting more than eight years ago), hidden frames and iframes, dynamically reloaded <script> elements and even reloading transparent images using cookies as the information transportation vessel. But the technique really became more than just a clever hack when Microsoft introduced the XmlHttpRequest object in Internet Explorer 5. The goal was to populate Xml data islands without sending back the whole page, hence the Xml prefix, but today it doesn't have much to do with Xml any more as the data that's transferred is most of the time not Xml. Client-side Xml never really happened.

Of course, the main reason why all this buzz is happening now is that Google is using the technique extensively in almost all of its recent betas (Google Maps, Google Suggest, GMail, etc.). If your e-mail is using an Exchange server, you probably also know Outlook Web Access, which is the most amazing Web application I've ever seen. It feels almost like the desktop version, it has context menus, drag & drop, multiple selection using CTRL+Click, rich text editing, etc. And of course, the MSDN library TreeView has been expanding nodes out-of-band for as far as I can remember.

So, what do we do in ASP.NET 2.0 to make it as easy as possible to develop Web applications that take advantage of this technique? A lot, actually. First, some of the new controls just use the technique out of the box. TreeView populates nodes on-demand without posting back, GridView paginates without posting back, etc. Second, there is an API for control developers that makes it very simple to add the feature to your controls without having to worry about most of the plumbing. You end-up basically handling control events as usual.

You just need to write the client-side script that will prepare the event arguments, the server-side event handler that will handle these arguments and return a response, and the client-side script that will take the response and reinject it into the page.

Here's a very simple and silly example: 

<%@ Page Language="C#" Title="Fun with callbacks: a simple one" %>
<%
@ Implements Interface=System.Web.UI.ICallbackEventHandler %>
<script runat=server>
string ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
  return "Hello, " +
    eventArgument +
   
". Your name is " +
   
eventArgument.Length.ToString() +
   
" characters long.";
}
</script>
<
html>
 
<head runat=server/>
  <script>
  
function OnCallback(result, context) {
   
alert(result);
 
}
 
</script>
 
<body>
   
<form runat=server>
      Enter your name here:
     
<input name="name" />
     
<input type=button ID=CallbackBtn value="Send"
       
onclick="<%= ClientScript.GetCallbackEventReference(
         
this,
         
"document.forms[0].name.value", 
         
"OnCallback", 
         
"this", 
         
"OnCallback", 
         
true) %>"
/>
   
</form>
 
</body>
</
html>

Here, the client-side script is very simple. We get the callback script we need by calling the ClientScript API, using the page (this) as the event target, the contents of the input box as the event argument, and OnCallback as the client-side function to call with the results.

The server-side script is just an event handler for the callback event where we take the argument string and do some calculations on it before we return the result string.

And that's all, we don't need to write any more code. But we can do even better. I've noticed that many callback scenarios are just refreshing some part of the page. So to avoid having to write the client-side script that takes the results from the server and reinjects them into the DOM, I've written a small WebControl that handles that for you. It's called RefreshPanel and I'll show how to use it in the next post in this series.

In the meantime, you can find its source code as well as a few sample user controls that use it at this address:
http://www.gotdotnet.com/workspaces/workspace.aspx?id=cb2543cb-12ec-4ea1-883f-757ff2de19e8

Read on to the second part:
http://weblogs.asp.net/bleroy/archive/2005/04/15/400792.aspx

The third part:
http://weblogs.asp.net/bleroy/archive/2005/05/19/407539.aspx

And the fourth:
http://weblogs.asp.net/bleroy/archive/2005/06/27/415936.aspx

Open() late, Close() early

One of the most common mistakes Web developers do is try to be smart about database connection management. Connections are expensive resources, and it seems like it would make a lot of sense to store an open connection, say, in a Session or Application variable, for later re-use, so that next time you need it, it's already there and ready to use.

Well, don't.

In ADO.NET, there's something called connection pooling that you get for free when you use it. Basically, some very smart and highly specialized people have already figured out very a optimized implementation of connection management. In a nutshell, there is a small set of connections that ADO.NET will keep track of so that reopening a connection with the same connection string is virtually free, which is exactly what developers who don't know about it try to achieve by more simplistic and much less efficient ways. It's ironic that they actually make things worse by trying to improve what's already been optimized. It's a typical example of useless early design for performance optimization. As the old saying teaches us, if it ain't broken, don't fix it.

If you try to store connections, two things will happen.

First, you're clogging the connection pool for other applications by keeping for yourself one or more connections, doing nothing with it most of the time.

Second, you're creating a very small bottleneck for your own application: a single connection can't be used by two threads at the same time, so you're forcing all your threads (which you do have in an ASP.NET application even though it's almost invisible to the casual prograsmmer) to fight for the connection.

There is one simple guideline when dealing with database connections, and it's the title of this post: Open() late, Close() early.

It means that you should open connections as late as possible (just before querying the database is ideal), and close them as soon as you're done with them (ideally just after querying): Open() and Close() are the actions that take away or give back connections from or to the pool. This way, you're taking the connection away from the pool the minimum amount of time, so that it can be reused by other threads and applications as soon as possible.

It also means that your connection strings should be as few as possible, because the pool relies on the connection string to reuse an existing connection or open a new one.

One final advice about DataReaders and DataSets. While DataSets can be a little heavy for some tasks, they have the great advantage of being disconnected objects, which means that they are good citizens with the connection pool and that filling a dataset is something that keeps connections out of the pool for a minimum amount of time. DataReaders, on the other hand, need an open connection the whole time they're open. That's usually fine because they are very fast. But if you misuse them, they can be counter-productive for the reasons I've mentioned above. For example, if you do a whole lot of things not related to data-reading while looping over the DataReader, you're keeping a connection away from the pool. So your thread may be fast, but others and other applications can be dramatically slowed down. So here, you should treat the DataReader as a connection, and Open() late and Close() early. In other words, read the data as fast as possible and close the reader. In a Web application, for example, you can open the reader, DataBind() immediately, and close the reader. Don't open the reader in some early event, and keep it around for the rest of the page lifecycle.

And of course, the worse you could do is to forget to Close() altogether. So your best friend in this matter is the using() {} statement (which is now also available in VB). It will make sure your precious connections are closed in all cases, even if something goes terribly wrong and an exception is thrown while the connection is open (it's equivalent to a try/finally). And it will force you to keep the Open() and Close() in the same scope.

No April Fool's jokes this year :(
Yesterday, all bloggers here at Microsoft received a memo from Steve Ballmer asking us not to do any April Fool's jokes on our blogs. Here's a citation from this memo:
"This kind of joke can affect the image of the company negatively and spread false information. We'll have to be very strict on enforcing this policy and any employee who chooses not to obey this simple rule will have to face sanctions up to and including termination of their employment."
That's really sad, I had a few good ones that I had been preparing. Oh well.
 
Update: of course, this was written on April 1st, so none of this is true. SteveB never wrote such a memo.
More Posts