2007-06-21

Quantum bugs

I've just been driven mad by a "Quantum bug". What is a quantum bug? Well, it's a bug that doesn't exist unless you look at it :-)

I kept getting a NullReferenceException in my code. It happened whenever I changed a property of a class. I assumed it was something in the framework, but it was in fact in a library of my own. When the property changed it would trigger an observer I had attached, the purpose of this observer is to check if the form that owns my component is the active form, if it is then it records the object that changed; the purpose is to know which objects were modified by which form.

Anyway, to check if the form is active I do this

Form activeForm = System.Windows.Forms.Form.ActiveForm;
Form activeMdiChildForm = activeForm.ActiveMdiChild;
if (activeForm == this.Form || activeMdiChildForm == this.Form)
.....

Now the quantum!

If you are debugging your app Form.ActiveForm will always return null! How useful is that eh? :-) So, when debugging my app I get a NullReferenceException, when I let it run through I don't.

There was me being a good boy, stepping through the code I had just added to check it does what it is supposed to, and Bill punishes me for it!

Damn :-)

2007-06-16

Cocoa, falling at the first hurdle?

I was REALLY looking forward to programming some apps on the Mac using Cocoa and objective-C.

I am reading "Cocoa programming for MAC OS X" by Aaron Hillegas. I had read as far as page 30 when I saw the following:

"Objective-C is a very simple language. It has no visibility specifiers: All methods are public; and all instance variables are protected. (Actually, there are instance specifiers for instance variables, but they are rarely used. The default is protected, and that works nicely.)"


WHAT?

Works nicely? I disagree! It is actually possible to make a class's method private by not including it in the interface declaration (myclass.h) and just adding the implementation file instead, but what about protected methods?

public
I expose as little as needed to ensure the class provides the service it was designed to. The signatures/names of members in my public area change as little as possible so as not to break other people's code. Public members are therefore inflexible.

protected
I am happy to expose a few more methods here. Typically virtual methods so that anyone inheriting from my class can hook in their own code and modify the behaviour of my class. Protected members are a little more flexible to change as fewer lines of code depend on them, but I still try to change them as little as possible.

internal / friend
These are vital to me. They allow me (and only me) access to certain members of other classes. I find this invaluable when I need to have some kind of collaboration between classes in a framework that nobody else should have access to, including descendants of my own classes that are not in the same assembly/module.

private
An absolute necessity. Access to some members would allow a consumer to manipulate members of your class without obeying "the rules". This would include adding items to a private array member without going through the relevant method which might perform other tasks such as


  1. Enforcing read-only.

  2. Checking security permissions.

  3. Executing other methods to enforce proper business procedure.



As you can see, I feel very strongly about encapsulation. It provides flexibility to change the way you provide your service and enforces correct behaviour. When you order new computer from a manufacturer you don't walk along the conveyor belt interfering with the way each element is added do you? You interface with the manufacturer using the approved process (public PlaceOrder) and then leave them to get on with the 100 or so private procedures they go through to fulfill the order.

Never before have I read so little about a language before deciding it is a waste of time proceeding! Maybe encapsulation will be implemented along with the new garbage collection features (I wont go into reference counting) that is coming in the next version of Mac OS X. I'll have to wait and see!

Enough rails for me

That's it, I've had enough of Ruby on Rails!

I like the rails part, it's a very clever approach, but I really dislike the Ruby part!

The final straw occurred yesterday. It's very common in OOP to have the constructor set default values for your object just in case the consumer of your class does not set them. In C# I would do something like this.....

public class Post : MyBaseClass
{
private bool isPost = true;
}

In Ruby I was trying to achieve this simple behaviour...

01: IsPost is set to true
02: A form is displayed with the default value
03: User changes the value
04: My Post instance is updated with the values from the form

I tried to override Initialize() only.

def Initialize
super
is_post = true
end

Now Post.new(params[:post]) is not reachable for some reason.


So in my Post class I did this

def Initialize(* params)
super(params)
is_post = true
end

The problem here is when I try to initialize the values from the form in my controller class.

post = Post.new(params[:post])

This means that the super(params) is called (which sets is_post to the value in the form) and then sets is_post = true. So my value is always true regardless of what the user enters.

So I tried putting "is_post = true" before the call to super, but that throws a null reference exception.

Finally someone told me to merge hashes like this

{:is_post => true}.merge(params);
super(params)

I don't recall the error, but that didn't work either.


I'm sorry, but ANY language that gives me this much hassle just to set a default is not worth it.

C# on rails, now *that* would be nice!

2007-06-01

Installing Rails on a Mac

What a lot of grief!

After trying more simple steps on another site (and failing) I eventually managed to get the following steps to work:

http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx


There were however some changes:

01: Make sure you download TextMate. I kept reading it as TextEdit for some reason, so just be aware of that when "mate ~/.bash_login" wont work.

02: One of the steps tells you to type "make ~/.bash_login". I found that adding the suggested text to that file made no difference when you log out/in or close the Terminal window. This was because a file named "~/.bash_profile" existed. If this file exists then you should modify it instead of the one mentioned in the article.

03: When trying to do "rake db:migrate" I would experience the following error:

otherwise you will experience the following error:
dyld: NSLinkModule() error
dyld: Library not loaded: /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib
Referenced from: /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7/lib/mysql.bundle
Reason: image not found
Trace/BPT trap


This is because the latest version of MySql has changed a path from lib/mysql to just lib/. To fix this problem you need to type the following (it's all one line):

sudo install_name_tool -change /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib /usr/local/mysql/lib/libmysqlclient.15.dylib /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7/lib/mysql.bundle

Right then. Now that those Hellish few days are over it's time to get annoyed with trying to write a website I suppose :-)

2007-05-16

Convert absolute path to relative path

Today I needed to convert an absolute path to a relative path based on a specified base path. E.g.

c:\a\b\c -> c:\a\b\c\d\file.txt = d\file.txt
c:\a\b\c -> c:\a\file.txt = ..\..\file.txt
c:\a\b\c -> c:\a\x\file.txt = ..\..\x\file.txt

I am surprised there is nothing in the .NET framework so I had a hunt around and converted the code from the following URL (http://www.vergentsoftware.com/blogs/ckinsman/default.aspx?date=2006-08-07) into C#....


private string RelativePath(string absolutePath, string relativeTo)
{
string[] absoluteDirectories = absolutePath.Split('\\');
string[] relativeDirectories = relativeTo.Split('\\');

//Get the shortest of the two paths
int length = absoluteDirectories.Length < relativeDirectories.Length ? absoluteDirectories.Length : relativeDirectories.Length;

//Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;

//Find common root
for (index = 0; index < length; index++)
if (absoluteDirectories[index] == relativeDirectories[index])
lastCommonRoot = index;
else
break;

//If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
throw new ArgumentException("Paths do not have a common base");

//Build up the relative path
StringBuilder relativePath = new StringBuilder();

//Add on the ..
for (index = lastCommonRoot + 1; index < absoluteDirectories.Length; index++)
if (absoluteDirectories[index].Length > 0)
relativePath.Append("..\\");

//Add on the folders
for (index = lastCommonRoot + 1; index < relativeDirectories.Length - 1; index++)
relativePath.Append(relativeDirectories[index] + "\\");
relativePath.Append(relativeDirectories[relativeDirectories.Length - 1]);

return relativePath.ToString();
}

2007-05-08

Acer Skoda - I mean Ferrari

I have owned an Acer Ferrari now for approximately 13 months. Previously I had a problem with it freezing randomly. I spent some time trying to reproduce the problem and after a month or two was finally able to reproduce it 100% of the time. I sent my laptop back to Acer armed with exact steps and as a result my laptop was returned to me after only a couple of days.

For the past 2-3 months I have been seeing very rare, random resets. Obviously I blamed Windows, as you do :-) Some software I had to convert AVI to MPEG would always reset the laptop, but I just put that down to dodgy software. More recently I noticed that 7-zip would also reset my laptop *only* if I used ULTRA compression on a file larger than 500MB.

So I tried Winzip, same problem. I installed Vista and tried both Winzip and 7-zip in there, same problem. With steps to reproduce + proof that it was not the OS I felt I could finally send the laptop back for repair without running the risk of it coming back "No fault found".

The problem lies in my opening sentence "I have owned an Acer Ferrari now for approximately 13 months". The problem is that in my quest to find 100% reproducible steps my warranty has expired by about 30 days.

I explained all of this to the guy on the phone at Acer. "If only more customers were like you!" he said, and then continued to tell me that I had to pay for the repair. After talking over me for a few minutes I finally lost my temper and swore at him, something I have never done before.

So, now I am really annoyed! I have an old £400 Hewlett Packard laptop that has been working (slowly) without problems for years now, yet I pay thousands for a laptop with a quality brand such as "Ferrari" and it breaks twice in 13 months! I'd expect the quality to be better, wouldn't you?

What *really* annoys me though is that Acer didn't send me a card telling me my warranty was nearly up and asking if I would like to extend it (which I would have done), and ofcourse I had no idea it was coming to an end because the receipt is.......surprise surprise it is with my accountant who is preparing last year's accounts!

Ferrari I am disappointed. You have put your name to a machine that has the body of an F1 and the inner workings of a cheap old banger!

Sprites

Someone sent me this on Skype this morning. I think it's really cool, it reminds me of the hardware sprites on the old Commodore 64!

01: Go to any site with lots of images (image search on google is a good one)
02: Once the images appear copy/paste this text into your address bar.

javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i<DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval('A()',5 ); void(0);

2007-02-16

ECO JumpStart

I've been up quite late working out what I want to cover in the "ECO jump start" document. The trick is to start at a level where the user knows absolutely nothing, and end up where they know enough to decide whether or not they wish to spend some time learning how to use ECO or not.

I think I should start off explaining why multi-tiered app development is a good idea; then I think I should go on to creating a package in a DLL; then onto creating a non-persistent WinForm app using that package; then make it persistent; and so on. My problem is that ECO just does so much!

I'll paste what I have so far at the bottom of this blog entry to get any feedback. As the list progresses the subjects become more advanced and I am worried that the jump start might actually frighten people off by making them feel overwhelmed. Maybe I should just take it so far and then leave the more advanced items out? Maybe I should just include a section at the end of the document explaining what advanced features are available without demonstrating how to use them? Then maybe do an "Advanced ECO" document in which I follow on where I left off and demonstrate the more advanced stuff.

At the moment I am undecided, what do you think? Remember, this document is really intended to server two purposes; it should be there to help new users to understand ECO, and also to act as a promotional ("marketing" if you like) document to boast how good ECO is without scaring people away.

And now the table of contents:


Overview
Why create separate layers (GUI / Business / Data access)
An overview of how ECO uses UML as the design tool to sepcify the business layer
Explain that ECO can persist those business objects in a number of ways, the DB being switched in a matter of seconds


Creating a business classes package
Create project
Add references
Configure package settings
Add 2 classes with n--m association
Add properties
Add association
Generate code

Creating a non-persistent WinForm app
Create ECO WinForm project
Add reference
Select packages

Adding some simple GUI
Delete all but rhRoot
(Design support)
Set EcoSpace type
Add DataGrid
Set EcoSpaceType
Add ExpressionHandle (allInstances)
Bind the datagrid to the ExpressionHandle

Adding new objects
Add EcoListActions
Set RootHandle
Add button
Set the ListAction to Add

Drag and drop
Add another ExpressionHandle
Add another DataGrid
Add another "Add" button
Add CurrencyManagerHandle, set parent = DataGrid1
Add ExpressionHandle, expression = "self.AssociationToOtherClass"
Add a DataGrid
Add DragDrop extender
DataGrid2 = DragSource
DataGrid3 = DropTarget
Run + create + drag/drop

Making the objects persistent
Add XML persistence to EcoSpace
Add EcoGlobalActions to form
Connect to RootHandle
Add button
Set GlobalAction to UpdateDatabase
Run, create, save, close, run

Creating a database
Remove the XmlPersistence
Add DB persistence + set configuration
Add connection
Generate DB
Run, create data, save

Evolving a database
Add new properties
Rename a property + set FormerName
Add new super class + move property up
Evolve DB
Run, show same data

Quick prototyping the model
Set AutoForm = True on grids
Run app, show auto forms
Add button
Set GlobalAction = debugger
Run app, show debugger, show access to auto forms

State machines
A simple state machine with triggers
Triggers with paramters
Transitions with guards
Multiple transitions out of a single state with guards (explain how only 1 must be true)
Multiple transitions out of a single state with the same trigger
Nested regions
Concurrent regions
Final state

Multi user
Unloading the cache to see changes by other users
Turning on optimistic locking
Getting conflict information
ChangeActionKind
Locking regions

Remote persistence
Creating the remote persistence server
Registering as a remotable server
Registering as a HTTP remotable server available via IIS
Switching the WinForm to RemotePersistenceClient

Multi user synchronization server
What it is, how it works
Adding it to the remote persistence server
Requesting changes from the WinForm client
Run two apps, show the propagation of changes between them

ECO and ASP .NET
Create ECO web application
Reference the same model
Explain why we have a PersistenceMapperProvider component (only about caching the mapping information for now)
Show how to generate DB / evolve from PersistenceMapperProvider instead of the EcoSpace

ECO ASP .NET autoforms for protoyping in ASP .NET

Show the ECO WebForm
Recreate the same WinForm gui in a WebForm

ASP .NET performance
Show the pool size in Web.Config
Explain what happens when more people request pages than there are EcoSpaces in the pool
Explain the SessionStrategy
Explain the maximum lifetime setting in Web.Config

ASP .NET multi-user
Explain how EcoSpaces are kept in sync with each other via the PersistenceMapperProvider
Demonstrate how WebForm data is in sync
Demonstrate how the WinForm / WebForm apps do not stay in sync with each other
Migrate the ASP .NET app to use the persistence server
Demonstrate WinForm app reflecting changes to data made in a WebForm app

Overview of each of the ECO components

ECO in code
Switching between Object and IObject
Overview of some of the ECO services
UndoService, In-memory transactions + undo blocks
DirtyListService
PersistenceService
OCLPS Service
OCL service
OCL action language service


Advanced modeling techniques
HasUserCode = true
OCL derived attributes
Overriding OCL derived attributes in subclasses
Code derived attributes
Code reverse derived attributes
Derived associations
Transient properties
Transient classes
Transient associations
Association classes
Platform independant property types
AutoInc
Blob
Memo
Delayed fetch properties
Referencing other packages
Extending classes
Adding associations
Adding class constraints
Enforcing in a WinForm application
Enforcing the same constraints in a webform application
Disabling the auto-generated multiplicity constraints
Object versioning
Navigating historical data
Retrieving all historical versions of an object

Advanced database techniques
Reverse engineering a database
Cusomizing the mapping information to fit an existing database
Evolving the structure of an existing database
Using a single model to pull data from multiple databases (including mixed servers) and updating the data too

Disabling BlueTooth on a Pocket PC

We use wireless printing through a COM port over BlueTooth. Having BlueTooth on all of the time can contribute towards energy consumption and cause the battery life on the Pocket PC to deplete faster. Now I disable BT when the application starts, and then re-enable it to print and disable it immediately afters. This adds about 1 second to each print job but it should save the battery power.


[DllImport("BthUtil.dll")]
private static extern int BthGetMode(out BlueToothRadioMode dwMode);

[DllImport("BthUtil.dll")]
private static extern int BthSetMode(BlueToothRadioMode dwMode);

public static BlueToothRadioMode BlueToothRadioMode
{
get
{
BlueToothRadioMode result;
BthGetMode(out result);
return result;
}
set
{
if (value != BlueToothRadioMode)
BthSetMode(value);
}
}

2007-02-14

Keeping a Pocket PC awake

My compact framework application imports XML into a local database. As there is so much data to import this can take up to an hour. During development there were no problems with this, but of course during development the Pocket PC is docked in its cradle which provides it with power.

When a Pocket PC is removed from its cradle it manages power differently, just like an unplugged laptop. So every five minutes the Pocket PC would hibernate and the user would have to turn it back on in order for the import to continue. During an hour the user would have to do this approximately twelve times. How annoying, and dangerous too if the employee is driving to their first job.

Anyway, I found the following very useful code on the web and thought I'd point it out as it was so useful!


public class Device
{
#region Device sleep support
[DllImport("CoreDll.dll")]
public static extern void SystemIdleTimerReset();

private static int DisableSleepCallsCount = 0;
private static System.Threading.Timer PreventSleepTimer = null;
#endregion

public static void EnableDeviceSleep()
{
DisableSleepCallsCount--;
if (DisableSleepCallsCount == 0)
{
if (PreventSleepTimer != null)
{
PreventSleepTimer.Dispose();
PreventSleepTimer = null;
}
}
}

public static void DisableDeviceSleep()
{
DisableSleepCallsCount++;
if (DisableSleepCallsCount == 1)
{
TimerCallback keepAlive = new System.Threading.TimerCallback(
delegate(object applicationData)
{
try
{
SystemIdleTimerReset();
}
catch (Exception)
{
}
}
);
PreventSleepTimer = new System.Threading.Timer(keepAlive, null, 0, 30 * 1000);
}
}
}


Now I can do this


Device.DisableDeviceSleep();
try
{
//Do stuff
}
finally
{
Device.EnableDeviceSleep();
}

2007-02-12

Dotting the I's and crossing the T's

Ever played "Spot the difference"? I'm sure you have :-)

I'm just looking through an application I have inherited from a Turkish company it was outsourced to. I was just browsing through a 5MB SQL script to generate the DB + stored procs when I saw this....

@MATERIAL_CODE=REPLACE(@REPLACE_MATERIAL_CODE,'Imprinter','Imprınter')

Does it do anything? Sure it does, but can you see what it is? There are two clues in this post but I wont tell you where!

2007-02-07

ECO book at last!

Hi all

I have finally reached a point where I can dedicate a few hours each today to "something new" and have decided that the ECO book I have always wanted to write would be great fun. My first idea is to create 2 or 3 separate items:

Title: ECO jump start
This would be in paper / PDF format and would contain a set of exercises for a new user to follow in order to get started with ECO as quickly as possible. It would basically cover creating a simple model, prototyping using auto-forms, using the different handles, databinding, parent-child grids, creating/evolving the DB, OCL / code derived / reverse derived attributes, and stuff like that.

Title: ECO API
This would be an electronic format which would basically be a "Press F1" reference.

Title: ECO book
This would either be a paper/pdf format or, if possible, part of the ECO API document. It would be a technical overview of the framework + its services and abilities. I imagine it might be similar to my ECO Services chapter, except I'd probably include a set of "How to" items at the end of each chapter.


The plan here is that we get

1: An easy way for people to assess exactly what ECO does, and to quickly start creating simple "test" applications.
2: A complete reference for information on specific items where you would normally find yourself hitting F1.
3: A conceptual overview of the whole thing, explaining how it all works together and how to achieve certain goals.


This is what I have in mind at the moment, but it could all change based on the feedback I get :-) The above is basically the order I learned in when I learned Bold and then later moved over to ECO, it goes from simple through to in-depth. What I want to know is

