Skip to main content

If it can go wrong, it will!

If you've been reading my blog, you know that I am developing an application using Visual Studio 2005 and Castle's ActiveRecord framework. Recently, I bought Visual Studio 2005 Pro so I could use Crystal Reports. Believe it or not, The user was able to install my application without a working version of .Net 2.0 around. As far as I can tell, they did a Windows Update, and somehow it corrupted their video drivers. Then they did a system restore back to before .Net 2.0 was installed. After this, they installed my application, and it crashed (of course!). They tried to repair .Net 2.o Framework, but that didn't quite do it. So now I will probably be connecting via Remote Desktop to survey the damages.

I learned something from this, and I will probably have two tiny executables in my bin folder ... a "hello world" application that maybe says "If you can see this, then .Net 2.0 is installed." and maybe does some sniffing around to see if an important font (Bar Code 3 of 9) is installed. When someone calls you up and says that an application crashed before it has shown the login form, that is never good. My first guess was that there was a security problem, so I asked him to go to Administrative Tools, but he could not find the .Net 2 Configuration Wizard. Aha! I guess I need to include the re-distributable components on CD, just for these cases. And I need to add a logging library, so I have some idea what the user is doing if and when the program crashes.

Why am I having so many problems? Can you say 3 week delivery time for a 5 month project? So I gave them "what I have", even though it is not fully tested. I hate doing this, but I did not have a choice. So I will be updating the application every few weeks as I enable menu items and enable or add forms. The good side of this is that the users will be testing my code, and because there is a real need, I was forced to focus my efforts on the core feature set. Since the most critical feature is creating an XML report, I started with that, hard-coding as I went. Then I dropped each value into the schema where it made the most sense, until I had no more hard-coded values. Then I refactored the code two or three times, separating user interface, business logic and entity / data management. I probably have more work to do here, but I have NUnit tests for some of the application, and since the UI (user interface) is in an EXE assembly, and the business logic and data classes are in a DLL assembly, all I have to do is write tests, and anything that I can't do from the NUnit tests needs refactoring.

Since I have about 100 things to do before I finish the application, I have resorted to two types of comment tags, the standard "//TODO: Implement method bar.MarkWidgetAsAssembled" and "//FUTURE: Do this later, since it is not a critical deliverable element". After my first official deliverable, I will do a file find and replace of //FUTURE: with //TODO: and finish up.

Another defensive tactic that I commonly use is try / catch blocks in my event methods. For example if I have a "Create ..." button, I will wrap my processing in a try / catch block, and throw a Debug.WriteLine(ex.ToString()) or log the exception then inform the user that the request failed. The business code that I called will be self-cleaning, so I do not need a finally block. The business code will have a try / finally if there is a file that needs to be closed, etc. .Net 2.0 has a BackgroundWorker that allows me to send progress percent and messages, without any System.Windows.Forms (SWF) references. This is important, since one of my rules is that business / data library code should NEVER refer to UI components aka SWF. This allows me to test my library code more easily, and who knows, some parts of the application might be used in a batch environment. Perhaps my file import code could be hooked to a directory watcher that imports new files and then moves them to /loaded or /error subdirectories. If my import code popped up a MessageBox under any circumstance, I could never use it in an automated fashion. Here is some pseudo-code:

// Button Event
private void BtnCreate_Click(sender, event)
{
try
{
DateTime selectedMonth = GetDateFromUI();
CreateWidgetSet(selectedMonth);
}
catch (Exception ex)
{
LogError(ex);
MessageBox("An error occurred while trying to create a widget set.\n\n" + ex.Message,
Title, MessageBoxButtons.OK);
}
}

// business action method
private void CreateWidgetSet(DateTime selectedMonth)
{
// actual business method
Widget.CreateASet(selectedMonth);
}

Comments

Popular posts from this blog

Updated ActiveRecord Code Generator

Today, I updated the ActiveRecord Code Generator a bit. I checked in changes to use primary and foreign key details from INFORMATION_SCHEMA. The original code used naming conventions to decide what various fields were used for - ID = Primary Key, Field_ID = Foreign Key to table Fields. If you want to use naming conventions, let me know and I can add a setting in App.Config to allow this (along with any "real" key constraints).

How does Rails scaffolding select HTML input tags?

Recently, a reader saw my fix for SQL Server booleans, and asked me a followup question: why does Rails display a yes/no selection instead of a checkbox? The short answer is look in {RUBY_HOME} /lib/ruby/gems/1.8 /gems/actionpack-1.10.2 /lib/action_view/helpers, but your path may vary depending on whether you are using gem, "edge rails", etc. Anyway, look in the file "active_record_helper.rb" for a method called "all_input_tags", and notice that it calls "default_input_block" if you don't supply an input_block. Now notice that "default_input_block" creates a label and calls "input(record, column.name)" which in turn calls "InstanceTag#to_tag" which finally looks at the datatype and maps boolean to a select tag. Perhaps a wiser Rails explorer can provide us with the rationale for this, but I guess we could add a MixIn for InstanceTag that redefines the to_tag() method, or just do a dirty and unmaintainable hack l...

Features of the Code Generator

I just updated my code generator to optionally generate validation attributes. This simple change includes App.config file entries for all check boxes, and a new checkbox for "Validation" - aka validation attibute generation. While I was making this change, I realized that I really need to pass a CodeGenerationContext object to the DbTable, DbField and ModelGenerator classes. The requester can populate the context, and pass it to the code generator. Anyway, enough about the code, let's talk about the templates. I made a simple template this weekend to generate a DataGridView column array, suitable for databinding. I'm sure my new template will need some tweaks to handle Foreign Keys better (it currently just displays them as TextBox). Let's look at a template. ##FILENAME:PR_${table.GetClassName()}_Insert.sql ## ## Generate a SQL stored procedure to insert a record into the ## specified table and return the newly created primary key ## ## FUTURE: The generat...