I've just recently download Windows Live Writer to help me out with my blog posting.... maybe it'll make me write more stuff!
This is just a wee test post to make it works like I want.
I like to use NAnt for building my .Net projects.
I am not a huge fan of the IDE (Visual Studio), but I am well aware that I am in the minority here.
So if I plan to use NAnt on a project, I've got to do it in such a way that the other developers can still use the features of the IDE, mainly intelli-sense and the Error List window.
I've been using a very small .csproj file (described below) which calls out to NAnt, and a small custom MSBuild task to log errors so that the IDE will display them correctly.
Starting with a blank project file, you can add all the C# files with a single MSBuild entry like this:
<Compile Include="**\*.cs" />
And that's it! Now you can browse all your C# code using the solution explorer. The intelli-sense will recognise your classes and start auto-expanding as you type.
I typically add nodes for each file-type I want to be able to edit: .cs, .build, .xml, .config, etc.
The IDE will use intelli-sense for the classes defined in your C# code files. You also want it to pick up external references. Add a line for each reference you need intelli-sense for in another ItemGroup:
<Reference Include="NHibernate" />
The IDE will look for the assemblies under the path defined in the <OutputPath> node near the top of your project file. If you want, you can add a <HintPath> sub-element:
<Reference Include="NHibernate">
<HintPath>SDKs\NHibernate\NHibernate.dll</HintPath>
</Reference>
Now you'll have intelli-sense for your referenced assemblies too.
When Visual Studio builds it runs the 'DefaultTargets' for the project (usually an MS-defined target named 'Build'). When Visual Studio cleans it runs a target named 'Clean', and when it re-builds it runs the target named 'Rebuild'.
You can change the 'DefaultTargets' for the project at the top of the project file:
<Project DefaultTargets="NAntBuild" ...
Then add a target that uses the Exec task to call NAnt:
<Target Name="NAntBuild">
<Exec Command="SDKs\nant-0.85\bin\NAnt.exe" />
</Target>
Now the solution will build using NAnt. Add targets for 'Clean' and 'Rebuild' too if you want to do these via NAnt.
Note, when you customise your targets the IDE will give you a security warning. Since you customised it yourself, you can safely ignore this warning.
So far this works, but build errors are only found by examining the output window. It would be nice to have errors reported in the 'Error List' window so that you can just double-click them.
We wrote a custom MS-Build task called ExecParse, which inherits from 'Exec', to allow parsing of of the output and logging errors to the MS-Build engine.
The configuration for ExecParse takes a regular expression to search the output for, and allows logging of errors using text from captures in the regular expression.
A typical compiler error looks like:
[csc] c:\...\MyFile.cs(24,60): error CS0246: ...
So we can create a regular expression that captures this, and log an error using the MSBuild engine:
<UsingTask AssemblyFile="...\ExecParse.dll"
TaskName="ExecParse.ExecParse" />
...
<PropertyGroup>
<ExecParseConfiguration>
<Error>
<Search>\[csc\] (.*?)\((\d+),(\d+)\): error …
([^:]*): (.*?)[\n\r]</Search>
<File>$1</File>
<LineNumber>$2</LineNumber>
<ColumnNumber>$3</ColumnNumber>
<Subcategory>$4</Subcategory>
<Message>$5</Message>
</Error>
</ExecParseConfiguration>
</PropertyGroup>
...
<Target Name="NAntBuild">
<ExecParse Command="SDKs\nant-0.85\bin\NAnt.exe"
Configuration="$(ExecParseConfiguration)" />
</Target>
Now you have a NAnt build, building within the IDE, using intelli-sense, and logging errors to the 'Error List' window.
NHibernate uses an Identity Map to maintain a single instance of each persistent entity in memory. This allows you to run round a domain model without having to worry about which instance of an entity you are modifying.
NHibernate also uses Lazy Load. The default implementation for this uses Dynamic Proxy (DP) to generate a class at runtime that inherits from your real class. This generated class intercepts all of the overridable methods and properties in your class to load the instance from the database if required.
Identity Map allows you to load up the same object twice but to have your references point to the same underlying instance.
The following example NUnit test demonstrates loading 3 objects, two of them with an ID of 1, and the other with an ID of 2:
[Test]
public void TestIdentityMap()
{
MyClass myInstance1 = Session.Load<MyClass>(1);
MyClass myInstance2 = Session.Load<MyClass>(1);
MyClass myInstance3 = Session.Load<MyClass>(2);
Assert.AreSame(myInstance1, myInstance2);
Assert.AreNotSame(myInstance1, myInstance3);
}
The following diagram shows how the references relate to the objects created on the heap.
The Identity Map ensures that there is only one instance of the object with ID equal to 1 in memory.
The basic principle of Identity Map is easy to pick up. However, there is a further complication because of the implementation of the proxies used for Lazy Load.
When you ask NHibernate for an object, it returns you a instance of a class created at runtime by DP. This class intercepts any overridable methods/properties and redirects them to a second object instance that has the 'real' class type.
So in the above example, the following is actually created on the heap:
Since there are really two instances created on the heap when NHibernate gives you a proxy, there is an identity problem that you could run into.
Consider the following class:
public MyClass
{
public virtual bool IsMe(MyClass anotherMyClass)
{
return anotherMyClass == this;
}
}
The following test will fail because the reference myInstance points to the proxy, while the 'this' pointer is the real class:
[Test]
public void TestIsMe()
{
MyClass myInstance = Session.Load<MyClass>(1);
Assert.IsTrue(myInstance.IsMe(myInstance));
// The above line will fail
}
It is easy to imagine an example where a child entity notifies its parent of an event, and the parent then updates all its children except the child that initiated the event. The parent or child might have to be careful about reference comparison in such an example.
NHibernate relies on DP intercepting access to an instance of a class, and redirecting these calls to the 'real' instance. However DP is powerless to intercept private field and method access.
Situations where this could occur might be:
Consider the following class:
public MyClass
{
private int _calculation = -1;
public virtual int Calculation
{
get { return _calculation; }
}
public static void SetCalculation(
MyClass instance,
int calculation)
{
instance._calculation = calculation;
}
}
The following test will fail because the static method sets the field not on the 'real' class but the proxy object, while the property accessor is intercepted and redirected to the 'real' instance.
[Test]
public void TestCalculation()
{
MyClass myInstance = _session.Load<MyClass>(1);
MyClass.SetCalculation(myInstance, 7);
Assert.AreEqual(7, myInstance.Calculation);
// the above line will fail
}
It is not uncommon for a class to have an interface for talking to other instances of itself while keeping that interface private from outsiders.
I suspect NHibernate could be improved to make the proxies self-proxy. If this could be done then there would be no second instance created, and no potential identity problem.
To avoid problems with the 'this' pointer, simply try to avoid using it for comparison with another object.
To avoid problems with one instance of a class talking to another instance of the same class, prefer virtual methods over non-virtual ones to allow Dynamic Proxy to intercept any calls made on the instance. (i.e., avoid using private fields or methods from one instance to another instance of the same class.)
A complete example of the above, with failing NUnit tests, can be downloaded here: NHibernateProxyDemo.zip
NAnt allows you to combine tasks to automate your build process. Tasks can be grouped together into targets, and targets can call other targets (much like functions/methods in any other language).
If you need to do something that cannot be done by an existing task, you have two options:
Option 1 has some limitations; you can create a target, give it a name, and call it in your
script by using <call target="myTarget"/>. However, the target cannot specify a list of
parameters, so you have to rely on setting (agreed) properties before calling the target
if you want to pass information to it.
Option 2 allows you to create a new task for NAnt. They are considerably more effort to write and maintain as they need knowledge of a .Net language (e.g., C#). If you want to do something that cannot be done by existing tasks, then they are probably your only option.
Sometimes you want to write a task that can be done with existing tasks (e.g., check a target is up to date; if it isn't, code-generate some code, compile it, and register the assembly in the GAC), but without resorting to C#. We thought a middle ground would be nice, so we wrote a couple of custom NAnt tasks that allow you to create new custom NAnt tasks that are written using regular NAnt script.
NAntScript is an open source project we wrote to allow NAnt users to script custom NAnt tasks using only existing NAnt tasks (no C# required).
Two custom tasks are provided:
Now you can write custom NAnt tasks without resorting to C#.
Every software project needs to gather requirements, perform analysis and design, implement, and finally deliver a working system.
These days we have a lot of tools to get us through the implementation and delivery, from TDD through to automated installers (e.g., WiX). We have a maturing vocabulary of Design Patterns to aid communication in a design phase. However analysis is often left as a hand-waving exercise with less clearly defined tools and methods.
All too often the requirements gathering is merely verbatim recordings of wish lists dictated by the customer. These requirements are often recorded by people with lofty titles such as Business Analyst; ironic considering they rarely contain much, if any, analysis of the problem.
The requirements lists are usually driven out of a more traditional waterfall style development, and serve two main purposes:
The first of these points (scope) cannot be defined up-front. Software development is now recognised as new product development, and not a manufacturing process. Requirements change. Always. The way to confront this problem is to gather a few requirements at a time, implement, deliver, and iterate.
The second point is born out of a naïve company's need to be able to prove that what they have delivered is legally acceptable, despite the fact it will invariably not be what the customer wants. The preference should of course be for "customer collaboration over contract negotiation" [Agile].
The interesting question to ask when confronted by one of these bullet-list style requirements documents is "how does this help me solve the problem?" These documents rarely (if ever) contribute to a solution - instead they allow nervous project managers to relax contented in their ignorant belief that the solution can be proven correct.
So we know to avoid requirements lists. We are educated enough to use a more modern approach to documenting requirements such as use cases, user stories, scenarios, etc. Typically I choose use cases. All we need to do is describe the steps that an actor performs to complete a task, then we have accurately described our requirements, right?
Well, perhaps ... but ... it's still not necessarily analysis.
These could still essentially be verbatim recordings of the customer's description of how a given actor performs a task. But if that's all that is recorded then you've missed a trick - you've missed the opportunity to actually analyse the problem and start down the road to a solution.
Note: I am not suggesting that these use cases are useless, just that they could be so much more. If someone's already done this work then it can still be useful for verifying that delivered software actually solves the problem at hand. However, without analysis, they still don't provide progress to a solution.
The task of analysis is to define the objects in your proposed model. Rather than just defining them then forgetting them, instead they should become the nouns that you continually use when discussing functionality.
Use these nouns when discussing requirements with the customer. Use them when writing your use cases. Use them in every technical note, and every phone call. In short, use them everywhere; they then become the Ubiquitous Language [Evans] of the system.
Building the analysis model is a creative process, and as such does not have easily defined rules. Often the customer will already have names for items in the problem domain; these might translate directly to objects in the analysis model. Other times the customer might have several different names for related items, and it is the job of the analyst to abstract these to a single object in the analysis model. Often the customer will have concepts that require a new noun to be invented. These are the tools of the analyst: mapping, abstraction, and invention.
In addition to using the Ubiquitous Language, the language should be persisted along with any other documentation. It may be defined using a UML class diagram. It may be defined using a dictionary/glossary style document. It may be a combination of a class diagram and supporting text. What is important is that the definitions are concrete, and presentable to the customer.
Once the initial model is in place, the use cases can be fleshed out. It is important that the use cases use the language of the analysis model. This starts to extract the actions that can be performed the objects, and starts to highlight the relationships and constraints between them.
The process of defining the model, the objects and their relationships, the properties of the objects, the actions that can be performed on them, and the constraints on their use - that's analysis.
Suppose we take a look at the process of hill-starting a car (we'll assume for simplicity it's an automatic).
The use case (recorded verbatim) might read:
Okay, so it's a slightly artificial example - but that's only obvious to us because we know what a car is, and how it works. If we'd never seen or heard of a car before, then we wouldn't know whether these steps read appropriately or not.
This use case describes the process of getting the car moving, but does not bring us any closer to designing a car. Instead we should be analysing the scenario to try and extract the key objects in the domain.
If we start asking questions as to how someone starts the car, we might come up with the idea that a car has a "starting component" - we might even make the jump to calling it an "Ignition". We now have our second object in the domain (the first was a Car), we have a relationship defined (a Car 'has an' Ignition), we might define the actions (activate?), and a constraint (the Ignition can only be activated when the Car is not already started).
We might very quickly end up with a rich new language (Car, Ignition, Brake, Gear Stick, ...), the relationships between them (a Car 'has a' Brake), some constraints (the Gear Stick is in one of park, drive, ...), and the actions that can be performed on them (the Brake can be 'applied'). If we'd never seen a car before, the analysis might need to invent a new name for an item - although we're comfortable with the term nowadays, do you think the first person to hear the pedal called an "Accelerator" though it was an ideal name? I doubt it, but it has become an accepted part of the Ubiquitous Language of driving a Car.
The use case might now read:
Notice the nouns are capitalised. Notice there are clearly defined verbs on the nouns. We are now a step closer to building a car; we know what a car has, and can start to design some of its components. We have something that is giving us a step towards a solution.
Define the nouns, and use them religiously, not just in your use cases, but in every conversation you have with the customer. Describe the relationships between the objects in this model, and the constraints. Construct your use cases using these names as proper nouns, defining the kinds of operations that can be performed.
If you do this you'll have analysed the problem, and proposed a solution.
Avoid bullet-lists of requirements recorded verbatim. They try to limit scope. They try to define a contract. In short, they are anti-agile!