如何使用NHibernate将XML类型列映射到强类型对象属性?

时间:2022-10-24 21:25:08

I have the following table:

我有下表:

CREATE TABLE [dbo].[Data] (
    [Id]            UNIQUEIDENTIFIER NOT NULL,
    [Data]   XML              NOT NULL,
);

I need to map it to the object:

我需要将它映射到对象:

class Data
{
    public virtual Guid Id {get; set;}
    public virtual StronglyTypedData Data {get; set;}
}

Where, StronglyTypedData is something like:

其中,StronglyTypedData类似于:

class StronglyTypedData
{
    public string Name {get; set;}
    public int Number {get; set;}
}

By default, XML columns are mapped to XmlDocument properties, but I would like XML serialization/deserialization to StronglyTypedData property to happen instead at mapping time.

默认情况下,XML列映射到XmlDocument属性,但我希望在映射时将XML序列化/反序列化为StronglyTypedData属性。

What do I need to do to accomplish this?

我需要做些什么才能做到这一点?

2 个解决方案

#1


4  

You need to write an IUserType that takes care of the conversion.

您需要编写负责转换的IUserType。

You could start from XmlDocType, which is the one actually converting from raw XML to a XmlDocument.

您可以从XmlDocType开始,这是实际从原始XML转换为XmlDocument的XmlDocType。

#2


8  

I was going to make this a comment on Diego's post, but it was too long and I wanted syntax highlighting. I modified the XmlDocType that Diego posted so that it would use xml serialization to and from a strongly typed object.

我打算对Diego的帖子做一个评论,但它太长了,我想要语法高亮。我修改了Diego发布的XmlDocType,以便它可以在强类型对象中使用xml序列化。

I made my own generic IUserType to handle the strong typing:

我制作了自己的通用IUserType来处理强类型:

//you'll need these at the top of your file
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using NHibernate.UserTypes;
//using NHibernate.SqlTypes;
//using System.Data;
//using System.Xml;
//using NHibernate.Type;

[Serializable]
public class XmlType<T> : MutableType
{
    public XmlType()
        : base(new XmlSqlType())
    {
    }


    public XmlType(SqlType sqlType)
        : base(sqlType)
    {
    }

    public override string Name
    {
        get { return "XmlOfT"; }
    }

    public override System.Type ReturnedClass
    {
        get { return typeof(T); }
    }

    public override void Set(IDbCommand cmd, object value, int index)
    {
        ((IDataParameter)cmd.Parameters[index]).Value = XmlUtil.ConvertToXml(value);
    }

    public override object Get(IDataReader rs, int index)
    {
        // according to documentation, GetValue should return a string, at list for MsSQL
        // hopefully all DataProvider has the same behaviour
        string xmlString = Convert.ToString(rs.GetValue(index));
        return FromStringValue(xmlString);
    }

    public override object Get(IDataReader rs, string name)
    {
        return Get(rs, rs.GetOrdinal(name));
    }

    public override string ToString(object val)
    {
        return val == null ? null : XmlUtil.ConvertToXml(val);
    }

    public override object FromStringValue(string xml)
    {
        if (xml != null)
        {
            return XmlUtil.FromXml<T>(xml);
        }
        return null;
    }

    public override object DeepCopyNotNull(object value)
    {
        var original = (T)value;
        var copy = XmlUtil.FromXml<T>(XmlUtil.ConvertToXml(original));
        return copy;
    }

    public override bool IsEqual(object x, object y)
    {
        if (x == null && y == null)
        {
            return true;
        }
        if (x == null || y == null)
        {
            return false;
        }
        return XmlUtil.ConvertToXml(x) == XmlUtil.ConvertToXml(y);
    }
}

//the methods from this class are also available at: http://blog.nitriq.com/PutDownTheXmlNodeAndStepAwayFromTheStringBuilder.aspx
public static class XmlUtil
{
    public static string ConvertToXml(object item)
    {
        XmlSerializer xmlser = new XmlSerializer(item.GetType());
        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            xmlser.Serialize(ms, item);
            UTF8Encoding textconverter = new UTF8Encoding();
            return textconverter.GetString(ms.ToArray());
        }
    }

    public static T FromXml<T>(string xml)
    {
        XmlSerializer xmlser = new XmlSerializer(typeof(T));
        using (System.IO.StringReader sr = new System.IO.StringReader(xml))
        {
            return (T)xmlser.Deserialize(sr);
        }
    }

}