A: Do you think I should go for 3 separate items or try to combine the last 2?
B: Is there anything I have missed?
C: What information would you like to see?

As a closing note I will paste in a table of contents I wrote 729 days ago when I first decided this would be a good idea if only I had the spare time :-) It's not necessarily what I still have in mind, but it might get some brain cells bubbling in your heads to get us all started off. If there are any topics you guys mention that I have missed I will add them to my list. Due to its age (1 day short of 2 years old) it is based on ECO II, so there are lots of nice new ECO III items I can put in such as creating your own services, state machines etc.


An overview of ECO
What is an ECOSpace
Modeling an application
Attributes, Methods, and Relationships
OCL Derived attributes
Code derived attributes
Reverse derived attributes
Inheritance Vs Composition
Packages
Creating a simple ECO WinForms app
Creating a simple ECO ASP.net app
Creating a simple ECO Webservices app
Borland.ECO namespaces
Handles
ObjectRepresentation
Persistence
Services
Subscription
UmlRt
WinForm
Persistence
Changing persistence
Evolution
Custom persistence mapping - AutoInc etc
Reverse engineering an existing database
OCL



Also need to include multi-user support, optimistic locking, change propagation, reconciliation, object locking regions, writing your own services + OCL extensions.



Thanks for your feedback!


Pete

2007-02-01

ECO extensions 2.1 released

I have just released a minor update to ECO extensions. It contains a fix for a bug that would prevent the DirtyObjectCatcher from catching modified objects for MDI child forms.

string.GetHashCode

I recently developed a simple support application that allows users on a PC to provide a one-off security number to a Pocket PC user and when entered the PPC will perform a specific support function.

I used the same class to generate these security codes in both the desktop and compact framework applications, tested it quite thoroughly and it all seemed to work fine. However once deployed it became evident that the codes provided by our support department were being rejected by the PPC as invalid. So what went wrong?

Seeing as the same class was used for both applications I thought it would be okay to test the encoding/decoding of command numbers on only a single platform, and this was my mistake! The routines use string.GetHashCode() to add a checksum to the end of the security codes just to prevent the user from performing actions without authorisation. For reasons I cannot imagine the implementation of string.GetHashCode() is different in the compact framework from the one in the full .NET framework. It was due to the result of this method being different on the two platforms that the codes were being rejected.

So, I have disassembled the compact framework version and used that in my desktop application instead, and now it works just fine. Here is the code....

public int MyHashCode(string value)
{
int charIndex = 0;
int num1;
int num2 = 0x1505;
int num3 = num2;
char currentChar;
while (charIndex < value.Length)
{
currentChar = value[charIndex];
num1 = (int)currentChar;
num2 = ((num2 << 5) + num2) ^ num1;

if (charIndex == value.Length - 1)
break;

num1 = value[charIndex + 1];
if (num1 == 0)
{
break;
}
num3 = ((num3 << 5) + num3) ^ num1;
charIndex += 2;
}
return (num2 + (num3 * 0x5d588b65));
}

