Search code examples
xmlnhibernateexpando

NHibernate XML Document in Database to Expando Object in entity


I have a SQL Server database that contains an xml column. I need to map that xml column to an expando object within my domain entity. I am using NHibernate. How do I extend NHibernate to accommodate this? I am assuming (I am new to NHibernate) that I have to override the implementation to get and set the xml data, but I don't know how to do that in NHibernate.


Solution

  • Thanks to Petr's answer, I have come up with the following initial attempt at a user type to handle the expando. It works quite nicely and I can now offer client set properties for each of my objects. I set the properties that each object must have, and each client can then add their own properties to meet their needs.

    One caveat - I am only setting this up for persistence purposes. Searching is not necessary in this app as all querying is done against a MongoDB database that has denormalized copies of the data.

    public class ExpandoUserType : IUserType
    {
        public object Assemble(object cached, object owner)
        {
            return cached;
        }
    
        public object DeepCopy(object value)
        {
            return value;
        }
    
        public object Disassemble(object value)
        {
            return value;
        }
    
        public bool Equals(object x, object y)
        {
            return false;
        }
    
        public int GetHashCode(object x)
        {
            return 0;
        }
    
        public bool IsMutable
        {
            get { return false; }
        }
    
        public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
        {
            var obj = NHibernateUtil.XmlDoc.NullSafeGet(rs, names[0]);
    
            if (obj == null) return null;
    
            var xmldoc = (XmlDocument)obj;
    
            dynamic expando = new ExpandoObject();
    
            foreach (XmlElement el in xmldoc.FirstChild.ChildNodes)
            {
                object val = null;
    
                switch (Convert.ToString(el.Attributes["type"].InnerText).ToLower())
                {
                    case "string":
                        val = el.InnerText;
                        break;
    
                    case "int32":
                        val = Convert.ToInt32(el.InnerText);
                        break;
    
                    case "int16":
                        val = Convert.ToInt16(el.InnerText);
                        break;
    
                    case "int64":
                        val = Convert.ToInt64(el.InnerText);
                        break;
    
                    case "bool":
                        val = Convert.ToBoolean(el.InnerText);
                        break;
    
                    case "datetime":
                        val = Convert.ToDateTime(el.InnerText);
                        break;
    
                    case "byte":
                        val = Convert.ToByte(el.InnerText);
                        break;
    
                    case "decimal":
                        val = Convert.ToDecimal(el.InnerText);
                        break;
                }
                ((IDictionary<String, Object>)expando).Add(el.Name, val);
            }
    
            return expando;
    
        }
    
        /// <summary>
        /// Transforms the expando object to an XML Document for storage in SQL.
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="value"></param>
        /// <param name="index"></param>
        public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
        {
            if (value == null || value == DBNull.Value)
            {
                NHibernateUtil.String.NullSafeSet(cmd, null, index);
            }
            else
            {
                NHibernateUtil.XmlDoc.Set(cmd, expandoToXML((ExpandoObject) value, "root"), index);
            }
        }
    
        public object Replace(object original, object target, object owner)
        {
            return original;
        }
    
        public Type ReturnedType
        {
            get { return typeof(ExpandoObject); }
        }
    
        public NHibernate.SqlTypes.SqlType[] SqlTypes
        {
            get { return new[] { NHibernateUtil.XmlDoc.SqlType };  }
        }
    
    
        private static XmlDocument expandoToXML(dynamic node, String nodeName)
        {
            XElement xmlNode = new XElement(nodeName);
    
            foreach (var property in (IDictionary<String, Object>)node)
            {
    
                if (property.Value.GetType() == typeof(ExpandoObject))
                    xmlNode.Add(expandoToXML(property.Value, property.Key));
    
                else
                    if (property.Value.GetType() == typeof(List<dynamic>))
                        foreach (var element in (List<dynamic>)property.Value)
                            xmlNode.Add(expandoToXML(element, property.Key));
                    else
                    {
                        XElement xnode = new XElement(property.Key, property.Value);
                        xnode.SetAttributeValue("type", property.Value.GetType().Name);
                        xmlNode.Add(xnode);
                    }
            }
    
            return xmlNode.GetXmlNode();
        }
    }