2007-07-18

Source control

At work we use SourceSafe for our version control, and I used SourceAnyWhere as the client. I would like to outline the facts and let you decide what happened for yourself.

01: I checked out my model (EcoModeler)
02: I added a new class.
03: Generated code.
04: Checked in the mode.

05: Checked out.
06: Added two new associations
07: Generated code.
08: Checked in.

09: Checked out.
10: Added a parameter to a method.
11: Changes a state machine diagram.
12: Checked in.

After each of the changes I would implement code in my application that used them, so if the changes were lost at any point during this process then I would notice because my app would no longer compile.

Today I checked out the model and noticed that my new class was missing. I backed up my generated source code and then regenerated code from the model. Using BeyondCompare I checked for differences and came up with the list of changes in the steps above.

Somehow the model in my source control had 3 revisions missing. I checked the history and sure enough there were no check in actions since June 27th. I had to recreate the changes in my model by reading the differences.

So, what do you think caused this problem?

2007-07-13

UndoBlocks, SyncServer, and multiple EcoSpaces

In an ECO app I am writing it is very important that updates by other users are identified as soon as possible. This is because there will be multiple users all selecting jobs to do from a predefined list, and it is not only possible but also very likely that more than one user will select the same job at the same time (the job at the top of the list).

To implement this I chose to use the remote persistence feature of ECO and in particular its Sync Server feature. Every 5 seconds my client apps will contact the server and retrieve a list of updates made by other users, it will then apply those updates to the cache of its own EcoSpace. So when a user selects a job to do at the top of the list their app will stamp it as InProgress and update the DB, the job will then automatically disappear from the screens of all other users.

As I like to use undo blocks in my app to allow the user to cancel changes (using my DirtyObjectCatcher component) it is highly likely that during these synchronisation operations there will be an active undo block. As a result of this any changes applied to the cache via the sync' process will be caught by the undo block. If the user decides to cancel their changes then all values in the undo block will be undone, including the synchronised changes from other users. This isn't a great problem for me because I am using the TimeStamp feature in ECO to ensure that when I update the DB I am actually updating the same values I originally fetched, but it does mean that the user is looking at old data. For example

01: User A is looking at a list of jobs, they choose to action Job 1
02: User B sees Job 1 disappear from their screen and choose to action Job 2
03: User A sees Job 2 disappear from their screen.

So far, so good. Now remember, the change of state to Job 2 was caught by the undo block that was activated when User A started to edit Job 1.

04: User A decides to cancel their changes.
05: The active undo block reverses its changes, this includes the changes applied by the sync server.

As a result user A sees Job 2 reappear on their screen.

This didn't cause me an operational problem, because when actioning a job I do this...

01: Assign to current user
02: Update DB
03: If no exception then continue to edit
04: If OptimisticLockException occurs then unload the job and tell the user another user is working on the same job and ask them to select another + unload the job from the cache (will causes it to be reloaded with the new info and disappear from the screen).

This is essential anyway because the Sync server only makes it less likely to experience a conflict, it does not make it impossible (always use OptimisticLocking unless overwrites are acceptable!).

Still, I wanted to prevent the user from seeing this message as much as I possibly could. The answer is very simple, you just use a new instance of the EcoSpace for editing the job. The UndoBlock isn't actually needed now, but I still have it because the DirtyObjectCatcher provides a great way of providing validation feedback to the user. When the UndoBlock is reversed though it will revert to old values in the new EcoSpace instance, which is irrelevant because it will be disposed once the owning form is closed. Any updates to the data will appear in the main app within 5 seconds due to the fact that the second EcoSpace instance will also use remote persistence to save the changes which will in turn ensure that all other EcoSpaces (in the same app or on different machines) will be notified of the changes.

I think I might play with this technique some more. It looks like quite a nice way of working with ECO and multiple users!

2007-07-10

CapableObjects announced

There are exciting times ahead! The team that brought you ECO for Delphi are now a separate company named Capable Objects (http://www.capableobjects.com).

ECO will still be part of the next release of Delphi, but according to the official statement (http://dn.codegear.com/article/3673>) and comments in the newsgroups it is quite obvious there will also be a version of ECO for Visual Studio!

In the past the ECO guys released patches for ECO bugs, unfortunately it was not always possible to provide a patch. With the guys now being a separate company they are free to decide their own release schedules. Instead of having to wait for a Delphi update they can now release updates as often as they wish. Just like the good old BoldSoft days when we used to get an official update really quickly if ever a critical bug was found. Not only that, but it would also seem that they will be shipping source code too!

But that's not all John!

The guys at Capable Objects have asked me to write documentation. I've been adding XMLDoc to the source code so the API help will be more up to date. As soon as possible I will be writing a quick start document for Delphi (and any other IDE's) too.

Can't wait! :-)

2007-06-23

I've changed my mind on MonoRail

It is amazing!

I'll leave that previous post just in case anyone ever needs to create an ECO object instance first and then connect it with an EcoSpace afterwards, but you don't need it for MonoRail!

  public void Join([EcoDataBind("User")]User user)

{

PropertyBag["User"] = user;

if (this.Params["User.FirstName"] != null)

{

GetErrors(user.AsIObject(), Errors);

if (Context.Params["ConfirmEmailAddress"] != user.EmailAddress)

Errors.Add("Email address confirmation does not match email address.");

if (Errors.Count == 0)

{

//TODO EcoSpace.UpdateDatabase();

Redirect("Account", "Home");

}//No errors

}

}


That's all there is to it now! I have created a small set of classes to enable ECO support in MonoRail. Instead of descending your controllers from SmartDispatcherController you will now descend from EcoSmartDispatcherController. This gives the following abilities:

  1. The EcoSpace instance is created automatically for each request.

  2. If the EcoSpace is dirty at the end of the request it is serialized and stored in the Session. Next request it is deserialized from the Session instead of being created new.

  3. You can use the [EcoDataBinder] attribute as displayed above. This will ensure that the object instance of the parameter type is created within the EcoSpace of the controller. No need to modify your business classes at all. Form data is automatically bound to the object, and when posted the binding is automatically reversed.

  4. You can add the ExternalId to your form as a hidden input like so $EcoFormHelper.ExternalId("User", $User).



I really hope I don't find an insurpassable obstacle with this framework, because I can really see myself liking it!

MonoRails, loving it

So I didn't like Ruby on Rails much. More accurately I didn't like the Ruby language or ActiveRecord much, but the "Rails" part I really quite liked! So my investigation continues and I have found myself looking at MonoRails.

MonoRails is what I would have as a child called "a rip off", but these days it is known as a "clone" :-) It's basically a .NET version of Rails, which obviously appeals to me because I liked the Model-View-Controller approach of Rails and I obviously like C#.

MonoRails has its own version of ActiveRecord (which I shall be avoiding) and an interface into NHibernate too (which I haven't looked at in great depth, but it certainly doesn't look as powerful as ECO). So I have been trying to get MonoRails working with ECO instead. Considering I don't know MonoRails at all I am surprised at how quickly I managed to do what I wanted. Take the following controller method as an example, when the user visits localhost/Account/Join the following method will be executed...

public void Join([DataBind("User")] User user)


MonoRails will automatically create an instance of User (my ECO class) and then automatically populate its contents from the form that was posted. The first problem I had here was that ECO classes have no parameterless constructors. I could have gone down the following route:

public void Join(string salutation, string firstName,
string lastName, string emailAddress, string password)


but that would mean I have to create the User instance myself and then populate its properties from the parameters. The problem with this approach is that I am just too damned lazy :-) So, my first hurdle was to allow ECO classes to be constructed + have their properties set without an EcoSpace. I created a base RootClass that all of my business classes will ultimately descend from, and then I did this...

First I created a simple class that implements IContent and stores the values in a Dictionary.
 internal class TemporaryCache : IContent
{
private Dictionary<int, object> MemberValues = new Dictionary<int, object>();
#region IContent Members
object IContent.get_MemberByIndex(int index)
{
return MemberValues[index];
}

void IContent.set_MemberByIndex(int index, object value)
{
MemberValues[index] = value;
}

#endregion

public void ApplyValues(IContent destination)
{
foreach (int currentKey in MemberValues.Keys)
destination.set_MemberByIndex(currentKey, MemberValues[currentKey]);
}
}


All other methods of IContent throw a NotImplementedException, all we need here is get_MemberByIndex and set_MemberByIndex so that simple property values (not associations) may be set before the object has an EcoSpace. At some point we need to update the values in the EcoSpace, which is exactly what ApplyValues does, it just copies its own values across to a target IContent.

Now we need to use this class as our business class’s IContent and implement a parameterless constructor.

  public RootClass()
{
eco_Content = new TemporaryCache();
}


And finally we need at some point after creation + setting the property values to be able to attach this object + its contents to the new EcoSpace.

  public void AttachToEcoSpace(DefaultEcoSpace ecoSpace)
{
if (ecoSpace == null)
throw new ArgumentNullException("ecoSpace");
if (this.eco_Content != null && !(this.eco_Content is TemporaryCache))
throw new InvalidOperationException("EcoSpace already assigned");

TemporaryCache oldContent = (TemporaryCache)eco_Content;
eco_Content = null;
this.Initialize(ecoSpace);
oldContent.ApplyValues(eco_Content);
}


Now my Join() method looks like this:
  public void Join([DataBind("User")] User user)
{
user.AttachToEcoSpace(EcoSpace);
PropertyBag["User"] = user;
if (this.Params["User.FirstName"] != null)
{
user.GetErrors(Errors);
if (Context.Params["ConfirmEmailAddress"] != user.EmailAddress)
Errors.Add("Email address confirmation does not match email address.");
if (Errors.Count == 0)
{
EcoSpace.UpdateDatabase();
Redirect("Account", "Home");
}//No errors
}
}


Standard stuff


I descend all new Controller classes from my own abstract BaseController which merely creates a new EcoSpace at the start of a request and disposes it at the end. This class also exposes an Errors property (List<string>) that I can add errors to.

 public class BaseController : SmartDispatcherController
{
protected readonly List<string> Errors = new List<string>();

protected override void Initialize()
{
base.Initialize();
EcoSpace = new ApplicationEcoSpace();
PropertyBag["Errors"] = Errors;
}

protected override void ReleaseResources()
{
EcoSpace.Dispose();
base.ReleaseResources();
}

private ApplicationEcoSpace ecoSpace;
protected ApplicationEcoSpace EcoSpace
{
get
{
ecoSpace.Active = true;
return ecoSpace;
}
private set
{
ecoSpace = value;
}
}
}

My Account controller then descends from this, like so:
 [Layout("default")]
[Rescue("generalerror")]
public class AccountController : BaseController
{
public void Join([DataBind("User")] User user)
{
user.AttachToEcoSpace(EcoSpace);
PropertyBag["User"] = user;
if (this.Params["User.FirstName"] != null)
{
user.GetErrors(Errors);
if (Context.Params["ConfirmEmailAddress"] != user.EmailAddress)
Errors.Add("Email address confirmation does not match email address.");
if (Errors.Count == 0)
{
EcoSpace.UpdateDatabase();
Redirect("Account", "Home");
}//No errors
}
}
}


Why do I set PropertyBag["User"] = user;? Simple, I can then databind to it in my view...
<h2>Join</h2>
$HtmlHelper.Form("Join.rails")
<table summary="Enter your information">
<caption>Please provide the following information.</caption>
<tbody>
<tr>
<th>
<label for="User.Salutation">Salutation</label>
</th>
<td>$HtmlHelper.InputText("User.Salutation", $User.Salutation)</td>
</tr>
<tr>
<th><label for="User.FirstName">First name</label></th>
<td>$HtmlHelper.InputText("User.FirstName", $User.FirstName)</td>
</tr>
<tr>
<th>
<label for="User.LastName">Last name</label>
</th>
<td>$HtmlHelper.InputText("User.LastName", $User.LastName)</td>
</tr>
<tr>
<th>
<label for="User.EmailAddress">Email address</label>
</th>
<td>$HtmlHelper.InputText("User.EmailAddress", $User.EmailAddress)</td>
</tr>
<tr>
<th>
<label for="ConfirmEmailAddress">Confirm email address</label>
</th>
<td>$HtmlHelper.InputText("ConfirmEmailAddress", $ConfirmEmailAddress)</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<input type="Submit" value="Join"/>
</td>
</tr>
</tfoot>
</table>
$HtmlHelper.EndForm

Did you notice the [Layout("default")] on my AccountController? That tells MonoRails to use my default.vm file as the master page for the HTML output. It basically sets up the header information etc and displays any errors, here is how it looks:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<meta name="keywords" content="" />
<title>NotASausage</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link href="/StyleSheets/Main.css" rel="stylesheet" media="all" type="text/css" />
<link href="/StyleSheets/Menu.css" rel="stylesheet" media="all" type="text/css" />

<link href="/StyleSheets/ThumbnailViewer.css" rel="stylesheet" media="all" type="text/css" />
<script src="/Scripts/ThumbnailViewer.js" type="text/javascript"></script>
</head>
<body>
<div id="MainFrame">
<div id="Header"><img id="HeaderBanner" src="/Images/Banner.jpg" alt="NotASausage" /></div>
<div id="SideBar">
<div id="MainMenu">
<h2>Menu</h2>
<ul>
<li><a href="/default.aspx">Main page</a></li>
</ul>
</div>
</div>
<div id="Contents">
#if ($Errors && $Errors.Count > 0)
<ul class="ErrorList">
#foreach($ErrorMessage in $Errors)
<li>$ErrorMessage</li>
#end
</ul>
#end
$childContent
<div id="Footer">
©Peter Morris - All rights reserved.
</div>
</div>
</body>
</html>

The $childContent indicates where to inject the output of the current page.

How cool is that? I can now model in ECO, generate my business objects, automatically generate my DB, and then finally put together some very simple application actions which will control access to those business objects and display any validation problems back to the user!

So far I like what I see!

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!