2007-01-19

Testing a website with a root path

This post is really a reminder to myself, so that I can find the information quickly for the next time I reformat my hard drive!

To run a website with a root path in Visual Studio 2005 follow these steps:
  1. Tools->External tools menu
  2. Click Add
  3. Title = Webserver
  4. Command = C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE
  5. Arguments = /port:8080 /path:$(ProjectDir)
  6. Tick "Use output window"
Whenever you need the webserver running just open your project and go to Tools->Webserver.

2007-01-17

Calling base constructors in C#

I occasionally find it annoying that I cannot specify at which point in a class's constructor I wish to invoke the base constructor. Having C# always invoke it before any of the code in my descendant constructor is executed sometimes causes me problems.

Considering .NET is capable of calling the ancestor constructor at any point I wondered why C# wont allow it. I contacted Anders Hejlsberg and he was kind enough to reply, unfortunately he seems to have answered a question I didn't ask :-)

Anyway, I have been deleting some old emails today and I came across his response, which he gave me permission to publish:

The problem with Delphi's model (allowing constructors to be called on
an already constructed object) is that it makes it impossible to have
provably immutable objects. Immutability is an important concept because
it allows applications to hand objects to an external party without
first copying those objects and still have a guarantee that the objects
won't be modified. If constructors can be called on already constructed
objects it obviously isn't possible to make such guarantees. In Delphi's
case that may be ok since Delphi doesn't really make type safety
guarantees anyway (you can cast any object reference to a
pointer-to-something and start poking away), but .NET goes further with
type safety and this would be a big hole.

