September 2007 Entries
Yep, its that time of year, when we hard working chaps head off for their fortnight in the sun. So, I'm out of here and I'll not be back until October 15 - try not to miss me too much, and don't mess up the place whilst I'm gone. :-)
This year's apple crop is looking good. 
If you are, have you ever thought about speaking at the event? If so, then get yourself across there and submit your idea for a talk. I've submitted mine, go on, you know you want to. ;-)
Is out and ready for your consumption; go get it whilst its hot! :-)
Across here Craig has a cool video running down some of the great stuff being done by the UK IT community. I even managed to sneak in a glimpse (around 1:48). Up here in Dundee we are doing our bit too, so come on in, the water's great! Technorati tags: Community, DDD5
One of the pillars of SaaS (Software as a Service) - or software + service as Microsoft like to call it - is single instance mulitple occupancy. This is an overly complicated way of saying "there's only one instance of the code and eveyone uses it". It has to be like that because if you have 1M users and you make a code update, you don't want to upgrade 1M instances right? Now that throws up a couple of problems. Firstly, if everyone's using the same instance, how do you partition your data? Well, that's an easy one to solve to be honest, you just partition your data by user. By way of example, let's create data for a Customer and partition it by User, like so. CREATE TABLE [dbo].[CustomerBase](
[CustomerId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[EmailAddress] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED (
[CustomerId] ASC )WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY] ) ON [PRIMARY]
CREATE TABLE [dbo].[Users](
[UserId] [int] IDENTITY(1,1) NOT NULL,
[Login] [nvarchar](50) NOT NULL,
[Password] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED (
[UserId] ASC )WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY] ) ON [PRIMARY]
and you'd retrieve the CustomerBase information like so
SELECT
c.FirstName,
c.LastName,
c.EmailAddress
FROM
CustomerBase c
INNER JOIN Users u ON u.UserId = c.UserId
WHERE
c.UserId = 1 -- or whatever User you need.
The second problem is a little more thorny unfortunately. The second problem is that no two users are going to describe their Customers in the same way. Now you could take the old shrinkwrapped software approach to this problem and say "we describe customers like this and if you want to use our product your customers have to look this this", but this is the 21st centuary and that's not very friendly.
So to overcome this problem, we use a metadata solution. We create a table called CustomerBase, into which we put the minimum amout of data that we require to make our software work. We then allow each User to extend the description of a Customer to suit themselves. Now each User has a unique description of their Customer, but we're still using a single instance of the codebase. Here's an example of what I'm talking about.
Create a table to hold the User's extended description CREATE TABLE [dbo].[CustomerExt](
[CustomerExtId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[FieldName] [nvarchar](50) NOT NULL,
[Type] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_CustomerExt] PRIMARY KEY CLUSTERED (
[CustomerExtId] ASC )WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY] ) ON [PRIMARY]
Then create a table to hold the value of that extended description and link it back to the original CustmerBase entry.
CREATE TABLE [dbo].[CustomerExtValue](
[CustomerExtValueId] [int] IDENTITY(1,1) NOT NULL,
[CustomerExtId] [int] NOT NULL,
[CustomerBaseId] [int] NOT NULL,
[value] [nvarchar](256) NOT NULL,
CONSTRAINT [PK_CustomerExtValue] PRIMARY KEY CLUSTERED (
[CustomerExtValueId] ASC )WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY] ) ON [PRIMARY]
Now you can get the default values for each Customer, and all the extended values, by User, using the following sql
SELECT
c.FirstName,
c.LastName,
c.EmailAddress,
e.FieldName,
e.Type,
v.Value
FROM
CustomerBase c
INNER JOIN Users u ON u.UserId = c.UserId
INNER JOIN CustomerExt e ON e.UserId = u.userId
INNER JOIN CustomerExtValue v ON v.CustomerBaseId = c.CustomerBaseId
WHERE
c.UserId = 1 -- or whatever User you need
Now, you can either do the casting of the values to their correct type in the database API layer, or pass it, as is, to the application layer to sort out, its up to you and will depend on your situation.
UPDATE: As Arnon pointed out in the comments, there are other ways to partition your data. The one I chose is this one, the other two are this and this.
Back here I posted about the ActiveRecord pattern. Since then people have written to me asking how you actually implement it with regard to the business object not having to know anything about the database, but the ActiveRecord having to deal with any number of business objects that may subclass it. Well there's a number of ways to do it I suppose, but the one I use is to do it by way of a mixture of convention and reflection. So, by way of example, if you subclass ActiveRecord to create a Customer business object then, by convention, the stored procedure that creates it is called CreateCustomer. What I do then is to reflect on the database to find out what parameters that stored procedure takes, then I reflect on the object to get the property values. Again, by convention, the object has properties matching the stored procedure parameters. I have posted an implementation of the create function below, to illustrate what I mean, along with the stored procedure I use to reflect on the database. In the next post on this topic, I'll look at the special case of serializing a master / detail relationship to the database. using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.Diagnostics;
using System.Reflection;
namespace garyshort.org.patterns.activerecord
{
/// <summary>
/// GS - Implement the ActiveRecord pattern
/// </summary>
abstract public class ActiveRecord
{
public void Create()
{
//GS - By convention this method calls the
//CreateObjectName stored procedure so we
//need to reflect on the database to find
//out what parameters that stored proc takes
using (SqlCommand reflectionCmd = new SqlCommand())
{
string procName = "Create" + this.GetType().Name;
SqlConnection conn = new SqlConnection();
reflectionCmd.Connection = conn;
reflectionCmd.Connection.ConnectionString =
ConfigurationManager.ConnectionStrings[
"ConnString"].ConnectionString;
reflectionCmd.Connection.Open();
reflectionCmd.CommandText =
"GetParamsForStoredProcNamed";
reflectionCmd.Parameters.AddWithValue(
"@storedProcName", procName);
reflectionCmd.CommandType =
CommandType.StoredProcedure;
SqlDataReader reflectionDr =
reflectionCmd.ExecuteReader();
//GS - Now we know what params the stored proc takes
//we can fill it with the object properties which, by
//convention, will be named the same as the params.
//Reflect on the object to fetch the property values
using (SqlCommand createCmd = new SqlCommand())
{
string paramName = String.Empty;
object paramValue;
while (reflectionDr.Read())
{
//GS - Get the parameter name and add it if
//its not there already. Remember the
//parameter name from the stored procedure
//reflection will have an @ before it, so we
//have to substring past it.
paramName = reflectionDr.GetString(0);
if (!createCmd.Parameters.Contains(
paramName))
{
paramValue = this.GetType().InvokeMember(
paramName.Substring(1),
BindingFlags.GetProperty, null,
this, null);
createCmd.Parameters.AddWithValue(
paramName, paramValue);
}
}
//GS - Now all we have to do is execute the
//stored proc
createCmd.CommandText = procName;
createCmd.CommandType =
CommandType.StoredProcedure;
SqlConnection createConn = new SqlConnection();
createConn.ConnectionString =
conn.ConnectionString;
createCmd.Connection = createConn;
createCmd.Connection.Open();
int newId =
Convert.ToInt16(createCmd.ExecuteScalar());
createCmd.Connection.Close();
//GS - Assign the new Id to the object
object[] args = { newId };
this.GetType().InvokeMember("Id",
BindingFlags.SetProperty, null, this, args);
}
reflectionCmd.Connection.Close();
}
}
}
/// <summary>
/// GS - Create a business object that inherits from ActiveRecord
/// </summary>
public class Customer : ActiveRecord
{
//GS - Set up some properties for our customer
private int _Id = 0;
public int Id
{
get { return _Id; }
set { _Id = value; }
}
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; }
}
private string _Surname;
public string Surname
{
get { return _Surname; }
set { _Surname = value; }
}
private string _EmailAddress;
public string EmailAddress
{
get { return _EmailAddress; }
set { _EmailAddress = value; }
}
//GS - Customer business implementation goes here
}
/// <summary>
/// GS - Create a program to test our implementation
/// </summary>
class Program
{
static void Main(string[] args)
{
//GS - Instatiate a new customer
Customer cust = new Customer();
//GS - Fill out the fields
cust.FirstName = "Gary";
cust.Surname = "Short";
cust.EmailAddress = "gary@garyshort.org";
//GS - Serialize it to the database
cust.Create();
//GS - Prove it was actually saved and we got an Id back
Debug.WriteLine("Id = " + cust.Id);
}
}
}
And here is the SQL used for reflection SELECT
c.name AS ParameterName,
t.name AS Type
FROM
sysobjects o
INNER JOIN syscolumns c ON o.id = c.id
INNER JOIN systypes t ON c.xtype = t.xtype
WHERE
o.type = 'p' AND
o.name = @storedProcName
The UK government is considering intervening in the way broadband is rolled out, in an effort to speed up the deployment of super-fast services.
[ More...]
Going home I either catch the 17:07 or the 17:15 train, depending on how fast I walk after I leave the office. Last night I caught the 17:07, which was a good job as the 17:15 never made it to Dundee.
Its just been announced that Developer Day #6 (DDD6?) will be held on November 24th at Thames Valley Park in Reading. Keep the date free folks!
... after 17 years industry experience, I'm now getting emails like this from head hunters.
"Hello Gary,
After reviewing your resume, the Attenex recruiting team believes that your skills and abilities may fit the profile for Software Engineer (Entry-Level) position with Attenex in Seattle, WA."
"Entry-Level", gee thanks guys! Can't say I'm tempted to be honest. :-)
Download the latest release of the WCSF and run the installer: then hit the "check dependencies" button and you get a dialog that looks like this: Now, since I have VS 2008 beta 2 installed it tells me I need VS 2005 (WCSF doesn't work with 2008 beta 2 apparently). Well fair enough, so I hit the "download" link it takes you to the following page: Since the main feature of this page is VS 2008 beta 2, I wonder how many people spend ages downloading that, only to find that it doesn't work with WCSF. :-(
The Blue Monster has made it to Dundee! Watch out for us at the VBUG conference this year (check out what I'll be speaking about here). What I like about the Blue Monster is that Steve, et al, has built it into such a brand that it doesn't matter that it had to go on my laptop sideways - everyone can still instantly recognise it.
| Tuesday, October 2, 2007 | | 7:00 PM to 9:00 PM | Guy Smith-Ferrier Talk for NESDN User Group Guy Smith-Ferrier will be delivering his Visual Studio 2005 IDE Tips and Tricks talk to the North East Scotland .NET User Group.... | | Queen Mother Building, Dundee University Dundee, DD1 4HN UK |
"Squeak by Example, intended for both students and developers, will guide you gently through the Squeak language and environment by means of a series of examples and exercises." Squeak by Example Technorati tags: Smalltalk, Squeak, Book
... No, not that kind of proposition, behave yourself. It was from the University where I did my agile talk the other day. Stay tuned over the next week or two to see how it pans out.
As I alluded to in my earlier post, I heard a piece of exciting news last night whilst talking at the Glasgow branch of Scottish Developers. The news is that there will be a DDD-Scotland event (along the lines of the DDD days in Reading). The date is penciled in for spring '08 at the Glasgow Caledonian University; so keep your calendars clear and watch this space for more information as and when it becomes available.
Last night's talk went well (4.33 / 5 feedback score) and despite someone organising it for the same night as the Scotland match, there was a decent turn out. :-) Got to say, the facilities at the Glasgow Caledonian University are superb, plus I heard a bit of exciting news for scottish based developers, which I'll tell you about under a seperate post once I confirm that I'm allowed to blog about it.
This post by James Robertson (product evangelist for Cincom Smalltalk) explains where they have gotten to in seven years of development of their new UI framework and well, it looks like they've gotten nowhere. It makes me nervous to see such a big player in my beloved Smalltalk's field make bad strategic decisions like this. :-(
Technorati Tags: James Robertson, Smalltalk
Just a reminder that I'll be giving my talk on agile software development in Glasgow on September 12 you can get all the details here. If you are planning on going, leave a comment on this post.
Dundee City Council (that's my council by the way so don't I feel special) have shown themselves to be a bunch clueless half wits. It was bad enough when they were only doing so in the local press, but now word has spread to the BBC.
The story is basically pretty straight forward. Pupils (and believe it or not parents too) at a Dundee primary school are violent and abusive towards staff. Staff complain, on an almost daily basis, to the council. The Council ignore the problem. Husband of Depute Head gets fed up with the lack of support from the Council and goes to the local paper. The Council suspends the Depute Head for going to the press. Amazing!!
The Council make some cracking comments in the BBC article towit:
It [the Council] has launched an investigation into the allegations, and said initial inquiries indicated some were untrue.
Meaning that some are true, including the fact (confimed by the police) that the police had to be called to a fight in the school amongst (wait for it...) some parents!
A council spokesman said: "The council is undertaking a thorough and professional investigation into all the allegations that have been made.
But only after the local press were involved, before that they ignored the problem for months.
The spokesman added that staff could report directly to the council any concerns they may have and it said complaints were always investigated.
Not true! Staff made numerous complaints to the Council (which were ignored) before they felt that they had no choice but to inform the press.
"The education department would like to reiterate that Sidlaw View is undertaking excellent work in an area of the city that suffers from a number of complex problems.
"The commitment and dedication of staff is reflected in increased levels of attainment in reading, writing and maths."
Well thanks for chipping in with that, but no one said there was a problem in that area. What they said was that pupils and parents were abusive and violent towards staff and you did nothing about it.
The question is now, with the everyone's attention focused on you, what are you going to do to fix a problem that should have been tacked months ago, not ignored. The Director of Education is on a huge salary, time she started earning it I say.
Parents have been warned of the effects of food additives on their children's behaviour after new research found a possible link to hyperactivity.
[ More...]
Meanwhile; still no cure for cancer.
Yep, yesterday the Windows Live Product set (including mail, SkyDrive, Writer and OnceCare) went live, get it here. Ah at last, the live version of Windows Live Writer, how long have we all been using the beta version; seems like years?
Hat tip to Microsoft's own Blue Monster. :-)
|