Paul's profilePaul Bunting - BlogPhotosBlogListsMore Tools Help

Paul Bunting

My Twitter

Loading...Loading...

My Twitpic Feed

Loading...Loading...

Quote of the Day

Loading...

Video

 

MSDN: Channel 9 This Week

Loading...Loading...

Weather

Loading...

Paul Bunting - Blog

http://archsoftnet.spaces.live.com/blog/ ... http://twitter.com/PaulBunting
9/5/2009

Open email – scam warning

I am posting this open email here so a link can be created rather than having to forward it each time I am asked.

Sent: 05 September 2009 11:16
To: Various recipients
Subject: FW: Hey [Password stealing scam warning - please readme]
Importance: High

GENIUNE WARNING

I have received the email below a couple of times now. It claims to come from someone you know inviting you to see who has blocked you.

It takes you to a site something like “oops-you-got-blocked.com” which is a fake site claiming it is secure and inviting you to log in… It is not a genuine site, the email is spam and the site is not secure, it is a fake site designed to steal your password, contacts and possibly emails. DO NOT LOG INTO IT.

Things you should watch out for

  • You should never log into a site that says it is secure but does not have a padlock in the browsers toolbar (normally somewhere up near where you typed the address), it should also start with https:// and NOT http:// in the address bar. You should be able to click on the padlock and view the sites certificate, who issued it and who it is registered to.
  • If you are at all in doubt you can also run a whois query (google “whois” for lists of whois seach engines ie http://whois.domaintools.com/ is one). It will tell you who bought the domain, how long they have had it, etc.
  • If you whois “oops-you-got-blocked.com” you should see it is registered to someone (probably a hacker) in Beijing and hosted in Hong Kong, so will be nothing to do with Microsoft.
  • Another give away is that there are no genuine terms and conditions on the site.
  • You can also google part of the content of a suspect email and if you find references to it online then you know it is fake.

If you have already fallen for the scam

  • Warn all your contacts not to click on the link and certainly not to attempt to log into it. It will have had access to your entire address book and will have emailed your contacts as if it was you.
  • Change your passwords NOW, if you use the same password anywhere else remember to change it on all sites you use it on
    (you can change your hotmail password via Microsoft’s passport site www.passport.com, down the bottom click on “Sign In to Account Services”)
  • It has had access to your entire email account, it could have accessed your email as well as your contacts.
  • Make sure you report the breach to you hotmail.

Please feel fee to pass this email on, but if you do so please ensure you send to your contacts using the BCC field (if it is not visible you can normally make it so in your email account settings), the BCC field will hide all your recipients from the other recipients and any forwarded messages, thus protecting the privacy of your contacts.

Paul

--------------------------------------ORIGINAL MESSAGE
From:
xxxxxxxxxxxxxx [mailto:xxxxxxxxxxxxx@hotmail.co.uk]
Sent: 05 September 2009 09:37
Subject: Hey

Hello!
xxxxxxxxxxx@hotmail.co.uk invited you to check who has deleted or blocked you from their contact list on MSN Messenger.

It's Easy, Secure and Free!

Try it Now, Click Here

Thanks
Status Checker Team.
______
This mail is sent by xxxxxxxxxxxxx@hotmail.co.uk using MSN status checker Application.
This is NOT Spam.

2/16/2009

LINQ to SQL and Entity Framework… issues I have encountered with EF.

For the past 6 months or so there has been various discussions raised on the LINQ to SQL (released with .NET 3.5) vs. Entity Framework (released with .NET 3.5 SP1). Including several blog posts claiming that LINQ to SQL is dead (here, here and here are a few of them) and that LINQ to SQL is to be replaced by EF; these blogs were a result of an ADO.NET team blog post.

This is probably in part to do with the fact that LINQ to SQL was written by the C# development team, and EF is developed by the ADO.NET team, and they were both developed in parallel.

The ADO.NET team started development of the Entity Framework in 2003; at around the same time the C# design group embarked on developing a LINQ to SQL extension as a “stand-in” for the abandoned ObjectSpaces O/RM tool. I have read that LINQ to SQL was mainly written to help aid the development of LINQ in particular for the AND/OR query clauses.

Apparently the two projects were developed with little communication between the teams and in 2006 the ADO.NET team gained ownership of the LINQ to SQL and LINQ to DataSet implementations, adding LINQ to Entities into the mix.

The Entity Framework was originally meant to be included in .NET 3.5, however it was removed from early betas. It was later included in .NET 3.5 SP1. LINQ to SQL was originally developed to work with other databases, but prior to it’s release (for non-technical reasons) it was restricted to work with MS SQL Server only.

Having been playing with LINQ to SQL for a bit I found it very easy to implement and program against. Pretty much just dragging my tables into a DBML and by customising the MySettings class it allowed me to use the same connection settings as previous datasets and non-.NET apps. It made it very easy to integrate some LINQ to SQL with existing application development.

The database I am currently developing with has 364 tables (with around 850 FK links between them). Though dropping all of these onto a LINQ to SQL DBML file takes a while for Visual Studio to process, it works. Customising the DBML would be a bit of a pain however, given the lack of update facility in the designer (though there is a tool I am looking at http://www.huagati.com/dbmltools/, which would also allow me to standardise legacy naming conventions).

Would be nice to be able to spread the tables across several DBML files, allowing commonly used tables to be included in each LINQ to SQL class. Not had much chance to look at this but seems to cause problems when just dropping the tables into separate files.

Dropping the same tables into an Entity Data Model takes around the same time, but crucially any time I attempt to update the diagram in the designer, or often just selecting a table, Visual Studio bombs out, crashing.

There are bound to be several benefits to using the EF, such as: - 

  • Full provider model with support for multiple RDBMSs, including SQL Server Compact.
  • Not tied to a one:one relationship between entities and database tables.
  • Support for Table per Class and Table per Concrete Class hierarchy models.
  • Support for entity-splitting and complex types.

Though it may be the most serious issue I have encountered, this is not the only issue I have had with EF.

One common thing I do through-out code is check and see if changes have been made to a dataset or LINQ to SQL model. something easily achieved in both (<context>.GetChangeSet.Deletes, etc in LINQ to SQL and <DataTable>.GetChanges for DataTables).

It took me a fair amount of research to find how to do it for an Entity Data Model… and disappointingly it is not as simple a command: -

Dim changes  = _
     <context>.ObjectStateManager.GetObjectStateEntries( _
            EntityState.Added Or _
            EntityState.Modified Or _
           
EntityState.Deleted  _
            ).[Select](Function(o) o.Entity).OfType( _
                  Of <my table>)

Another problem I have had is to do with the connection settings. When you use an Entity Data Model, the connection string is not added to the MySettings class, it is just added to app.config. This make it a bit more involved to share connection settings in a common method (as discussed here). Plus when you go into the entity model, the connection setting is not selectable (or editable) in the design view.

Overall both are a good base and are much improved over having to write my own custom functions to build DataSets and DataAdapter from the large database schema. With both LINQ to SQL and LINQ to Entity, I can get past having to embed query strings in a DataAdpter. Building some classes that achieve the same thing but allow them to be debugged and syntax checked by Visual Studio when a schema update occurs.

As for which to use… Entity Framework looks to be the focus of future Microsoft developments in Visual Studio 2010, but in Visual Studio 2008 I like LINQ to SQL because it seems more integrated with the development environment, development is easier and it doesn’t crash Visual Studio! I do hope that the Entity Framework issues are fixed in .NET 4.0.

For now I will be continuing with LINQ to SQL, but ensuring it is separated out as much as I can, with as little customisation as possible, so that if need it can be easily replaced by Entity Data Models in the future.

Paul B
MCP

PS Eric Nelson posted a small blog and slides on Entity framework (Battle of the ORMs slides and links) today at http://geekswithblogs.net/iupdateable/archive/2009/02/16/battle-of-the-orms-slides-and-links.aspx

2/15/2009

Customising MySettings class in .NET to override a connection string.

With .NET 2.0 came the “My” classes, a useful feature of the classes was the integration into the “app.config” file and project properties for configuration of settings, including connection strings.

In order to maintain compatibility with previous app that share database configuration settings with new modules and developments, it would be nice to be able to override settings. It would be nice to use customised connection settings yet seamlessly use built in features of Visual Studio to set connection strings for DataAdapters, LINQtoSQL, etc.

Thankfully this is easily achieved in .NET 2.0 and onwards (with the exception of Entity Models, released with .NET 3.5 SP1, which I will go into in another blog).

To modify simply open the project properties, and switch to the the settings window and click on the “View Code” button.

Settings

This will add a partial class for “MySettings” to your project. From which it is easy to override any settings.

To override a particular property or group of properties is easy by overriding the “Item” property: -

Partial Friend NotInheritable Class MySettings
    Default Public Overloads Overrides Property Item(ByVal propertyName As String) As Object
       
Get
            If
propertyName = "MyDatabase"
Then
                Return
Connection.GetConnectionString

           
Else
                Return
MyBase.Item(propertyName)
           
End If
        End Get
        Set
(ByVal value As Object)
            If propertyName = "MyDatabase" Then
               
Connection.SetConnectionString(value)

           
Else
                MyBase.Item(propertyName) = value

            End If

        End Set
    End Property
End Class

Where “Connection” is a custom class build to use shared legacy application connection settings.

Paul B
MCP

1/23/2009

T-SQL Foreign Key ON DELETE CASCADE and ON DELETE SET NULL – Creating an alternative INSTEAD OF DELETE TRIGGER that allows multiple cascading paths...

… to avoid “Error message 1785 occurs when you create a FOREIGN KEY constraint that may cause multiple cascade paths”

Multiple-links In most cases if you need to support linking of more than one ID from one table into another table, you would use an intermediary table, this allows any number of links to be established and details of each link can be supplemented with additional details.

Multiple-links-same-table However when maintaining and upgrading an existing database that has been around for many years, or even designing a new one, were there will only ever be a need to link in a specific number of IDs, it may be the case that the IDs are placed within the other table directly. 

Doing this prevents the ability for SQL to use the ON DELETE CASCADE and ON DELETE SET NULL from working.

Multiple-links-different-tables Indeed even if there is only the one ID linked in, but there is more than one round table link you cannot have multiple ON DELETE CASCADE foreign keys established between the tables because it will also fail. Both these examples give the error “Error message 1785 occurs when you create a FOREIGN KEY constraint that may cause multiple cascade paths”.

I recently embarked on upgrading an existing database to include foreign keys relationships (previously controlled by software only), so that we could introduce full support for merge replication.

The database includes over 360 tables with around 860 relationships to be establish between tables, it is used by over 4500 users in a large number of organisations, with around 70 modules (or applications) accessing the data.

As the applications have been designed and written over the past 10 years by several developers there is no way to guarantee that all the relationships between tables are being enforced correctly… Leaving aside the creation of the FKs and the verification of existing data for another blog, I needed a way to maintain compatibility with all the applications currently using the database. The only way to do this while properly enforcing the foreign keys would be to delete or set to NULL the appropriate linked data on deletion of a row, rather than rely on the application to perform the action.

Having searched for quite a while I found that there was many examples of people recommending using an INSTEAD OF DELETE TRIGGER, to accomplish it, though I could not find any examples. After searching for some time the only examples I found contain hard coded references to linked tables. This is no way ideal and would be a nightmare to maintain with over 860 foreign key links between over 360 tables, which are added to and modified on a regular basis as the applications continue to grow.

Anyway to cut a long story short and get to the code I wrote a TRIGGER that would satisfy my needs, based on a few assumptions.

  1. If a Foreign Key column is nullable I am assuming it is not dependant on the Primary Key data and should be set to NULL rather than deleted.
  2. If the Foreign Key does not support NULLs then the data must be dependant on a valid link and will be deleted… obviously in this case I am assuming that all of the FKs that do not support NULLs are not to be set to a default value from the PK table.
  3. Within the TRIGGER I am will be checking if the FK constraint has a referential action, is disable, is trusted (enforced) and also isn’t a link back to the PK table itself.
  4. The Trigger is designed to fail if fired against a table that has a multi-column Primary Key.

It is worth noting when using a trigger it can be run on multiple rows and is not run for each row deleted, so must handle this

Now I am a bit short of time tonight so I am just going to just show the source of what I have done and assume you will be able to interpret it….

The trigger will cycle through all Foreign keys based on the tables name and the primary key column name. It then deletes or updates the dependant data as appropriate, as it does so (assuming the same trigger is on the dependant tables) if the dependant tables have an update or delete trigger on them it will in turn trigger that causing a cascade effect… As a result of this it could potentially result in a long loop so needs to be used with caution and the database’s foreign keys need to be established carefully.

You should not use this trigger if you are unsure, and I bear no responsibility for any data that is lost as a result of it’s usage! The trigger is based on T-SQL for MS SQL Server 2005, INSTEAD OF DELETE does not work in MS SQL 2000 and I have not tested it on MS SQL 2008.

The trigger looks something like the following; it is based on a table called [Table_1] with a Primary Key column that is either an integer or varchar called [ID], I will be adding it to all tables I’ll be writing a script that just runs through all the tables adding it: -

Code revised 31st Jan. 2009

 

IF  EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[TRIG_PK_Table_1_Delete_FKs]'))

DROP TRIGGER [dbo].[TRIG_PK_Table_1_Delete_FKs]

GO

 

/**

CODED BY: Paul Bunting (http://www.paulbunting.com/)

Created: 23/01/2009

Modified: 31/01/2009

**/
CREATE TRIGGER TRIG_PK_Table_1_Delete_FKs ON dbo.Table_1

INSTEAD OF DELETE

AS

BEGIN

    SET NOCOUNT ON

      PRINT '***************************************************'

      PRINT 'RUNNING - TRIG_PK_Table_1_Delete_FKs'
 
      -- before doing anything lets check we
      -- actually have something to delete!
      DECLARE @Counter_DELETED_ROWS INT
      SELECT @Counter_DELETED_ROWS = COUNT([EQUIPMENTID]) FROM DELETED

      IF (@Counter_DELETED_ROWS <= 0)
      BEGIN
          PRINT ''
          PRINT '0 records deleted'
          PRINT ''
      END
      ELSE
      BEGIN   

    -- 1) we only wish to deal with tables that have one Primary Key...

    --    so lets check that this table has only one Primary Key

    DECLARE @Counter_PKs INT

    SELECT @Counter_PKs = COUNT(Col.Column_Name) FROM
            INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col

            INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab

            ON  Col.Constraint_Name = Tab.Constraint_Name AND
            Col.Table_Name = Tab.Table_Name

      WHERE Tab.Constraint_Type = 'PRIMARY KEY' AND
            Col.Table_Name = 'Table_1'

     

      -- 2) Verify only 1 found

      PRINT '  Found ' + CONVERT(VARCHAR(255), @Counter_PKs) +
            ' primary keys in table'

      IF (NOT (@Counter_PKs = 1))

      BEGIN

            -- Lets throw an error if this trigger has been established 

            -- against a table with more than one Primary Key, could

            -- alter this later to handle them but no requirement to

            -- do so at the moment

            DECLARE @tn varchar(4000)

            SELECT @tn = object_name(parent_obj)
            FROM sysobjects WHERE id = @@procid

            SET @tn = 'Casscade deletes not allowed for this ' +
                   'table due to multiple primary key columns: ' + @tn

            PRINT @tn

            IF EXISTS(SELECT TOP(1) * from deleted) 

                  RAISERROR (@tn, 16, 1)   

      END

      ELSE

      BEGIN

            -- 3) Lets get all the Foreign Keys assosiated with this table

            DECLARE @ForeignKeys TABLE (TableTempID bigint
               IDENTITY(1,1) PRIMARY KEY CLUSTERED,

               [Name] varchar(2000),[OBJECT_ID] int, [is_disabled] bit,
               [is_not_for_replication] bit,
               [delete_referential_action] int,
               [update_referential_action] int,
               [Fk_table_name] varchar(2000),
               [Fk_table_schema] varchar(2000),
               [Pk_table_name] varchar(2000),
               [Pk_table_schema] varchar(2000),
               [Fk_col_name] varchar(2000),
               [Pk_col_name] varchar(2000),
               [constraint_column_id] int,
               [is_not_trusted] bit)

           

            INSERT INTO @ForeignKeys
               ( [Name], [OBJECT_ID], [is_disabled],
                 [is_not_for_replication], [delete_referential_action],

                 [update_referential_action], [Fk_table_name],
                 [Fk_table_schema], [Pk_table_name], [Pk_table_schema],

                 [Fk_col_name], [Pk_col_name], [constraint_column_id],
                 [is_not_trusted] )

               ( select Fk.name, Fk.object_id, Fk.is_disabled, 
                  Fk.is_not_for_replication, Fk.delete_referential_action,

                  Fk.update_referential_action,
                  object_name(Fk.parent_object_id) as Fk_table_name,

                  schema_name(Fk.schema_id) as Fk_table_schema,
                  TbR.name as Pk_table_name,
                  schema_name(TbR.schema_id) Pk_table_schema,

                  col_name(Fk.parent_object_id,
                  Fk_Cl.parent_column_id) as Fk_col_name,

                  col_name(Fk.referenced_object_id,
                  Fk_Cl.referenced_column_id) as Pk_col_name,
                  Fk_Cl.constraint_column_id,

                  Fk.is_not_trusted

                 from sys.foreign_keys Fk
                  left outer join sys.tables TbR

                 on TbR.object_id = Fk.referenced_object_id
                  inner join sys.foreign_key_columns Fk_Cl

                 on Fk_Cl.constraint_object_id = Fk.object_id

                 where

                  ( ([TbR].[name] = 'Table_1') AND
                    (col_name(Fk.referenced_object_id, 
                        Fk_Cl.referenced_column_id) = 'ID') AND
                    ([Fk].[delete_referential_action] = 0) AND

                    ([Fk].[is_disabled] = 0) AND
                    ([Fk].[is_not_trusted] = 0) ) )

 

            -- 4) Lets see how many Foreign Keys we are dealing with

            DECLARE @Counter bigint

            DECLARE @MaxTableCount BIGINT

           

            SELECT @MaxTableCount = max(TableTempID) from @ForeignKeys

            SET @Counter = 1

            PRINT '  Found ' + CONVERT(VARCHAR(255), @MaxTableCount) +
                 ' foreign keys links for this table'

 

           


            DECLARE @Fk_table_name VARCHAR(2000)

            DECLARE @Pk_table_name VARCHAR(2000)

            DECLARE @Fk_col_name VARCHAR(2000)

            DECLARE @Pk_col_name VARCHAR(2000)

            DECLARE @is_not_trusted BIT

            DECLARE @is_disabled BIT

            DECLARE @delete_referential_action INT

            DECLARE @Fk_col_IsNullable BIT

           

            DECLARE @sqlQuery nVarchar(4000)

           

            -- 5) Lets cycle through all the Foreign Keys

            WHILE (@Counter <= @MaxTableCount)

            BEGIN

                 

                 SELECT @Fk_table_name = ltrim(rtrim(Fk_table_name))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @Pk_table_name = ltrim(rtrim(Pk_table_name))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @Fk_col_name = ltrim(rtrim(Fk_col_name))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @Pk_col_name = ltrim(rtrim(Pk_col_name))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @is_not_trusted = ltrim(rtrim(is_not_trusted))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @is_disabled = ltrim(rtrim(is_disabled))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 SELECT @delete_referential_action =
                   ltrim(rtrim(delete_referential_action))
                  FROM @ForeignKeys WHERE TableTempID = @Counter

                 

                 SELECT @Fk_col_IsNullable = c.IsNullable
                  FROM syscolumns c WHERE c.id =
                   OBJECT_ID(@Fk_table_name) AND
                   c.NAME = @Fk_col_name

                 

                  PRINT '---------------------------------------'

                  PRINT '  Processing key ' +
                    CONVERT(VARCHAR(255), @Counter) + ' of ' +
                    CONVERT(VARCHAR(255), @MaxTableCount)

                  PRINT '  @Fk_table_name "' + 
                    CONVERT(VARCHAR(255), @Fk_table_name) +
                    '" ~ @Fk_col_name - "' +
                    CONVERT(VARCHAR(255), @Fk_col_name) + '"'

                  PRINT ''

 

                  -- 6) Lets verify that there is no CASCADE or
                  --    UPDATE already on the Key link and that
                  --    it is not a system table... also

                  --    need to verify that the Foreign Key Table
                  --    is not the same as the current table and
                  --    that Key should be enforced.

                  IF (

                              (@delete_referential_action = 0) AND

                              (@is_not_trusted = 0) AND

                              (@is_disabled = 0) AND

                              (@Fk_table_name <> 'dtproperties') AND

                              (@Fk_table_name <> 'sysdiagrams') AND

                              (NOT (@Fk_table_name = @Pk_table_name))

                        )

                  BEGIN

                 

                        -- 7) Because we need to execute deletes with
                        --    a "EXECUTE sp_executesql..." statement the

                        --    DELETED table is not available so we must
                        --    cycle through it in case it is a batch
                        --    delete.

                        --

                        --    We convert the values to Varchar here to
                        --   account for PKs that are text and integer,
                        --   plus we need it as text later

                        --

                        --    This is because we do not know what all
                        --    the FK column names will be so we need

                        --    to build the query

                        DECLARE @DELETED_ROWS TABLE
                          (TableTempID bigint IDENTITY(1,1)
                            PRIMARY KEY CLUSTERED,

                           [ValueINNER] varchar(4000))

                       

                        INSERT INTO @DELETED_ROWS

                          ( [ValueINNER] )

                          ( SELECT CONVERT(VARCHAR(4000), [ID])
                            from DELETED )


                        DECLARE @CounterINNER bigint

                        DECLARE @MaxTableCountINNER BIGINT

                        DECLARE @ValueINNER VARCHAR(4000)

           

                        SELECT @MaxTableCountINNER = max(TableTempID)
                          from @DELETED_ROWS

                        SET @CounterINNER = 1

           

                        -- 8) Lets cycle through all the ROWS to be deleted

                        WHILE (@CounterINNER <= @MaxTableCountINNER)

                        BEGIN

                              SELECT @ValueINNER =
                                 ltrim(rtrim([ValueINNER])) 
                              FROM @DELETED_ROWS WHERE
                                TableTempID = @CounterINNER


                              -- 9) Lets see if the Foreign Key
                              --    column allows nulls... we will
                              --    use this to guess if the linked
                              --    table is dependant on the
                              --    link (i.e. a link between

                              --    an email and it's attachments)
                              --    or if it is only a lookup link
                              --    (i.e. a link between a staff 
                              --    member and the email they edited...
                              --    may want to delete the staff
                              --    member but don't want their
                              --    emails deleted)

                              PRINT '===='

                              IF (@Fk_col_IsNullable = 1)

                              BEGIN

                                    -- 10a) Set LINKED values to NULL
                                    --      (Using the like statement here
                                    --      should account for INT and
                                    --      VARCHAR PKs)

                                    PRINT '  UPDATE [' + @Fk_table_name
                                     + '] SET [' + @Fk_col_name 
                                     + '] = NULL WHERE [' + @Fk_col_name
                                     + '] LIKE ''' + @ValueINNER + ''''

                                    SET @sqlQuery = '

SET NOCOUNT OFF

UPDATE [' + @Fk_table_name + '] SET [' + @Fk_col_name + '] = NULL WHERE [' + @Fk_col_name + '] LIKE ''' + @ValueINNER + '''

SET NOCOUNT ON'

                                    --PRINT @sqlQuery

                                    EXECUTE sp_executesql @sqlQuery

                              END

                              ELSE

                              BEGIN

                                    -- 10b) Delete DEPENDANT values
                                    --      (Using the like statement
                                    --      here should account for INT
                                    --      and VARCHAR PKs)

                                    PRINT '  DELETE FROM ['
                                     + @Fk_table_name + '] WHERE ['
                                     + @Fk_col_name + '] LIKE '''
                                     + @ValueINNER + ''''

                                    SET @sqlQuery = '

SET NOCOUNT OFF

DELETE FROM [' + @Fk_table_name + '] WHERE [' + @Fk_col_name + '] LIKE ''' + @ValueINNER + '''

SET NOCOUNT ON'

                                    --PRINT @sqlQuery

                                    EXECUTE sp_executesql @sqlQuery

                              END

                              PRINT '===='

                              SET @CounterINNER = @CounterINNER + 1

                        END 

                  END

 

                  SET @Counter = @Counter + 1

            END

            PRINT '---------------------------------------'

           

            -- 11) Proceed with the delete...

            SET NOCOUNT OFF

            DELETE FROM [Table_1]

                  WHERE [ID] in (SELECT [ID] FROM DELETED)

            SET NOCOUNT ON

            --PRINT 'DELETE FROM [Table_1]

            --    WHERE [ID] in (SELECT [ID] FROM DELETED)'

      END

 

      END 

     

      PRINT 'COMPLETED - TRIG_PK_Table_1_Delete_FKs'

      PRINT '***************************************************'

      SET NOCOUNT OFF

END

GO

1/10/2009

1972 MGB GT - Part 3 - Fresh Air Vent, Cabin Heater & Pipes, Water Jets, a Wiring problem found and Engine Test Run.

Before starting work proper, on my project car, I wanted to test run the car for a bit to check the engine out after replacing most of the ignition system.

Being winter, in Scotland, I wanted to check out a few things before I did this...

First the water jets did not work; on the heavily salted roads and driving into Glasgow, I would want them working. In addition the fresh air vent that is hidden behind the centre console was jammed open and there was no heat from the cabin heater to demist the windscreen.

During the journey, bring the car back from Heathrow, it was obvious from the smell that a connection in the manual water jets had came loose. No water would exit the jets but there was a distinct smell of screen wash from the centre console indicating that the manual pump was working, just not how it should.

1972 MGB GT - Central Air Vents Removed 1972 MGB GT - Water Jet and Fresh Air VentI figured this would be a quick fix so one Wednesday night I removed the central air vents, which came out easy (and notably where not connected to their air pipes) and found that it was a simple disconnected connection.

1972 MGB GT - Driver-side Windscreen Demist Vent - reconnectedFrom here I could also see that not only were the central vents disconnected, but the main window demist vents were also disconnected, on both ends!

1972 MGB GT - Passenger-side Windscreen Demist Air Pipe - reconnected into heaterLooking into the air pipes I also found the issue the jammed open fresh air vent (which can be seen in the background of the above picture, closed)... it was jammed open due to one of the windscreen demist pipes, having falling behind the leaver preventing it from closing.

While I fixed the air pipe connections I noticed a loose wire sparking off the stereo casing. Judging for the condition of the female bullet connectors condition, it looked like it was fairly old and must have been shorting there for some time!

1972 MGB GT - Center Console I removed the radio that had been fitted somepoint fairly recently... it has MP3 support, a USB connector and SD Card slot. I taped up the loose connection with 1972 MGB GT - Rather messy wiring behind the center consolesome insulating tape and will have to investigate further where exactly it should be connected to when I dismantle that area during the rebuild process.

While re-inserting the radio, due to the mess of the wiring behind it I inadvertently disconnected clock and cigarette lighter.

Once reconnected I found I was getting heat to the demist vents, and now the fan switch did not have the water jets squirting onto it, the fan worked (well most of the time, switch seems faulty)... the heater would only give heat to the windscreen and would not fully switch to the centre console vents that continued to release cold air through the driver side, though this would be more than fine for a few test drives.  

So I had water jets and de-mist ability... this would allow me to take the car for a few test runs...

The next morning I used the car for the school run taking Caelan to nursery.

This went fine, with the car starting each time first turnover, so I decided it should be fine to take into a work meeting in Glasgow on Friday. This would be a round trip journey of close to 100 miles and provides the opportunity to the use the motorway there and to use some B roads for 3/4 of the return journey.

The journey into and out of Glasgow went without a hitch! So all going well so far.

1/7/2009

1972 MGB GT - Part 2 - Lights, Ignition System and Misfire

As mentioned in my previous post, on the journey back from Heathrow, the car had a slight misfire which was more noticeable the next day.

1972 MGB GT - engine bay, with K&N filters fitted The MGB GT has a twin carburetor, where one carburetor feeds the first two cylinders and the other the second two. As the air filters looked quite old and the misfire sounded like it was on a couple of cylinders I thought I would check them first.

As it turned out the rubber seals around the inside of the K&N filters had completely split, partially blocking the air filters.

1972 MGB GT - Briefly replaced K&N filters with standard filters Before going down to pick up the car I had ordered a service kit from the MG Owners Club, as the previous owner had told me it would be due one soon. The service kit came with two standard air filters. Though they are designed to go in the standard air filter housing I briefly replaced the K&N filters with the new standard filters to see if it would make any difference. I also gave the carburetors a quick visual check and oiled the accelerator connections.

Replacing the filters did not correct the problem so I proceeded to check the ignition system.

As it turned out there was several problems, the points were corroded, the distributor cap had worn points and corroded lead connector points, the ignition leads were corroded and damaged, and the spark plugs looked like they could do with being replaced.

1972 MGB GT - Damaged distributor cap points 1972 MGB GT - Corroded distributor lead connection points 1972 MGB GT - Spark plug 1972 MGB GT - Corroded distributor points
1972 MGB GT - Worn rotor arm 1972 MGB GT - Corroded and damaged ignition leads 1972 MGB GT - Spark plugs

As the service kit I bought came with a new condenser, points and spark plugs, I fitted these first and ordered new ignition leads, distributor cap, rotor arm, ignition coil and air filters.


1972 MGB GT - Heater blower, that appears to be the problem In addition to the misfire, when bringing  the car back up to Scotland, the window wipers had stopped working around half way back. Checking the handbook I quickly found which fuse had blown. The same fuse is used for the heater blower, which appears to be the problem.

1972 MGB GT - Added a couple of spares, just in case they are neededAs there was no spare fuses in the fuse box I had to repeatedly stop on the way back to clear the screen. As the fuses used in the MGB's are no-longer available at any service stations I also order some some from the MG Owners Club (http://www.mgocspares.co.uk/).


While picking up the car the previous owner also noticed that one of the side lights had stopped working during their journey to the Airport. While checking this I also gave the car a quick check over to see if anything else was in need of immediate replacement prior to starting on the bodywork.

While checking the lights I found that the housing for the driver side headlamp was severely corroded, and some of the wires (namely the side light ones), had came away from their terminal connectors.

1972 MGB GT - Corroded driver side headlamp. 1972 MGB GT - Worn rubber grommet from where the wires go through to the headlamp fitting 1972 MGB GT - Where the rubber grommet should be
1972 MGB GT - New verses old headlamp housing 1972 MGB GT - Most of the housing is gone 1972 MGB GT - new verses old, again

I added the headlamp housing and rubber seal to the list of parts I needed just now.


The delivery of the spares was very prompt, allowing me to replace the parts over the next few weekends, taking a few weekends off to visit relatives over Christmas and getting the car running again last weekend.

1972 MGB GT - New distributor points and condensor fitted 1972 MGB GT - New rotor arm fitted 1972 MGB GT - New leads and spark plugs fitted
1972 MGB GT - New distributor cap fitted 1972 MGB GT - New headlamp housing fitted 1972 MGB GT - New air filters fitted
1972 MGB GT - Side lights rewired on both sides 1972 MGB GT - Kieren helping me test the lights 1972 MGB GT - Engine bay after parts replaced

Having replaced all but the ignition coil and distributor itself, the engine appears to be running smoothly now, though I have only ran it for 5 to 10 miles so far, I intend run it a bit more before starting to strip out more major parts for inspection, and possible replacement.


Some of the things that are on my list to tackle next include: -

  • One of the jack points needs re-welded (will also get a mechanic to check and see if any other welding is required)
  • Some areas need to be retreated with under-seal
  • Driver side sill, to the rear, needs repaired
  • The major job... the various areas of bodywork that need attention and a re-spray
  • General engine bay tidy up (some water trap areas need touching up and repairing)
  • Source problem with de-mist pipes and manual water jets
  • Locate where cold air is entering the foot-well (cool air vent was shut)
  • Look into the heater and all associated piping (air and water), see what needs replaced and what is OK
  • Fitting electric water jets (still fitted with the old manual jets)
  • Window seals (front quarter windows in particular) need attention
  • Oil change and replace oil filter
  • Check header gasket and possibly replace
  • Re-oil SU carburetors
  • Replace timing belt (as I have no idea when it was last done) 

              ... no doubt more will be added to the list as the project unfolds ...

For it's age and the money I parted with, I am quite pleased with the condition of the car so far... particularly given the previous owner had told me that the owner, prior to them, had left it sitting outdoors unused for many years (to the point it was moss covered), before they rescued it. They gave it a little clean up and continued using it for a couple of years as a general run around until they had to sell to it to make way for a VW Camper they were purchasing.

1/6/2009

1972 MGB GT - Part 1 - A project to keep my sanity!

I had originally intended on keeping a separate blog or web-site to log my progress on this project, but have decided to just expand this blog, rather than manage yet another site.

My late Granny in early May 2004 (RIP 2008), with newborn CaelanIn late October 2008 my dad informed me that he would be giving me, my brothers and my sister a little of his share of his inheritance. My dad, his brothers and sisters had split some inheritance that Granny had left after she had passed. She passed away earlier in 2008 after suffering for several years from Cancer. True to her nature and not wanting people to fuss, she had kept her suffering a secret almost to the end, just getting on with life and only telling the family when she started to get too ill.

Though it was not a lot of money I did not want to waste it on bills or similar... So I decided it was about time to renew an old hobby, classic cars... and possibly introduce my kids to some dirt and oil.

For many years now my main hobby has been has been my job and vice versa (software development and computer programming)... hobby since 1990 job since 1997, with any spare time between the two being taken up by the kids, parenthood and the occasional computer game or xbox distraction.

In order to keep the old brain fresh for coding I figure it is time to get my fingernails dirty again.


Prior to spending most of my time in front of a computer screen I used to spend a reasonable amount under a bonnet or re-wiring my old bangers. Admittedly most of the time it was out of necessity, rather than desire... spending most of my money on books, computer hardware, OU courses and the kids meant that all of my earlier cars were bangers run on a shoe string. Saying that there was always a lot of satisfaction in doing the repairs and maintenance myself or with the help of my dad...

First car I bought was a 1973 Opel Ascona, purchased for the pricey sum of £200, this was before owning a classic became more fashionable and the prices went up quite a bit.

Though it required a fair upkeep during it's life, it went on for a surprising number of years passing between myself and my father a couple of times... There was then the 1988 Ford Sierra Estate, actually this is the car I learned to drive in before getting the Opel when I passed my test, I swapped the Opel with my Dad for this and took on the repayments for a while. Then there was a 1973 Volkswagen Beetle (that needed completely rewired, having melted all the wiring and somehow just got a MOT... always checked the electrics on a car I bought after this one and never trusted a MOT), then a Fiat 126 (my dad bought this but I used it for a while) and a 1982 Ford Capri.

My first car - 1973 Opel Ascona - on display at a classic car run at Doune Motor Museum (which closed in 1998)My first car - 1973 Opel AsconaVolkswagen Beetle (poor picture but only one I could find) 
My Dad's Anglia, having undergine a complete sand down and re-paintVolvo 940 and Ford Mondeo

I then moved onto more modern cars working my way through a Rover Mini Metro, a Ford Focus, a Citroen Picasso, a Volvo 940 Turbo, a Ford Mondeo and currently a Chrysler Voyager.

And that's just the cars I have been through (my dad's list is much longer and more impressive, including a bubble car, Ford Cortina, various customs, a Ford Anglia and many more).


So... I had a few pennies from my Granny and decided to get an old classic in need of some repairs as a way to entertain myself on weekends in my garage. I had always wanted to repair and restore an old classic rather than just keep it running out of necessity.

Having researched various types of classic cars I decided that I would go for an MGB GT. There was several reasons for this...

Firstly I had always wanted one when I was younger but they had always been out-with my budget and once I could have afforded one, well one word... "kids", so not really practical. Though the money I was getting would not normally be enough for a MGB GT; with the economic climate looking bleaker and bleaker it seemed it would be a buyers market for second-hand cars, bringing prices much closer to my budget.

Other factors came into play as well, such as the great availability of reasonably priced replacement parts for the MGB GT, through the various owners clubs... it was after all one of the most popular British small sports cars built. There is also an abundance of books and other materials on them.

Having settled on the idea and to my surprise getting instant approval from my wife, I set about trying to find one. It soon became obvious that the best place to get a good deal would be on eBay, buying a car through eBay is something I never thought I would do, but prices elsewhere still seemed high.

I was ideally looking for something 1972 or earlier (something that would be road tax exempt here in the UK, so I don't have to worry about the yearly cost as much). It had to be something needing some work, but was currently running and have a MOT so that I could get it home.

Having browsed and monitored auctions for several weeks, it was looking like I would only be able to get something post 1973 for the money I had available. So I bid on a couple of cars from 1973. Though I was the highest bidder on both they never reached their reserve and the sellers were expecting more than double what the auctions reached and more than I was willing to pay.

I was in no rush and wasn't about to be convinced to part with more money than I had budgeted.

1972 MGB GT I continued monitoring and caught sight of a red 1972 MGB GT, with no reserve that was in need of some restoration and maintenance... over the course of the week I emailed the seller numerous detailed questions, he was extremely helpful answering all my questions and sending me additional photos of various problem areas that would need repaired.

Rusting seems (1972 MGB GT) Come the end of the auction I luckily won it by one pence. The wife got a giggle that I won the auction by only one pence!

I made arrangements with the owner to pick it up from Heathrow airport and booked my flights down for the following weekend. Flying down on the first flight on 29th Nov, I spent the day (coldest of the year, in car with no working heater) driving back up.

The car did have a slight misfire on the journey back, but I just kept the revs low and took my time and it got me back home with only a few minor hiccups.

1972 MGB GT engine bay - heater needs some work but otherwise in reasonable condition Some work need to driver side cill (1972 MGB GT)Paint work flaking away (1972 MGB GT) The next morning the misfire was much more noticeable and it was apparent that would be my first task, but I will leave that for my next post.

1/4/2009

Our family pets (the Zoo!).

RobbieTo go along with our 3 boys we have 2 dogs... Robbie a cross breed, and Sophie a Jack Russell Terrier, both of whom we adopted.

RobbieRobbie we adopted Nearly 8 years ago from the NCDL (now Dogs Trust), he was around 5 years old at the time so is getting on a bit now but despite recently going deaf he still has that puppy glint in his eyes. 
 
Sophie and RobbieRobbie and Sophie Sophie (who was previously called Honey), we adopted in spring 2008... my Mother had taken her in from someone living in Auchinleck when she moved to Melrose having lived abroad for a few years, but found her a bit much with work and other commitments, so she came to live with us and was renamed to Sophie.
 
SophieSophie was around 2 years old when we adopted her and her arrival brought a new lease of life to Robbie, who seems to have enjoyed teaching her how to play fight, eat carrots and many other things.
 
 
 

In addition to the two dogs, we have a hamster, Gilbert, and the wife got herself 4 baby Giant African snails a couple of months ago.

Gilbert Gilbert Gilbert Two Giant African Snails Giant African Snail

 
 

For Christmas this year, our family expanded again, with the addition of a further two pets...

To our surprise our youngest, Caelan, expressed a strong desire to have a snake several months back. Despite being only four and normally jumping from one thing to the next in a blink of an eye, he kept constant with this one (much to mummy's delight since she had been dying for an excuse to get one).

Mummy, 'Boy-snake' (Miami Corn Snake) and CaelanCaelan and 'Boy-snake' (Miami Corn Snake)'Boy-snake' (Miami Corn Snake) having dinnerThe Wife and 'Boy-snake' (Miami Corn Snake)

So as a nice Christmas surprise, a 3 month old Miami Corn Snake would be his "big" present this year. This was much to his delight since, since he seemed to have expecting a toy one, given they are not common pets. He instantly loved it, was very eager to hold it (even attempting to give it a kiss) and he named it "Boy-snake"!

 
 

The second pet to join our family this Christmas, yet to be named, was a Red Knee Tarantula. 

Kieren, the middle boy, had mentioned sometime ago that he would like a Tarantula. Not to discourage something that would make most kids squirm we told him that he couldn't just declare that he wanted one... They need looking after, can live for a long time and he doesn't know what is required or what they need.

KierenBaby Red Knee Tarantula - starting to show it's coloursBaby Red Knee Tarantula's first skin shed

To his credit he went off on his own and researched it with impressive vigour. Researching which types are best for pets and indeed which are best as a beginner pet. He researched what they eat, how long they live, how to look after one and showed great enthusiasm giving us lots of information about them. This gave the wife the green light to go off and get a baby spiderling for his Christmas.

Yesterday the Spider shed it's first skin and 'Boy-snake' shed it's first skin (with us) on boxing day. 

6/23/2008

Customizing Visual Studio 2008 Prerequisites to allow for Visual C++ 2005 Runtime


Ran into a problem a while back after upgrading to Visual Studio 2008 and thought I would share it.

When upgrading a group of projects that included an installer project which in turn includes the Visual C++ Runtime (2005) as a prerequisite, to Visual Studio 2008, it is not immediately obvious that the Visual C++ Runtime included in the prerequisites for Visual Studio 2008, is the 2008 version only.

The application I am currently working on uses a third party component whose prerequisite include the Visual C++ 2005 Runtime, so it is not under my control which version it needs. Components developed for C++ 2005 will not register with the C++ 2008 Runtime installed.

In order to include the 2005 runtime in the prerequisites list within you must add them in as custom prerequisites for Visual Studio 2008 installers.

prerequisites

This requires getting a copy of the Visual C++ 2005 Runtime installers and placing them in the Visual Studio 2008 folder (or vice versa). You will need to rename the folders as the folders and file names for 2005 and 2008 C++ Runtimes are the same. Once in the folder they will also need modified so that you can tell which are which (2005 or 2008), you do this by modifying the "package.xml" file held in the "en" folder of each package.

Once modified Visual Studio will pick up the new list and allow you to select the correct version.

Visual Studio 2008's packages folder has changed and is now in the SDKs folder, on 32bit OS systems this will be "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages" and on 64bit OS systems it is "C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages" (default paths).

For Visual Studio 2005 the packages folder is in a sub folder of the install path, on 32bit OS systems this is "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages" and on 64bit systems it is "C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages" (default paths).

In case you do not have access to the packages for the previous versions, I have uploaded them to my SkyDrive, click here to download.

Paul B
MCP

4/4/2008

Supporting custom built email applications (using TELNET to verify connectivity).


As a software developer and/or someone who is involved in the support of software, it is often useful to prove to a client that the software is not at fault. This is doubly important if your application manages emails, given the business relevance email systems have in modern times.

As a developer who may have developed Microsoft Office based Applications in VBA, you may know that if Office get's stuck waiting on a bit code to complete or some part of the application is taking considerable time to process something, it will appear to freeze. "DoEvents" and some sort of progress windows is always advised, but not always possible if the thing that is being processed is out-with your control.

An example of this would be if you have an Microsoft Office based Application that receives, files and controls emails. If there is a problem with a clients email server, or the pop3 is running very slow, a Microsoft Office based Application would appear to freeze as you cannot process the incoming email on a separate thread.

Other applications based on other languages would be less likely to freeze as the developer will likely have the opportunity to run such processes on a separate thread to the main application. As such the end user would most likely be completely unaware that slow connection or problem with their ISP existed. The developer would have the opportunity to cancel a process after a set timeout if it continued to fail to respond and report it to the user. Even so the client may still assume the blame is with the software rather than their service provider.

It therefore becomes essential to prove your case to a client.


One of the easiest and effective ways of proving your application is not at fault is to use a telnet client.

A telnet client is often overlooked as a tool, as people might assume it is only used to connect to a telnet service but it is very useful tool if you wish to check your email connectivity or even if you just want to see how many emails are waiting for you.

NOTE: Windows has always had it's own telnet client built in, accessible via the command prompt. As of Windows Vista, it is now an optional component, that you must switch on (via the "Turn Windows Features On or Off" window in "Programs and Features", which is accessed via the control panel).

To run telnet simply open the command prompt on your system and type TELNET and press return.

You will then be in the telnet client. To connect to an email server you must use the OPEN command, ensuring you use the correct port number. The format of the command is: -

open mymail.myserver.com portnumber

Email port numbers, by default, are port 25 for sending and port 110 for receiving emails.


Retrieving emails using Telnet client (POP3)...

So if you wanted to check your incoming mail from your ISP you could type: -

open popmail.ispserver.com 110

You should then get a response code 220 followed by some server details, if your connection was successful and the server is available.

NOTE: Most servers will disconnect quite quickly if you do not submit a request.

To check your inbox you must then authenticate yourself. This is done using the USER command followed by your login name. Once you get an OK response from the server for the user name you must then enter your password with the PASS command. For example: -

user myemail@mydomain.com

Response comes back along the lines of +OK

pass mypassword

Response comes back along the lines of +OK

NOTE: Most basic telnet clients (including the Windows one) do not like it when you use the delete or backspace buttons. So if you miss-type a command you must re enter it, not just delete and type over. This is because the delete character is sent with the command rather than deleting the content you just typed.

Once connected there are several commands you can use, some are: -

LIST
This will show a list of all the emails in your inbox, often only showing the email ID and the size.

RETR +
Where + is the email ID number will retrieve an email (i.e RETR 1 will retrieve email id 1).

DELE +
Where + is the email ID number will delete an email (i.e DELE 1 will delete email id 1).

QUIT
Will close your connection to the server.

If there is a problem with a pop3 server running slow you can use the RETR command to demonstrate the download response of the server when collecting an email. It is not unusual for a pop3 server to slow considerably, it may be a hardware problem, it may be backing up to a backup device, it may even be a DoS (Denial of Service) attack against the server. When using the RETR command the response time is visible as you can see progressively how long it takes for the message to be received as each chunk of data is received and displayed.


Sending emails using Telnet client (SMTP)...

Similarly you can use telnet to check the availability of a clients SMTP server for sending emails.

From the telnet client type: -

open smtpmail.ispserver.com 25

You should get a 220 response message back.

Then you will need to declare where you are sending from, this should really be the fully qualified domain name as seen by the outside world. To do this you must use the HELO command (sometimes EHLO). For example: -

HELO mydomain.com

You should get a 250 response back.

This much tells you if your SMTP server is available.

It gets a bit more complicated if your SMTP server requires authentication, as you must first encode your username and password as Base64. There are some tools online that can do this (though I would never recommend supplying your username and password to one), as well as a few tools you can download.

In the example I use below the base64 encoding of the word username is dXNlcm5hbWU= and of the word password is cGFzc3dvcmQ=

If your SMTP server did require authentication and your username was username. Then the command would be: -

auth login dXNlcm5hbWU=

From which you should get a 334 response back asking for password, if your password was password you would simply respond.

cGFzc3dvcmQ=

From which you should get a 235 response.

Once connected, with ESMTP if required or without if not, you can test the ability to send messages. To send a message you must use the MAIL FROM command, the RCPT TO command and the DATA command. For example: -

MAIL FROM: myemail@mydomain.com

Response should come back as a 250 message.

RCPT TO: myrecipient@theirdomain.com

Response should come back as a 250 message.

DATA<press return>
Subject: My Test Email<press return>
<press return>
My test message goes here.<press return>
.<press return>

The final response you should get back is another 250 message saying it has been sent or queued.


Now obviously if you can send and receive emails with telnet and not your application, and you are certain the problem is not with your application then the cause of your original problem would more likely be some firewall or anti-virus software blocking your application.

Paul B
MCP

 

Great Book Deals

  Amazon.co.uk Widgets

Electronics Deals

  Amazon.co.uk Widgets

Disclaimer

All posts are provided "AS IS". No warranty is implied and no rights or privileges are conveyed. All trademarks and copyrights are acknowledged.

I am not associated with Microsoft, and all comments held here are my own and are done so on a personal level and not as part of my employment.

Any feeds and links used are are done "AS IS" and I have no control over their content.

Content Copyright
©2007-2008 Paul Bunting,
All Rights Reserved.


Thanks for visiting!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.

BBC Sport: F1 Mole

Loading...Loading...

BBC Formula One | UK Edition

Loading...Loading...

MSDN: Visual Basic

Loading...Loading...

ITV F1 News

Loading...Loading...

MSDN: Magazine Articles

Loading...Loading...

MSDN: SQL Server

Loading...Loading...

Science @ NASA

Loading...Loading...