There, now get out of my head ;-)

Anders

2007-01-11

SQL Server amazes me!

Well, SQL Server amazes me, but not because I am impressed!

Today I wrote an application that does the following:
01) Find all ZIP files in a specific folder
02) Open each ZIP file in turn
03) Extract an XML file from the ZIP into an array of bytes
04) Insert that data into a table

There are 1,978 files in this folder and the XML within each ZIP file is around 2MB in size.

I ran this app and was really surprised at how soon my PC started to crawl, it was so slow that it was taking over 10 seconds to switch between MSN and a Skype text-chat window. So I decided to monitor the process.....

Importing the zip data into SQL Server took a total of 19 minutes and 15 seconds (this includes unzip time). What concerns me is that SQL Server's RAM usage went up to 830MB at its peak, this is what was crippling my PC. Twenty minutes after my app had finished SqlSevr.exe was still holding over 700MB of RAM, I then restarted my PC. SQL Server was using more RAM than I had available and was forcing my PC to use virtual memory!

So I decided to try FireBird 2.0

Importing the zip data into FB took a total of 15 minutes and 15 seconds (again including unzip time). RAM usage for FB started at 21MB but dropped to 16MB at home point within the first 5 minutes. It's memory consumption remained at 16MB.

In both cases I was creating + committing a transaction around each INSERT command. In fact it was the exact same code, I just did a search + replace from Sql to Fb.

Amazing!

2007-01-02

DaDa

Today my baby girl looked at me, said "DaDa" for the first time ever, and then giggled. What a great day! :-)