by Mikael Henriksson
13. August 2009 20:39
First of all I just want to state that I have no idea what I am doing but by the looks of things it is working… :) I found a ICustomType that I was almost happy with except that it stored the XmlDocument as varchar(4000). I’ll add that code at the end because it is 100 something lines. Let’s start with how difficult this was… NOT but maybe I just got lucky. Just using my XmlType class it didn’t work. Complained about System.Xml.XmlDocument could not be found when I tried mapping it so I had a look in the NHibernate source code and quickly I was able to find MsSql2005Dialect.cs and SqlTypeFactory.cs and two spots where I could potentially add code to make it work. I started with the dialect and since 2008 dialect inherits from 2005 it also inherits the xml type:
RegisterColumnType(DbType.Xml, "XML");
And then of course in SqlTypeFactory:
public static readonly SqlType Xml = new SqlType(DbType.Xml);
Now all that should be needed is to add that the column of your choice is of a custom type:
Map(x => x.ContactData, "contact_data").CustomType(typeof(XmlType));
Then a neat little class that inherits from SqlType. The last two classes could reside in
public class SqlXmlType : SqlType
{
public SqlXmlType()
: base(DbType.Xml)
{
}
}
And lastly the class that does the work:
using System;
using System.Data;
using System.Data.Common;
using System.Xml;
using global::NHibernate.SqlTypes;
using global::NHibernate.UserTypes;
public class XmlType : IUserType
{
public new bool Equals(object x, object y)
{
if (x == null || y == null)
return false;
var xdoc_x = (XmlDocument)x;
var xdoc_y = (XmlDocument)y;
return xdoc_y.OuterXml == xdoc_x.OuterXml;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (names.Length != 1)
throw new InvalidOperationException("names array has more than one element. can't handle this!");
var document = new XmlDocument();
var val = rs[names[0]] as string;
if (val != null)
{
document.LoadXml(val);
return document;
}
return null;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var parameter = (DbParameter)cmd.Parameters[index];
if (value == null)
{
parameter.Value = DBNull.Value;
return;
}
parameter.Value = ((XmlDocument)value).OuterXml;
}
public object DeepCopy(object value)
{
var toCopy = value as XmlDocument;
if (toCopy == null)
return null;
var copy = new XmlDocument();
copy.LoadXml(toCopy.OuterXml);
return copy;
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public object Assemble(object cached, object owner)
{
var str = cached as string;
if (str != null)
{
var doc = new XmlDocument();
doc.LoadXml(str);
return doc;
}
else
{
return null;
}
}
public object Disassemble(object value)
{
var val = value as XmlDocument;
if (val != null)
{
return val.OuterXml;
}
else
{
return null;
}
}
public SqlType[] SqlTypes
{
get
{
return new SqlType[] { new SqlXmlType() };
}
}
public Type ReturnedType
{
get { return typeof(XmlDocument); }
}
public bool IsMutable
{
get { return true; }
}
}
I created a patch for this in jira and I found the original file here.