Tuesday 29 January 2008

Binding It Twice

Last week I, like a lot of other Asp.Net newbies, got a weird validation error on my page saying, "Invalid postback or callback argument". The funny thing is, I've got another error two days earlier, even more weird, saying,

Item has already been added. Key in dictionary: 'ID' Key being added: 'ID'

The weird thing about this error is that I was adding an item to a GridView via a regular mechanism, using the prescribed DetailsView and ObjectDataSource, no funky MVP stuff. The "ID" in this error refers to the key column.

What caused the error in both cases was calling the DataBind method on postback, which you shouldn't usually do if you have ViewState enabled. In the second case, I blindly called DataBind on the page itself.

So, the rule for other newbie who happen to read this:
Whenever you init your page, call DataBind for the GridView only if you haven't set the DataSourceID, and always wrap it with "If Not IsPostBack".
If you add/change your record via a DetailsView that is bound to the same DataSource, you don't have to call DataBind on postback, since the DataSource will notify the GridView of the change.
If you add/change your record via some other means, call DataBind after it has been changed.

Here's my stack trace for Google:
[ArgumentException: Item has already been added. Key in dictionary: 'ID'  Key being added: 'ID']
System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) +2904181
System.Collections.Hashtable.Add(Object key, Object value) +11
System.Collections.Specialized.OrderedDictionary.Add(Object key, Object value) +49
System.Web.UI.WebControls.DataKey.LoadViewState(Object state) +88
System.Web.UI.WebControls.DataKey.System.Web.UI.IStateManager.LoadViewState(Object state) +4
System.Web.UI.WebControls.GridView.LoadDataKeysState(Object state) +310
System.Web.UI.WebControls.GridView.LoadControlState(Object savedState) +450
System.Web.UI.Control.LoadControlStateInternal(Object savedStateObj) +118
System.Web.UI.Page.LoadAllState() +212
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +109

Saturday 26 January 2008

WAMPed up!

You know, I'm not a big fan of LAMP. Let's say, I frown when I hear something good about it, and I hide my smile when I hear something bad. OK, at least I don't write that Windows Must Die in any other forum.

Apparently the Gods decided that it's time that I become more tolerant. First I had to make an Asp.Net site with mySql. It was sort of OK until a nasty bug deleted almost all our forums, and then they began to reappear out of some sort of mySql's recycle bin. But that's a different story.

Now I have to maintain a PHP project, and I decided that, hey, let's be tolerant! PHP is a cool object oriented language now, let's give it a try!

Installing Apache was no problem at all. I ran the default page, and it said in big bold letters, "It works!". That was cool.

Installing PHP was pretty straightforward too. But it was too late, so I went to sleep.

OK, now I'm going to check if my PHP works. Starting Apache and... there are lots of message boxes saying that httpd "This application has failed to start because php_mbstring.dll was not found" and then lots of others with different dll names. The poor beast tried again and again, so the messages won't stop until I killed it mercifully.

I decided to repair the installation and switched off all the extensions. It started nice, no message boxes. But the test PHP page I tried to view still showed nothing. In fact it showed the PHP source.

Later I discovered one possible cause. As somebody suggested, my PHP page contained something like <? phpinfo() ?>. This simplified syntax, it turned out, is switched off by default, you should write <?php phpinfo() ?> instead. Correct me if I'm wrong. But now that everything works, I can't verify this.

So, what did I do? I installed one of the available WAMP packages: EasyPHP. Works like a charm.. almost. A truly Windows-oriented application, it had minimum installation options. It even didn't ask me which of the three products (Apache, PHP, mySql) I want to install! And I was quite thankful, believe me. At least I didn't have to deal with environment paths, console commands, and huge ini files.

Saturday 12 January 2008

Readonly properties and testability

Sometimes an execution step requires that a property value is calculated. This value is used then in the subsequent step. When you want to test the second step in isolation, you usually mock the property. If you use "traditional" mock frameworks, or manually constructed stubs, the property should be virtual, and your object should be injected into the process somehow. This makes your design extendable, since whenever somebody wants a different behaviour, she can override the implementation of this property, and inject the custom object into the process.

Recently I discovered that the most flexible solution would be to create a special object that just transfers the data between the steps. You don't have to inject anything, if you want some custom data for the second step, you just create your data object manually. Without the dependency injection and inheritance requirements, the steps become completely isolated. You need some sort of controller to drive the flow, and your users can easily implement another controller with any of the steps replaced with different objects and different behaviour.

However, sometimes you, like me, are attached to the old fashioned idea of keeping behaviour with data. You need your class to expose the property and calculate its value:

