Friday, September 23, 2005

 

I recently discovered an issue with Windows Forms data binding when a form is bound against a smart data container like a DataTable with partial class code or a CSLA .NET –style object. The issue is pretty subtle, but nasty – and my current workaround doesn’t thrill me. So after reading this if you have a better work around I’m all ears – otherwise this could end up in the books I’m current writing, since both are impacted… :(

 

So here’s the problem:

 

Create a smart data source – defined as one that includes business logic such as validation and manipulation of data. In this case, it is the manipulation part that’s the issue. For instance, maybe you have a business rule that a given text field must always be upper case, and so you properly implement this behavior in your business object or partial class of your DataTable to keep that business logic out of the UI.

 

In a business object for instance, this code would be in the property set block – perhaps something like this in a CSLA .NET 2.0 style class:

 

  Public Property LastName() As String

    Get

      VerifyGetProperty("LastName")

      Return mLastName

    End Get

    Set(ByVal value As String)

      VerifySetProperty("LastName")

      If mLastName <> value Then

        mLastName = value.ToUpper

        PropertyHasChanged("LastName")

      End If

    End Set

  End Property

 

(in CSLA .NET 2.0 the PropertyHasChanged method raises the appropriate PropertyChanged event via the INotifyPropertyChanged interface and also calls MarkDirty)

 

Or a simpler class that implements INotifyPropertyChanged:

 

  Public Property LastName() As String

    Get

      Return mLastName

    End Get

    Set(ByVal value As String)

      mLastName = value.ToUpper

      RaiseEvent PropertyChanged(Me, _

        New PropertyChangedEventArgs("LastName"))

    End Set

  End Property

 

Specifically notice that the inbound value is changed to upper case with a ToUpper call in the set block.

 

Now add that data container to the Data Sources window and drag it onto your form in Details mode. Visual Studio nicely creates a set of controls reflecting your properties/columns (damn I love this feature!!) and sets up all the data binding for you. So far so good.

 

Now run the application and enter a lower case value into the Last Name text box and tab off that control. Data binding automatically puts the new value into the object and runs the code in the set block. This means the object now contains an upper case version of the value you entered.

 

Notice that the TextBox control does not reflect the upper case value. The value from the object was never refreshed in the control.

 

Now change another value in a different control and tab out. Notice that the Last Name TextBox control is now updated with the upper case value.

 

So that’s the problem. Data binding updates all controls on a form when a PropertyChanged event is raised, except the current control. You can put a breakpoint in the property get block and you’ll see that the value isn’t retrieved until some other control triggers the data binding refresh.

 

I did report this bug to Microsoft, but it has been marked as postponed - meaning that it won't get fixed for release. An unfortunate side-issue is that this issue makes data binding in 2.0 work differently than in .NET 1.x, so any .NET 1.x code that binds against a smart data container like a business object will likely quit working right under 2.0.

 

Now to my workaround (of which I’m not overly proud, but which does work). My friend Ed Ferron should get a serious kick out of this – feel free to laugh all you’d like Ed!

 

The problem is that data binding isn’t refreshing whatever control is currently being edited when the PropertyChanged event is raised. The obvious question then is how to get a PropertyChanged event raised after the property set block has completed. Because at that point data binding will actually refresh the control.

 

The easiest way to get some action to occur “asynchronously” without actually using multi-threading (which would be serious overkill) is to use the System.Windows.Forms.Timer control. That control runs on your current thread, but provides a simulation of asynchronous behavior. (On the VAX this was called an asynchronous trap, so the concept has been around for a while!)

 

Putting the Timer control into your data object is problematic. The Timer control implements IDisposable, so your object would also need to implement IDisposable – and then any objects using your object would need to implement IDisposable. It gets seriously messy.

 

Additionally, a real application may have dozens or hundreds of objects. They can’t each have a timer – that’d be nuts! And the OS would run out of resources of course.

 

But there’s a solution. Use a shared Timer control in a central location. Like this one:

 

Public Class Notifier

 

#Region " Request class "

 

  Private Class Request

 

    Public Method As Notify

    Public PropertyName As String

 

    Public Sub New(ByVal method As Notify, _

      ByVal propertyName As String)

 

      Me.Method = method

      Me.PropertyName = propertyName

 

    End Sub

 

  End Class

 

#End Region

 

  Public Delegate Sub Notify(ByVal state As String)

 

  Private Shared mObjects As New Queue(Of Request)

  Private Shared WithEvents mTimer As New System.Windows.Forms.Timer

 

  Public Shared Sub RequestNotification(ByVal method As Notify, _

    ByVal propertyName As String)

 

    mObjects.Enqueue(New Request(method, propertyName))

    mTimer.Enabled = True

 

  End Sub

 

  Shared Sub New()

 

    mTimer.Enabled = False

    mTimer.Interval = 1

 

  End Sub

 

  Private Shared Sub mTimer_Tick(ByVal sender As Object, _

    ByVal e As System.EventArgs) Handles mTimer.Tick

 

    mTimer.Enabled = False

    While mObjects.Count > 0

      Dim request As Request = mObjects.Dequeue

      request.Method.Invoke(request.PropertyName)

    End While

 

  End Sub

 

End Class

 

This could alternately be implemented as a singleton object, but the usage syntax for this is quite nice.

 

This Notifier class really implements the RequestNotification method, allowing an object to request that the Notifier call the object back in 1 tick of the clock (about 100 nanoseconds) on a specific method.

 

The reason the class maintains a list of objects to notify is because even in a single threaded application you can easily envision a scenario where multiple notification requests are registered within 100 nanoseconds – all you need is programmatic loading of data into an object.

 

Now your data object must implement a method matching the Notify delegate and register for the callback. For instance, here’s the updated code from above:

 

  Public Property LastName() As String

    Get

      Return mLastName

    End Get

    Set(ByVal value As String)

      mLastName = value.ToUpper

      Notifier.RequestNotification(AddressOf Notify, "LastName")

    End Set

  End Property

 

  Public Sub Notify(ByVal propertyName As String)

    RaiseEvent PropertyChanged(Me, _

     New PropertyChangedEventArgs(propertyName))

  End Sub

 

Rather than raising the event directly, the property set block now asks the Notifier to call the Notify method in 1 tick. So 1 tick later – after the property set block is complete and data binding has moved on – the Notify method is called and the appropriate PropertyChanged event is raised.

 

With this change (hack) data binding will now refresh the Last Name TextBox control as you tab out of it. Though there’s a 100 nanosecond delay in there, it isn’t something the user can actually see and so you can argue it doesn’t matter.

 

I can hear Ed laughing already…

Friday, September 23, 2005 5:46:01 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

You know how Microsoft collects all that information every time an application or the OS crashes? It is a cool idea and very valuable. You may have noticed that video drivers don't crash systems nearly as much as they did just a couple years ago. Wonder why? Because all that feedback has allowed the card vendors to make higher quality drivers.

You may have wished to implement something like this in your applications too. It may be possible to do it through Microsoft's mechanism, I honestly don't know - but I expect it wouldn't be easy or free. http://www.exceptioncollection.com appears to be one attempt to provide such a service on the web for free. I haven't used it, so I can't speak for how well it works, but I gotta say that the concept sounds quite attractive for some types of application.

Friday, September 23, 2005 4:02:22 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, September 19, 2005

Upon returning from the Microsoft PDC conference I found that my laptop could no longer access the nodes on my LAN. I could ping machines by IP address, but local name resolution was not working. After much discussion and running many network commands as suggested by various colleagues, my co-workers at Magenic hit upon the answer.

Apparently it is possible for your machine's network node type to be switched by any DHCP server. My DHCP server doesn't set the node type at all, but the one at PDC apparently set it to Peer-to-Peer, which I'm told forces your machine to only use a WINS server for name resolution on a LAN. I don't have a WINS server, so all name resolution was shot to hell when I got back from PDC.

The solution was to change my node type from Peer-to-Peer to almost any other value: b-node, Hybrid or Unknown are all good. Since there's no end user tool to do this, regedit is your friend as noted in this blog entry.

Situations like these remind me why I'm a software and not an infrastructure person. I find troubleshooting and solving this stuff to be frustrating and not at all fun... It is probably an aftertaste from my brief stint (18 months or so) of managing a corporate help desk many years ago - because that wasn't at all fun either.

Monday, September 19, 2005 2:01:36 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, September 07, 2005
I have started a document where I have and will post changes to CSLA .NET from version 1.5 to 2.0.
Wednesday, September 07, 2005 9:04:39 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, September 06, 2005

Geekdom comes in various forms - in my case both fandom and the computer world.

Over in fandom there's a targeted effort by fans to provide housing and assistance to the fans affected by Katrina, including having noted authors Mercedes Lackey and Larry Dixon offering up their home and time to host people and to coordinate others willing to host people or donate food, time or money. My wife's blog has more details and links - so if you are a fan here's a great way to help!

And in the computer world there's the Katrina Data Project, which I blogged about a couple days ago. Computer geeks of the world have similarly generously volunteered their time and resources to make this work as well. Here's part of an email from the project, along with contact info if you want to get involved as well:

Some quick data on what we've accomplish so far:

We're housing data on over 65,000 searching or safe individuals... the only larger datastore I am aware of is the ICRC Red Cross site.
We've received data that even the Red Cross hasn't aggregated yet... files put together by volunteers on the ground in shelters 
We're listed on the homepage of "Emergency web terminals" being deployed in the disaster area.
I just met today with the Salvation Army IT team... we will be bringing in over 50,000 more records from their data sources. We will be helping them capture additional data going forward... their system is currently transporting and storing data via email.
We are organizing a network of volunteers who will be traveling to their local shelters and help us receive data
We've got 500 volunteers in a callcenter waiting for access to our data so they can begin an outbound calling effort.
Much much more than I can type right now.
John Galloway
The Katrina Data Project
 
Public Search and Registry: http://www.searchkatrina.com 
Data Interchange Site: http://www.katrinadataproject.com

e: john@katrinadataproject.com
AIM: KDataProject

 

Tuesday, September 06, 2005 7:04:33 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, September 04, 2005
We're a few local (ATL) ASP.NET programmers who have created a central datastore, collection web interfaces, and processing system for missing/safe person data on people affected by hurricane katrina.
 
 
The idea caught on quick... we've only got 2 guys working on this full time and a third helping out when he can. Since going online we've ben contacted by LOTS of groups who want to work with us. We're overwhelmed and need help... we've got plans for a mobile device interface to our data, and are working with a callcenter fielding calls from disaster victims (the callcenter needs some special enhancements to the interface we can't get to fast enough.). We're being listed as the central survivors database on the emergency web terminals being deployed in disaster zones (http://www.publicwebstations.com/)
 
We've got people on the ground at shelters trying to gather lists and transmit them to our datastore electronically. We're also contacting almost 50 other websites which have lists or data collection systems... they are starting to use our MSP file spec and transmit data to us.
 
If you could get the word out that we are looking for volunteer programmers who can hit the ground running and take ownership of some of the special projects we have WE WOULD APPRECIATE IT.. but there are people out there looking for loved ones who would appreciate it even more.
 
Current experience we need:
  • .net mobile framework using our data classes to collect and process data
  • string data parsing experience... there's a lot of data in forum posts we can't use but could triple our data if we could parse into useful fields
  • Mass emailing system... as our system processes incoming bulk data, it makes matches between searchers and safe persons, flagging them as needed contact via email. We need a system to read from a database table containing these contact requests and send them out
  • any general ASP.NET (we're using VB) experience to help build special lookups and interfaces
I hate to make such a request on a holiday weekend, but we are swamped.
 
Please spread the word if you can.
 
Thanks!
 
-John
Developer, The Katrina Data Project
 
 
Sunday, September 04, 2005 2:48:46 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, August 29, 2005

I just got back from a week’s vacation/holiday in Great Britain and I feel very refreshed.

 

And that’s good, given that just before going to the UK I wrapped up the first draft of Chapter 1 in the new book Billy Hollis and I are writing. As you have probably gathered by now, this book uses DataSet objects rather than my preferred use of business objects.

 

I wanted to write a book using the DataSet because I put a lot of time and energy into lobbying Microsoft to make certain enhancements to the way the objects work and how Visual Studio works with them. Specifically I wanted a way to use DataSet objects as a business layer – both in 2-tier and n-tier scenarios.

 

Also, I wanted to write a book using Windows Forms rather than the web. This reflects my bias of course, but also reflects the reality that intelligent/smart client development is making a serious comeback as businesses realize that deployment is no longer the issue it was with COM and that development of a business application in Windows Forms is a lot less expensive than with the web.

 

The book is geared toward professional developers, so we assume the reader has a clue. The expectation is that if you are a professional business developer (a Mort) that uses VB6, Java, VB.NET, C# or whatever – that you’ll be able to jump in and be productive without us explaining the trivial stuff.

 

So Chapter 1 jumps in and creates the sample app to be used throughout the book. The chapter leverages all the features Microsoft has built into the new DataSet and its Windows Forms integration – thus showing the good, the bad and the ugly all at once.

 

Using partial classes you really can embed most of your validation and other logic into the DataTable objects. When data is changed at a column or row level you can act on that changed data. As you validate the data you can provide text indicating why a value is invalid.

 

The bad part at the moment is that there are bugs that prevent your error text from properly flowing back to the UI (through the ErrorProvider control or DataGridView) in all cases. In talking to the product team I believe that my issues with the ErrorProvider will be resolved, but that some of my DataGridView issues won’t be fixed (the problems may be a “feature” rather than a bug…). Fortunately I was able to figure out a (somewhat ugly) workaround to make the DataGridView actually work like it should.

 

The end result is that Chapter 1 shows how you can create a DataSet from a database, then write your business logic in each DataTable. Then you can create a basic Windows Forms UI with virtually no code. It is really impressive!!

 

But then there’s another issue. Each DataTable comes with a strongly-typed TableAdapter. The TableAdapter is a very nice object that handles all the I/O for the DataTable – understanding how to get the data, fill the DataTable and then update the DataTable into the database. Better still, it includes atomic methods to insert, update and delete rows of data directly – without the need for a DataTable at all. Very cool!

 

Unfortunately there are no hooks in the TableAdapter by which you can apply business logic when the Insert/Update/Delete methods are called. The end result is that any validation or other business logic is pushed into the UI. That’s terrible!! And yet that’s the way my Chapter 1 works at the moment…

 

This functionality obviously isn’t going to change in .NET or Visual Studio at this stage of the game, meaning that the TableAdapter is pretty useless as-is.

 

(to make it worse, the TableAdapter code is in the same physical file as the DataTable code, which makes n-tier implementations seriously hard)

 

Being a framework kind of guy, my answer to these issues is a framework. Basically, the DataTable is OK, but the TableAdapter needs to be hidden behind a more workable layer of code. What I’m working through at the moment is how much of that code is a framework and how much is created via code-generation (or by hand – ugh).

 

But what’s really frustrating is that Microsoft could have solved the entire issue by simply declaring and raising three events from their TableAdapter code so it was possible to apply business logic during the insert/update/delete operations… Grumble…

 

The major bright point out of all this is that I know business objects solve all these issues in a superior manner. Digging into the DataSet world merely broadens my understanding of how business objects make life better.

 

Though to be fair, the flip side is that creating simple forms to edit basic data in a grid is almost infinitely easier with a DataTable than with an object. Microsoft really nailed the trivial case with the new features - and that has its own value. While frustrating when trying to build interesting forms, the DataTable functionality does mean you can whip out the boring maintenance screens with just a few minutes work each.

 

Objects on the other hand, make it comparatively easy to build interesting forms, but require more work than I'd like for building the boring maintenance screens...

Monday, August 29, 2005 9:37:15 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, August 15, 2005

Listen to my latest interview on .NET Rocks! where I discuss CSLA .NET present and future.

Monday, August 15, 2005 1:38:43 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, August 11, 2005

Like any author who writes practical, pragmatic content I am constantly torn. Torn between showing how to write maintainable code vs fast code. Between distilling the essence of an idea vs showing a complete solution that might obscure that essence.

Just at the moment I'm building a demo application, including its database. Do I create the best database I can, hopefully showing good database design techniques and subsequently showing how to write an app against an "ideal" database? Or do I create the database to look more like the ones I see when I go to clients - so it will have good parts and some parts that are obviously ill-designed. This latter approach allows me to show how to write an app against what I believe to be a more realistic database.

I'm opting for the latter approach. Yet sitting here right now, I know that I'll get lots of emails (some angry) berating me for creating and/or using such a poor database in a demo. "Demos should show the right approach" and so forth. Of course if I were to use a more ideal database design I'd get comments at conferences (some angry) because my demo app "isn't realistic" and only works in "a perfect world".

See, authors can't win. All we can do is choose the sub-group from which we're going to get nasty emails...

But that's OK. The wide diversity of viewpoints in our industry is one of our collective strengths. Pragmatists vie against idealists, performance-hounds vie against those focused on maintainability. Somewhere in the middle is reality – the cold, hard reality that none of us have the time to write performant, maintainable code using perfect implementations of all best practices and known design patterns. Somewhere in the middle are those hard choices each of us makes to balance the cost of development vs the cost of licensing vs the cost of hardware vs the cost of maintenance vs the cost of performance.

I think ultimately that this is why computer books don’t sell in the numbers you’d expect. If there are 7+ million developers, why does a good selling computer book sell around 20,000 copies?

(The exceptions being end-user books and theory books. End-user books because end users just want the answers, and theory books because they often rise above the petty bickering of the real world. Every faction can interpret theory books to say what they like to hear, so everyone likes that kind of book. Theory books virtually always “reinforce” everyone’s different world views.)

I think the reason “good selling” books do so poorly though. Only a subset or faction within the computer industry will agree with any given book. That faction tends to buy the book. Other factions might be a few copies, but they get a bad taste in their mouths when reading it, so they don’t recommend or propagate the book. Instead they find other books that do agree with their world view.

Note that I’m not complaining. Not at all!

I’m merely observing that C# people won’t (as a general rule) buy a VB book, and Java people won’t buy a C# book. OO people won’t buy a book on DataSet usage, and people who love DataSets won’t waste money on an OO book. People who love the super-complex demos from Microsoft really hate books that use highly distilled examples, while people who just want the essence of a solution really hate highly complex examples (and thus the books that use them).

As I write the 2nd editions of my Business Objects books I’m simultaneously writing both VB and C# editions. Several people have asked whether it wouldn’t be better to interleave code samples, have both languages in the book or something so that I don’t produce two books. But publishers have tried this. And the reality is that C# people don’t like to buy books that contain any VB code (and visa versa). Mixed language books simply don’t sell as well as single-language books. And like it or not, the idea behind publishing books is to sell them – so we do what sells.

But that solution doesn’t work for realistic vs idealistic database designs – which is my current dilemma. You can’t really write a book twice – once with an ideal database and once with a more realistic one. Nor can you really double the size (and thus cost) of a book to fit both ideas into it at once.

So I’m settling for the only compromise I can find. Parts of my database are pretty well designed. Other tables are obviously very poor. Thus some of my forms are trivial to create because they come almost directly from the “ivory tower”, while other forms rely on more complex and less ideal techniques because they come from the ugly world of reality.

Maybe this way I can get nasty emails from every group :)

Thursday, August 11, 2005 1:54:09 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |