Friday, 30 November 2007

Tour Down Under payments are not secure

I went to register for the Challenge Tour (the 134km ride on 25 January 2008) today.

I clicked on a link from an email from the South Australian Tourism Commission that goes to this site - http://www.bizgate.sa.gov.au/shop/tdu/site/page.cfm?CONTENT=shop%5Ffront.

Fine.

I pick the correct event to pay for, add it to my cart, enter my details, then go to checkout. I then get a popup window asking for my contact details again (didn't they already know that?), and then the next page asks for my credit card info.

Whoa!

Hang on!

We're still on a HTTP page - no lock icon, no coloured address/location bar, nothing, just plain text sailing off into the Internet for anyone to snoop!

So I rang up the SA Tourism people, and the lady tried to reassure me - "yes, our online payment system is secure".

I was not reassured.

"Funny, we had someone else contact us with a similar query 10 minutes ago" - what a surprise.

I finally got her to find someone else who looks after the Tour down under website - a guy by the name of Darrenn. I spoke to him and explained the problem. I don't think he quite understood my concerns (not being an IT person apparently). Anyway, he gave me his email address and promised to look into it. I fired off an email to him.

Not content with that, I then thought I'd try and find someone who actually runs the BizGate site. Their top-level web page says they're run by the Department for Transport, Energy and Infrastructure. Fine. I rang DTEI and was given another phone number. I rang that and left a message.

Why do I feel a bit pessimistic that anything will get done :-(

The solution by the way, is to use this link instead:

https://www.bizgate.sa.gov.au/shop/tdu/site/page.cfm?content=search_results.cfm&mode=browse

Marvelous what a difference an 's' makes.

Update late Friday afternoon: The link has been fixed now

Thursday, 29 November 2007

Microsoft Office Mobile 6.1

Here's an upgrade for the Office Mobile applications to support Microsoft Office 2007 file formats. I wonder if this will appear on the Windows Update for Windows Mobile?

Cell broadcast name

In Australia, channel 50 is used for the "cell broadcast" information for mobile phone networks. Most often this contains the suburb or locality of the current base station the phone is talking to.

I enabled this on my new phone, and it works ok for GSM.

I've just been speaking to Optus technical support who told me that their 3G network doesnt' support this feature. A bit odd, as you'd think 3G would have more features, not less. The guy didn't know if there was some other way to retrieve the same kind of information.

One problem that I did discover is that every time the phone receives notification that it has changed base stations, it wakes up from standby mode.

That's probably why I was woken up at 2am this morning wondering who had left the light on - turns out it was my phone. Amazing how bright a backlit display can be in the middle of the night :-(

I've contacted HTC in Australia and asked them to find out if this is a bug or a feature. I'm certain my old phone didn't do this, so I consider it the latter.

I've also emailed our local Australian mobile device MVP Nick Randolph to see if he has any suggestions.

Wednesday, 28 November 2007

The old and the new

Here's a side-by-side comparison of my old HP rw6828 and my new HTC TyTN II.

  HP iPAQ rw6828 HTC TyTN II
Processor Intel® PXA270 Processor 416MHz Qualcomm® MSM 7200, 400MHz
Operating System Windows Mobile 5.0 Windows Mobile® 6 Professional
Memory 45MB available for persistent user storage
64MB SDRAM for running applications
ROM: 256MB RAM: 128MB SDRAM
Dimension 102mm (L) x 58mm (W) x 19.5mm (D) 112 mm (L) X 59 mm (W) X 19 mm (D)
Weight 140g 190g with battery
Display 56 x 56mm (2.205 x 2.205 inches) transflective TFT QVGA color, 240 x 240 pixels,0.24mm dot pitch, 64K-color support, portrait and landscape support with touch screen 2.8 inch, 240 X 320 QVGA TFT-LCD display with adjustable angle and backlight
Network GSM, GPRS, EDGE HSDPA/UMTS: Tri-band 850, 1900, 2100 MHz
HSDPA: Up to 384kbps for upload and 3.6Mbps for download
UMTS: Up to 384kbps for upload and download
GSM/GPRS/EDGE: Quad-band 850, 900, 1800, 1900 MHz (The device will operate on frequencies available from the cellular network)
Device Control   Finger scrolling and panning 5-Way navigation control
Keyboard None Slide-out QWERTY keyboard
Connectivity Class II device; up to 4 dBm transmit, typical 10 meter range
(approximately 33 feet) - High-speed, low-power, short-range
wireless communication with other Bluetooth devices Serial IrDA SIR, data transfer up to 115.2 Kb per second, USB
1.1 Client - support via HP standard mini-USB cable 802.11b (WEP and WPA)
Bluetooth® 2.0 Wi-Fi®: IEEE 802.11 b/g (WEP, WPA, WPA2)
HTC ExtUSB™ (11-pin mini-USB and audio jack in one)
GPS antenna connector
Camera Built-in UXVGA 1.3MP with LED light, 1280 x 1024 resolution,
JPEG and 3GP format, support for still image, and video
playback, H.263
Main camera: 3 megapixel CMOS color camera with auto focus Second camera: VGA CMOS color camera
Audio Integrated microphone, receiver, speaker and one 2.5mm
stereo headphone jack, MP3 stereo (through headphone jack)
Handsfree speakerphone
Built-in microphone and speaker
Ringtone formats Simple MIDI Type 0, WMA, WAV, MP3, polyphonic MIDI
  • MP3, AAC, AAC+, WMA, WAV, and AMR-NB
  • 40 polyphonic and standard MIDI format 0 and 1(SMF)/SP MIDI
Headset 2.5mm jack with single earbud style headset (mute and volume control) mini-USB connector
Battery Removable/rechargeable 1200 mAh, 3.7 Volt, Lithium polymer battery 1,350 mAh rechargeable Li-polymer battery Standby time:
  • Up to 350 hours for UMTS
  • Up to 365 hours for GSM
Talk time:
  • Up to 264 minutes for UMTS
  • Up to 420 minutes for GSM
  • Up to 120 minutes for video call
Expansion Slot Supports mini SD memory standard microSD™ memory card (SD 2.0 compatible)

Interesting things I've noticed so far:

  • While the new phone uses the "ExtUSB" port to connect the headset (meaning it should never get stuck), this does mean you can't charge or dock the device and listen on the headset at the same time.
  • The HTC device happily continues to play audio even when it loses the phone signal - my HP had this annoying "feature" that most times when I was travelling on a train through a tunnel, it would pause Media Player.
  • There's no volume control on the headset. Fortunately the headset does seem much more comfortable and clearer than the HP one.
  • The HTC memory card seems a lot faster to access.

Daylight Saving in South Australia

In most states and territories of Australia, we have daylight saving. While there probably aren't as many benefits for those living in the northern areas of the continent, I quite like it.

Next year, South Australia's daylight saving will end on Sunday 6 April at 3am. This is a change from the previous rule of the last Sunday in March, but it brings us into line with NSW, Victoria, ACT and Tasmania, which is probably a good idea.

Microsoft have been busy releasing time zone updates this year, particularly with changes in some US states and also Western Australia.

The latest revision includes updates for Australia, including Central Australian Standard Time, Australia Eastern Standard Time and Tasmania Standard Time. Updates for various operating systems are linked from the KB article.

Tuesday, 27 November 2007

Code analysis, VSLangProj and EnvDTE

If you have a Visual Studio add-in that references the VSLangProj assembly, that assembly implicitly refererences the EnvDTE assembly (version 7.0.3300.0). However that version of EnvDTE doesn't ship with VS2008.

There is a binding redirect in place for Visual Studio (via a devenv.exe.config file), but this doesn't appear to work for the Code Analysis command. I tried modifying the fxcopcmd.exe.config file to no avail.

The is the error I'm seeing:

Running Code Analysis...
C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\FxCopCmd.exe  /outputCulture:1033 /out:"bin\Debug\Altinoren.ActiveWriter.Dsl.dll.CodeAnalysisLog.xml" /file:"bin\Debug\Altinoren.ActiveWriter.Dsl.dll" /dictionary:"..\CodeAnalysisDictionary.xml" /directory:"C:\Program Files\Common Files\Microsoft Shared\MSEnv\PublicAssemblies" /directory:"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies" /directory:"C:\Windows\Microsoft.NET\Framework\v2.0.50727" /directory:"C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies" /directory:"C:\Program Files\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Common\Assemblies" /directory:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5" /rule:"C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\Rules" /searchgac /ignoreinvalidtargets /forceoutput /successfile /ignoregeneratedcode /saveMessagesToReport:Active /timeout:120
MSBUILD : error : CA0001 : The following error was encountered while reading module 'Altinoren.ActiveWriter.Dsl': Assembly reference not resolved: EnvDTE, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.
MSBUILD : error : CA0058 : The referenced assembly 'EnvDTE, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' could not be found. This assembly is required for analysis and was referenced by: 'Altinoren.ActiveWriter.Dsl.dll'.
Code Analysis Complete -- 2 error(s), 0 warning(s)

Update 29th November 2007:

David Kean has responded to my query in the MSDN forums with a workaround to this problem.

Monday, 26 November 2007

ActiveWriter for VS 2008

One of the recent additions to our toolkit has been ActiveWriter - a GUI for generating NHibernate classes and mapping files.

So before we can make the final jump to using 2008 for all our development, ActiveWriter needs to work.

Gökhan is planning an update that will work on 2008 - hopefully in a few weeks.

Wondering if it might not be too hard to do the port myself, I dove in and pointed VS 2008 at the latest source code.

Ouch!

I'm not an expert in the DSL tools stuff (which probably doesn't help), but things do appear have changed a little between 2005 and 2008. I wonder if you're better off creating new projects and copying over the content rather than just doing an upgrade.

Specifically in the ActiveWriter code, it makes use of the IVsDataConnectionsService interface. I'm not sure of the history of this interface, but it does appear that it has disappeared from the 2008 SDK.

Curiously, this interface doesn't even appear documented in the 2005 SDK, and I'm not clear what the appropriate replacement should be.

RikMigrations (database migration)

Roy mentions RikMigrations. I too am quite ignorant about Ruby, though I am certainly aware of how popular it has become.

Something to keep an eye on which might be useful in the future.

Currently we use Red Gate's SQL Compare to do a diff between our test and production databases. SQL Compare then generates the necessary T-SQL to modify the production schema to synchronise it to the same as test.

This works very well for us, so I'm interested as to what benefits a tool like RikMigrations would bring.

Thursday, 22 November 2007

New phone

My new phone was delivered this afternoon.

It is a HTC TyTN II.

It came in a very cool box.

I've unpacked it, and it is now charging.

I'm being very patient.

Visual Studio 2008 RTM - First impressions

I haven't done heaps with 2008 since installing it yesterday.

I did investigate opening one of our existing projects to see if I could create a 2008 solution, while leaving the 2005 solution intact so that other people could still use 2005 to work on the code. I'd read that this is possible.

I struck a problem though - it looks like this strategy doesn't work for web applications projects.

The upgrade wizard modifies the path in the project file to where the web application project msbuild stuff gets included from. This would obviously not work for other team members who didn't also have 2008 installed on their machines.

So it looks like the best plan is to make sure everyone on the team starts using 2008 at the same time.

I also need to confirm that all our various addins and dependencies also continue to work - things like PageMethods and ActiveWriter are good examples.

It is nice to have the IDE start without requiring it to run in Admin mode on Vista though.

.NET Framework Service Packs

Coinciding with the release of Visual Studio 2008, there are new service packs out for .NET 2.0 and 3.0.

If you've installed .NET Framework 3.5, these service packs are already included.

Tuesday, 20 November 2007

Visual Studio 2008 and .NET 3.5

As predicted in the media, they have shipped.

I've managed to get online access to our MSDN Subscription, and am madly downloading bits right now.

Now we can start using all that nice LINQ stuff in VB, not to mention the XML support too - hooray.

Monday, 19 November 2007

Smartphone saga

Did I mention my Smartphone was broken? Oh, yes I did.

Well even though it was still in warranty when I sent it off, unfortunately Optus took a couple of weeks to decide that they don't fix that model anymore. I'm told that the company that does handle repairs wouldn't do it under warranty then...

So finally I rang out our IT department to find out what's going on, and they said that it is fixed. Great. Except that they said what fixed it was upgrading the firmware to the latest version. Hmm, hadn't I already done that a while ago? Skeptical, but not one to argue with the fact that the audio was actually working, I got my phone back last week.

And so it was actually working again..

Until today.

Yep, the same problem again. What I suspect probably happened is that because of the various trips the phone has made, both in our internal mail, and also interstate once (possibly twice), it unstuck the headphone socket. But now there's no audio again, so I'm pretty sure the cause is infact a physical problem, that the audio socket is stuck in the 'plug is still in, so don't allow sound through the external speaker' mode.

Looking ahead, wouldn't one of these be a nice replacement.

One interesting side-note. While my rw6828 was away on its little holiday, I was given a plain old Nokia phone (a 3530?). Nothing remarkable, except that the battery would last for days. We live in a pretty poor reception area, which seems to suck other phone batteries dry in a day, so I was surprised that the Nokia would go for 3-4 days without requiring a recharge.

Sunday, 18 November 2007

Adelaide Geek Dinner

Last night I was honoured to be one of the attendees at the inaugural Adelaide Geek Dinner. A most enjoyable evening was had by all. 

Thanks to Jason Stangroome for organising the evening. The good news is he's planning to do it again in January.

One non .NET thing I did learn - Darren Neimke plays cricket (a bit of a bowler by all accounts)

Friday, 9 November 2007

Using Enum types with NHibernate (implementing IUserType)

NHibernate is a really nice ORM framework, and one of its features is that you can customise the mapping between the business entity's properties and database columns.

For example, you might have a 'GENDER' column in a database table that has the values "M" or "F".

Rather than your entity class have a String or Char property, wouldn't it be nicer to use a custom Enum like this?

EntityObj.Gender = Sex.Male

In order to implement this functionality, there are a few steps that need to be completed.

StringValueAttribute

We need some way of recording the string equivalent for each enum value. One way to do this is to use a custom attribute like this:

Public NotInheritable Class StringValueAttribute
    Inherits System.Attribute
 
    Private _value As String
 
    Public Sub New(ByVal value As String)
        _value = value
    End Sub
 
    Public ReadOnly Property Value() As String
        Get
            Return _value
        End Get
    End Property
End Class

You apply this to each value of the enumerated type:

Public Enum Sex
    <StringValue("M")> Male
    <StringValue("F")> Female
End Enum

TypeConverters

Next, we need a nice way to read those values and convert between the enumerated type and the string value. A custom TypeConverter can do this.

Here's a generic TypeConverter class:

Imports System.ComponentModel
Imports System.Globalization
 
''' <summary>
''' A generic base class for implementing type converters for enumerations. 
''' </summary>
''' <typeparam name="t"></typeparam>
''' <remarks>The Enumerations must use the <see cref="StringValueAttribute">StringValueAttribute</see> attribute 
''' to map the string/character value with each enumerated value.</remarks>
Public Class EnumConverter(Of t As Structure)
    Inherits EnumConverter
 
    Public Sub New()
        MyBase.New(GetType(t))
    End Sub
 
    ''' <summary>
    ''' We can convert from String or Char
    ''' </summary>
    ''' <param name="context"></param>
    ''' <param name="sourceType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
        If sourceType Is GetType(String) OrElse sourceType Is GetType(Char) Then
            Return True
        Else
            Return False
        End If
        'Return MyBase.CanConvertFrom(context, sourceType)
    End Function
 
    ''' <summary>
    ''' Convert from String and Char
    ''' </summary>
    ''' <param name="context"></param>
    ''' <param name="culture"></param>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks>If it is a comma-separated list, then will combine values</remarks>
    Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        If TypeOf value Is String OrElse TypeOf value Is Char Then
 
            'Dim x As t
 
            Dim rv As Integer
 
            Dim strValue As String = CStr(value).ToUpper(CultureInfo.CurrentCulture)
 
            For Each ch As String In strValue.Split(","c)
                ch = ch.Trim()
 
                Dim FieldInfos() As System.Reflection.FieldInfo = GetType(t).GetFields(Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public) ' ft.GetType().GetField(value.ToString())
 
                For i As Integer = 0 To FieldInfos.Length - 1
                    Dim fi As Reflection.FieldInfo = FieldInfos(i)
 
                    Dim attributes() As StringValueAttribute = CType(fi.GetCustomAttributes(GetType(StringValueAttribute), False), StringValueAttribute())
 
                    If attributes.Length > 0 AndAlso attributes(0).Value = ch Then
                        Dim newValue As Integer = CInt(System.Enum.Parse(GetType(t), fi.Name))
 
                        rv = rv Or newValue
                    End If
 
                Next
 
            Next
 
            ' a bit messy, but we cast to an object then back to the t type
            Return CType(CType(rv, Object), t)
        Else
            Throw New NotSupportedException()
        End If
    End Function
 
    ''' <summary>
    ''' We can convert to String and Char
    ''' </summary>
    ''' <param name="context"></param>
    ''' <param name="destinationType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        If destinationType Is GetType(String) OrElse destinationType Is GetType(Char) Then
            Return True
        Else
            Throw New NotSupportedException()
        End If
 
    End Function
 
    ''' <summary>
    ''' Convert to String or Char
    ''' </summary>
    ''' <param name="context"></param>
    ''' <param name="culture"></param>
    ''' <param name="value"></param>
    ''' <param name="destinationType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
 
        If value Is Nothing Then
            Throw New ArgumentNullException("value")
        End If
 
        Dim ch As String = String.Empty
        Dim ft As t = CType(value, t)
 
        Dim fi As System.Reflection.FieldInfo = ft.GetType().GetField(value.ToString())
 
        Dim attributes() As StringValueAttribute = CType(fi.GetCustomAttributes(GetType(StringValueAttribute), False), StringValueAttribute())
 
        If attributes.Length > 0 Then
            If destinationType Is GetType(String) Then
                Return attributes(0).Value
            ElseIf destinationType Is GetType(Char) Then
                Return CChar(attributes(0).Value)
            Else
                'Return MyBase.ConvertTo(context, culture, value, destinationType)
                Throw New NotSupportedException()
            End If
 
        Else
            Throw New ArgumentException("StringValue attribute not found for this value")
 
        End If
 
    End Function
 
End Class

You apply this to the enumerated type:

Imports System.ComponentModel
 
<TypeConverter(GetType(EnumConverter(Of Sex)))> _
Public Enum Sex
    <StringValue("M")> Male
    <StringValue("F")> Female
End Enum

Implementing IUserType

You now need a class that implements IUserType. This class is used by NHibernate to do the mapping/conversion to and from the database to the property on the class.

Here's a generic version, that you can then inherit from for your specific type:

Imports NHibernate
Imports NHibernate.UserTypes
 
''' <summary>
''' Generic class for implementing NHibernate's <see cref="IUserType">IUserType</see> for a value type. 
''' </summary>
''' <typeparam name="T">Value type</typeparam>
''' <remarks>The value type's <see cref="System.ComponentModel.TypeConverter">TypeConverter</see> is used for conversion</remarks>
Public MustInherit Class ValueConverterUserType(Of T As Structure)
    Implements IUserType
 
    Protected Converter As ComponentModel.TypeConverter = ComponentModel.TypeDescriptor.GetConverter(GetType(T))
 
    ''' <summary>
    ''' Reconstruct an object from the cacheable representation. 
    ''' </summary>
    ''' <param name="cached"></param>
    ''' <param name="owner"></param>
    ''' <returns></returns>
    ''' <remarks>At the very least this method should perform a deep copy if the type is mutable. (optional operation)</remarks>
    Public Function Assemble(ByVal cached As Object, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.Assemble
        Return DeepCopy(cached)
    End Function
 
    ''' <summary>
    ''' Return a deep copy of the persistent state, stopping at entities and at collections.
    ''' </summary>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function DeepCopy(ByVal value As Object) As Object Implements NHibernate.UserTypes.IUserType.DeepCopy
        Return value
    End Function
 
    ''' <summary>
    ''' Transform the object into its cacheable representation. 
    ''' </summary>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks>At the very least this method should perform a deep copy if the type is mutable. 
    ''' That may not be enough for some implementations, however; for example, associations must be cached as identifier values. (optional operation)</remarks>
    Public Function Disassemble(ByVal value As Object) As Object Implements NHibernate.UserTypes.IUserType.Disassemble
        Return DeepCopy(value)
 
    End Function
 
    ''' <summary>
    ''' Compare two instances of the class mapped by this type for persistent "equality" ie. equality of persistent state
    ''' </summary>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shadows Function Equals(ByVal x As Object, ByVal y As Object) As Boolean Implements NHibernate.UserTypes.IUserType.Equals
        Return x.Equals(y)
    End Function
 
    ''' <summary>
    ''' Get a hashcode for the instance, consistent with persistence "equality"
    ''' </summary>
    ''' <param name="x"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shadows Function GetHashCode(ByVal x As Object) As Integer Implements NHibernate.UserTypes.IUserType.GetHashCode
        Return x.GetHashCode()
    End Function
 
    ''' <summary>
    ''' Are objects of this type mutable?
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsMutable() As Boolean Implements NHibernate.UserTypes.IUserType.IsMutable
        Get
            Return False
        End Get
    End Property
 
    ''' <summary>
    ''' Retrieve an instance of the mapped class from an <see cref="IDataReader">IDataReader</see>.
    ''' </summary>
    ''' <param name="rs">IDataReader</param>
    ''' <param name="names">Column names</param>
    ''' <param name="owner">The containing entity</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function NullSafeGet(ByVal rs As System.Data.IDataReader, ByVal names() As String, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.NullSafeGet
 
        Dim obj As Object = NHibernateUtil.String.NullSafeGet(rs, names)
 
        Dim s As String = CStr(obj)
 
        Return Converter.ConvertFromString(s)
 
    End Function
 
    ''' <summary>
    ''' Write an instance of the mapped class to a prepared statement.
    ''' </summary>
    ''' <param name="cmd">an IDbCommand</param>
    ''' <param name="value">the object to write</param>
    ''' <param name="index">command parameter index</param>
    ''' <remarks>Implementors should handle possibility of null values.  A multi-column type should be written to parameters starting from index.</remarks>
    Public Sub NullSafeSet(ByVal cmd As System.Data.IDbCommand, ByVal value As Object, ByVal index As Integer) Implements NHibernate.UserTypes.IUserType.NullSafeSet
 
        Dim NativeValue As String = Converter.ConvertToString(value)
 
        NHibernateUtil.String.NullSafeSet(cmd, NativeValue, index)
 
    End Sub
 
    ''' <summary>
    ''' During merge, replace the existing (target) value in the entity we are merging to with a new (original) value from the detached entity we are merging. 
    ''' </summary>
    ''' <param name="original">the value from the detached entity being merged</param>
    ''' <param name="target">the value in the managed entity</param>
    ''' <param name="owner">the managed entity</param>
    ''' <returns>the value to be merged</returns>
    ''' <remarks>For immutable objects, or null values, it is safe to simply return the first parameter. 
    ''' For mutable objects, it is safe to return a copy of the first parameter. 
    ''' For objects with component values, it might make sense to recursively replace component values.</remarks>
    Public Function Replace(ByVal original As Object, ByVal target As Object, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.Replace
        Return original
 
    End Function
 
    ''' <summary>
    ''' The type returned by NullSafeGet()
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ReturnedType() As System.Type Implements NHibernate.UserTypes.IUserType.ReturnedType
        Get
            Return GetType(T)
        End Get
    End Property
 
    ''' <summary>
    ''' The SQL types for the columns mapped by this type.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public MustOverride ReadOnly Property SqlTypes() As NHibernate.SqlTypes.SqlType() Implements NHibernate.UserTypes.IUserType.SqlTypes
 
End Class

Note that you'll need to override the SqlTypes() property. This tells NHibernate the type of the database column. So in the following example, as the table column is probably a char(1), we use GetString(1).

Imports NHibernate.SqlTypes
 
Public Class SexTypeUserType
    Inherits ValueConverterUserType(Of Sex)
 
    Public Overrides ReadOnly Property SqlTypes() As NHibernate.SqlTypes.SqlType()
        Get
            Dim a() As SqlType = {SqlTypeFactory.GetString(1)}
 
            Return a
        End Get
    End Property
End Class

Mapping file

Now in the hbm.xml mapping file, you tell NHibernate to use your IUserType class:

    <property name="Gender" access="property" type="MyNameSpace.SexTypeUserType,MyAssemblyName">
      <column name="PERSON_GENDER" not-null="true" sql-type="char(1)" />
    </property>

ActiveWriter users.

If you use ActiveWriter to generate your classes and hbm.xml mapping files, please be aware that it is currently unable to resolve your custom user type. Hopefully this will be resolved in future releases.

The workaround for now is to generate with ActiveWriter, then hand-edit the appropriate hbm and code files. Just remember if you regenerate, you'll have to reapply your edits!

Thursday, 8 November 2007

Windows Live

There's a new version of Windows Live Messenger - v8.5.1302.1018 and the RTM of Windows Live Writer.

Get them both from the one installer.

I'm using Live Writer to create most of my blog posts now - it's a really nice editor, and you can tell it to generate XHTML (go to your weblog settings, look in the Advanced section and click XHTML).

Monday, 5 November 2007

Microsoft Visual Basic Compiler has stopped working

It would be me that happens to make the Visual Basic compiler crash!

As I've mentioned in the Rhino Mocks group, the crash appears to be caused by an interesting combination of a generic interface, another interface that inherits from the generic one, and a mock object that implements that interface. When you then try to use the new void method handling code (a new feature of Rhino Mocks 3.3) the compiler barfs (that's a compiler technical term).

I've attached a simple project to the MS bug report that reproduces the crash. Please contact me if you are interested in the code.

The workaround is to revert to the pre-3.3 way of handing void method expectations:

eg. instead of this:

Using Mocks.Record()
 
 Expect.Call(MockedObject.Subroutine)
 
End Using

Do this:

Using Mocks.Record()
 
 MockedObject.Subroutine
 
End Using

Fix it Pat (part 2)

Photo of David from Sunday Mail As we arrived at Church on Sunday, I was greeted by a number of people saying, "We saw you in the paper today!"

So while I didn't get a mention from Thursday's interview on the day, apparently my mug is in the paper edition, along with along with some of my comments (down under the Belair sub-heading).

To be fair to the Sunday Mail, they are being pretty open about the fact that they were the ones handing out the badges, and I think I've been quoted accurately, which is reassuring.

(Photo from Sunday Mail, 4th November 2007)

Friday, 2 November 2007

Fix it Pat

Today at Blackwood Train station, I was interviewed by a Sunday Mail journalist.

Apparently they are running a campaign to encourage more investment in public transport, particularly after the problems the trains had yesterday with the computer system failing completely.

I didn't make the copy in the final article - oh well.

Owen Vinall, 56, of Coromandel Valley and Susan Lewis, 49, of Blackwood ride the Belair train this morning. Picture: JENNIE GROOM

Looks like I will need to get Owen's autograph after all (he's the guy on the left). I was sitting just out of shot behind the guy standing up near the door on the right :-)