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

Castle ActiveRecord with DetachedCriteria

My current development environment is Visual Studio Express C# Edition (read that as free ), Castle ActiveRecord's latest svn trunk(usually within a few days), and NHibernate svn trunk. As of NHibernate version 1.2.0, there is a very cool new class out there ... DetachedCriteria. This class lets you set all of your Castle relational attributes like BelongsTo, HasMany, etc. as lazy fetch, and over-ride this for searches, reports, or anytime you know ahead of time that you will be touching the related classes by calling detachedCriteria.SetFetchMode(..., FetchEnum.Eager). As a good netizen, I have tried to contribute to NHibernate and Castle ActiveRecord even if only in the smallest of ways . Oh yeah, I tried mapping to a SQL VIEW, and it worked GREAT! I received a comment after my last post, indicating that there is a better way, and I am sure of it, but the view guaranteed that I only have one database request for my dataset. NHibernate was wanting to re-fetch my missing as

Castle ActiveRecord with Criteria and Alias

Update May 25, 2007: ActiveRecord now supports DetachedCriteria, which eliminates the need for the SlicedFindAll that I wrote below. It is nice when a library moves to add support for such commonly needed functions. So in summary, use Detached criteria instead of the code below. It is still a nice example of using NHibernate sessions. I have a history log, where each history record "belongs to" a service record. I have to treat this as a child-to-parent join, since some children are orphans. I wanted to use the FindAll(Criteria), but I wanted the option to have optional criteria, orders and aliases. My solution was to create an ARAlias class to represent an Associated Entity and an alias, and then build an ARBusinessBase class with the following method: public static T[] SlicedFindAll(int firstResult, int maxResults, Order[] orders, ARAlias[] aliases, params ICriterion[] criteria) { IList list = null; ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionF

Castle ActiveRecord calling a Stored Procedure

Update: I have contributed patch AR-156 that allows full integration of Insert, Update and Delete to ActiveRecord models . If you've been reading my blog lately, you know that I have been seriously testing the Castle ActiveRecord framework out. I really love it, but I have an existing Microsoft SQL Server database with many stored procedures in it. I have tested the ActiveRecord model out, and I am sure that I will learn enough to be able to use it for standard CRUD (create, read, update, delete aka. insert, select, update, delete) functionality. BUT ... If I really want to integrate with my existing billing procedures, etc, I will have to be able to call stored procedures. I have taken two approaches ... write the ARHelper.ExecuteNonQuery(targetType, dmlString) method that gets a connection for the supplied type, executes dmlString, and closes it. write the ARHelper.RegisterCustomMapping(targetType, xmlString) method that allows me to add mappings that refer to my auto-gener