I have a table where the primary key is a string (yuck, I know -- I can't change it).
Currently it is managed mostly by custom scripts, so this key gets polluted all over the databse, sometimes in differing case. So you'll have "domain\joe.user" in the User table, and "DOMAIN\Joe.User" as a foreign key reference in another table. Using joins this works fine because SQL is case sensitive.
The problem using NHibernate to manage this is that it compares strings to match things up.
So I have a schema like this:
Code:
USER
login name
-----------------------------
dummy Dummy User
APPLICATIONS
applicationid name
---------------------------------------------
app1 Application 1
app2 Application 2
USER_APPS
login applicationid
---------------------------------------------
dummy app1
DuMmY app2
This is easy to understand; a Many-to-Many between Users and Applications. But when I load this User in NHibernate, User.Applications.Count returns 1... NOT 2
So I read these forums and there is an old thread which talks about this. Sergey suggested using a Custom Mapping Type. So I created a CaseInsensitiveStringType that implements the Equals method by first comparing the .ToLower() of the 2 strings.
Here's that class:
Code:
Public Class CaseInsensitiveStringType
Implements IUserType
Private SQL_TYPES() As NHibernate.SqlTypes.SqlType = {New NHibernate.SqlTypes.StringSqlType()}
Public Function DeepCopy(ByVal value As Object) As Object Implements NHibernate.IUserType.DeepCopy
Return value
End Function
Public Function Equals1(ByVal x As Object, ByVal y As Object) As Boolean Implements NHibernate.IUserType.Equals
'are they both strings?
If Not (TypeOf (x) Is String AndAlso TypeOf (y) Is String) Then
Return False
End If
'are they equal (case insensitive)
Return x.ToString().ToLower().Equals(y.ToString.ToLower())
End Function
Public ReadOnly Property IsMutable() As Boolean Implements NHibernate.IUserType.IsMutable
Get
Return False
End Get
End Property
Public Function NullSafeGet(ByVal rs As System.Data.IDataReader, ByVal names() As String, ByVal owner As Object) As Object Implements NHibernate.IUserType.NullSafeGet
'get the value, if it is null, set the string to be emtpy
Dim idx As Int16 = rs.GetOrdinal(names(0))
Dim val As String
If rs.IsDBNull(idx) Then
val = String.Empty
Else
val = rs.GetString(idx)
End If
Return val
End Function
Public Sub NullSafeSet(ByVal cmd As System.Data.IDbCommand, ByVal value As Object, ByVal index As Integer) Implements NHibernate.IUserType.NullSafeSet
'we don't care about nulls here
Dim param As IDataParameter = cmd.Parameters(index)
param.Value = value
End Sub
Public ReadOnly Property ReturnedType() As System.Type Implements NHibernate.IUserType.ReturnedType
Get
Return GetType(String)
End Get
End Property
Public ReadOnly Property SqlTypes() As NHibernate.SqlTypes.SqlType() Implements NHibernate.IUserType.SqlTypes
Get
Return SQL_TYPES
End Get
End Property
End Class
I change my mapping files like this:
User.hbm.xmlCode:
<class name="User" table="[User]">
<id name="LoginId" column="LoginId" type="MyProject.CaseInsensitiveStringType, MyProject" length="50" access="nosetter.camelcase-underscore">
<generator class="assigned" />
</id>
<!-- main properties -->
<property name="FirstName" column="firstName" type="String" length="50" />
<property name="LastName" column="lastName" type="String" length="50" />
<set name="applications" lazy="true" access="NHibernate.Generics.GenericAccessor, NHibernate.Generics" table="AppUserXRef">
<key column="LoginId" />
<many-to-many class="Application" column="ApplicationId" />
</set>
</class>
and
Application.hbm.xmlCode:
<class name="Application" table="Application" proxy="Application" lazy="true">
<id name="ApplicationId" type="MyProject.CaseInsensitiveStringType, MyProject" length="50" column="ApplicationId" access="nosetter.camelcase-underscore">
<column name="ApplicationId" />
<generator class="assigned" />
</id>
<property name="Name" type="String" length="100" not-null="true" column="Description" />
<property name="Url" type="String" length="100" not-null="false" column="Url" />
</class>
I'm still getting the same results. Am I approaching my custom type the right way?