Then, finally, you can use Fluent.NHibernate to map your column like this:

然后,最后,您可以使用Fluent.NHibernate映射列,如下所示:

public partial class MyTableEntityMap: ClassMap<MyTableEntity>
{
    public MyTableEntityMap()
    {
        Table("MyTable");
         //...

        Map(x => x.MyStronglyTypedProperty).Column("SomeXmlTypeSqlColumn").CustomType(typeof(XmlType<TypeOfMyProperty>));
    }
}

#1


4  

You need to write an IUserType that takes care of the conversion.

您需要编写负责转换的IUserType。

You could start from XmlDocType, which is the one actually converting from raw XML to a XmlDocument.

您可以从XmlDocType开始,这是实际从原始XML转换为XmlDocument的XmlDocType。

#2


8  

I was going to make this a comment on Diego's post, but it was too long and I wanted syntax highlighting. I modified the XmlDocType that Diego posted so that it would use xml serialization to and from a strongly typed object.

我打算对Diego的帖子做一个评论,但它太长了,我想要语法高亮。我修改了Diego发布的XmlDocType,以便它可以在强类型对象中使用xml序列化。

I made my own generic IUserType to handle the strong typing:

我制作了自己的通用IUserType来处理强类型:

//you'll need these at the top of your file
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using NHibernate.UserTypes;
//using NHibernate.SqlTypes;
//using System.Data;
//using System.Xml;
//using NHibernate.Type;

[Serializable]
public class XmlType<T> : MutableType
{
    public XmlType()
        : base(new XmlSqlType())
    {
    }


    public XmlType(SqlType sqlType)
        : base(sqlType)
    {
    }

    public override string Name
    {
        get { return "XmlOfT"; }
    }

    public override System.Type ReturnedClass
    {
        get { return typeof(T); }
    }

    public override void Set(IDbCommand cmd, object value, int index)
    {
        ((IDataParameter)cmd.Parameters[index]).Value = XmlUtil.ConvertToXml(value);
    }

    public override object Get(IDataReader rs, int index)
    {
        // according to documentation, GetValue should return a string, at list for MsSQL
        // hopefully all DataProvider has the same behaviour
        string xmlString = Convert.ToString(rs.GetValue(index));
        return FromStringValue(xmlString);
    }

    public override object Get(IDataReader rs, string name)
    {
        return Get(rs, rs.GetOrdinal(name));
    }

    public override string ToString(object val)
    {
        return val == null ? null : XmlUtil.ConvertToXml(val);
    }

    public override object FromStringValue(string xml)
    {
        if (xml != null)
        {
            return XmlUtil.FromXml<T>(xml);
        }
        return null;
    }

    public override object DeepCopyNotNull(object value)
    {
        var original = (T)value;
        var copy = XmlUtil.FromXml<T>(XmlUtil.ConvertToXml(original));
        return copy;
    }

    public override bool IsEqual(object x, object y)
    {
        if (x == null && y == null)
        {
            return true;
        }
        if (x == null || y == null)
        {
            return false;
        }
        return XmlUtil.ConvertToXml(x) == XmlUtil.ConvertToXml(y);
    }
}

//the methods from this class are also available at: http://blog.nitriq.com/PutDownTheXmlNodeAndStepAwayFromTheStringBuilder.aspx
public static class XmlUtil
{
    public static string ConvertToXml(object item)
    {
        XmlSerializer xmlser = new XmlSerializer(item.GetType());
        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            xmlser.Serialize(ms, item);
            UTF8Encoding textconverter = new UTF8Encoding();
            return textconverter.GetString(ms.ToArray());
        }
    }

    public static T FromXml<T>(string xml)
    {
        XmlSerializer xmlser = new XmlSerializer(typeof(T));
        using (System.IO.StringReader sr = new System.IO.StringReader(xml))
        {
            return (T)xmlser.Deserialize(sr);
        }
    }

}

Then, finally, you can use Fluent.NHibernate to map your column like this:

然后,最后,您可以使用Fluent.NHibernate映射列,如下所示:

public partial class MyTableEntityMap: ClassMap<MyTableEntity>
{
    public MyTableEntityMap()
    {
        Table("MyTable");
         //...

        Map(x => x.MyStronglyTypedProperty).Column("SomeXmlTypeSqlColumn").CustomType(typeof(XmlType<TypeOfMyProperty>));
    }
}