Public Overridable ReadOnly Property SomeProperty() As SomeClass

Get

Dim result As New SomeClass

'Set some properties

Return result

End Get

End Property


The obvious solution is to calculate the property if you haven't set it yet, but still make it settable:

Private _someProperty As SomeClass

Public Property SomeProperty() As SomeClass

Get

If _someProperty Is Nothing Then _someProperty = CalculateValue()

Return _someProperty

End Get

Set(ByVal value As SomeClass)

_someProperty = value

End Set

End Property


Protected Overridable Function CalculateValue() As SomeClass

Dim result As New SomeClass

' Set some properties

Return result

End Function


A similar pattern can be applied to value objects. The trick is to make the associated private variable nullable:

Private _someProperty As Nullable(Of Boolean)

Public Overridable Property SomeProperty() As Boolean

Get

If Not _someProperty.HasValue Then _someProperty = CalculateValue()

Return _someProperty

End Get

Set(ByVal value As Boolean)

_someProperty = value

End Set

End Property


Protected Overridable Function CalculateValue() As Boolean

'Do stuff

End Function


Finally, let's try a similar thing with enumerables. Typically, we expose an enumerable as a readonly property, and fill the values at the initialization stage. Here we do a similar trick, but on a certain condition. We add a boolean property AutoCalculateValues and calculate them only if it is set to true, which is the default behaviour:

Private _autoCalculateValues As Boolean = True

Public Property AutoCalculateValues() As Boolean

Get

Return _autoCalculateValues

End Get

Set(ByVal value As Boolean)

_autoCalculateValues = value

End Set

End Property

Private _someProperty As IList(Of SomeClass)

Public Overridable ReadOnly Property SomeProperty() As IEnumerable(Of SomeClass)

Get

If _someProperty Is Nothing Then

_someProperty = New List(Of SomeClass)

End If

Return _someProperty

End Get

End Property

Protected Overridable Sub CalculateValues(ByVal list As IList(Of SomeClass))

list.Add(New SomeClass)

'...

End Sub


This time, the property is not settable, but the idea that it is initialized by default but the initialization can be switched off.

So, what are the advantages? In terms of testability, we can set the desired values directly, instead of using mocks, which simplifies our life a bit, and our tests become even more clear. But the main point is about reuse. Testability with mocks implies that one can modify the behaviour of our class by inheritance. A user of our framework can create a descendant of our class, overriding the property, and inject it into the workflow, just as we do in our tests. Making the property settable, on the other hand, makes it possible to extend the behaviour by composition: a user can, for example, add another class that sets the property in response to some external conditions. Another, more important, possibility, is to set the property manually. For example, on a Web page, the block elements are usually go under each other, but sometimes you want to position your div manually. You don't override the div class, you just tell it, "don't calculate your position, I'll do it myself". Note that the old inheritance possibility is still there -- you can override the CalculateValue() method if you want.

So, the system definitely becomes more open. This will definitely make the users of your component happier, but also opens more possibilities for misuse. For example, I could set the value that would put the system into an invalid state. However, the same result can be "achieved" by inheritance, only with more efforts. So, apart from validation, the best solution is writing docs properly. And remember that what seems like misusing to you might be a perfect way of using for others.

I'm using these ideas for some time with Inka, and I discovered that my design has improved. Each time I'm tempted to mock a parameterless method, I'm trying to convert it into a property, and then make it settable. I discovered that I have less dependencies and better decoupling, less pulls and more pushes, less methods that cover all possible situations, more small classes specifically designed for each situation.

Inka Layout, take three

The next release if Inka is being delayed, again and again. I decided to rewrite the layout engine. I already wrote about that ( see here, for example).

In short, first I tried Need Driven development: implement it step by step, mocking out the next step before implementing the current. This became a disaster: too much upfront design for me. Without seeing the whole picture, I was never sure that it works, much less that it works the way it should.

My second attempt was much better. At least it began better. I implemented a quick and dirty solution first, then added a few more integration tests and made my solution a bit more complex, then began to refactor. The refactoring stage took forever though. I still haven't finished it in the last release, so I was too shy to publish the code. With only integration tests in place, I wasn't seeing the correct direction of refactoring, so I was doing it blindly, and it took ages to figure out why this particular test broke when I did this simple refactoring.

Now it's the third attempt, and I've already abandoned version 3a. But I don't want to talk about it yet, until I see the results. I should say that the trunk version is the same as used in the last release, since I decided to put the new experimental version in a separate branch.