首页 > 解决方案 > Servicestack ORMLite - 在 PostgreSQL 中使用 XML 字段

问题描述

我有一个 Web 应用程序正在扩展以包含 PostgreSQL 作为数据库选项。对于现有的 MSSQL 实现,我们使用 XML 列将临时对象保存为 POCO 类的一部分。

public class POCO
{
    [PrimaryKey]
    public int POCOId { get; set; }

    public string StringValue { get; set; }

    public string XMLValue{ get; set; }
}

在 MSSQL 中,我们可以插入如下值,其中 GenerateXML 生成转换为字符串的有效 xml:

var poco= new POCO
        {
            StringValue ="My string here!"
            XMLValue= GenerateXML().ToString()
        };

        using (var context= new OrmContext())
            context.Connection.Insert(poco);

MSSQL 接受 XML 字符串并将其正确插入数据库。但是 postgres 要求将 xml 显式插入为 xml:

42804:列“xmlvalue”的类型为 xml,但表达式的类型为文本

因此,我需要将该字符串转换为 xml 类型,而不是在 poco 模型中使用字符串并作为字符串插入。

有没有办法通过使用属性标记字段或将其标记为 xml 来覆盖 postgres 的标准插入语句?

我的另一个选择是简单地将列更改为文本,但这确实使查询列几乎不可能。

标签: postgresqlormlite-servicestack

解决方案


您可以使用OrmLite 类型转换器自定义如何处理不同的 .NET 类型,但是您将无法自定义string,因为这会影响所有string字段。

相反,我刚刚在此提交中添加了对 PostgreSQL 中 Xml 的支持,方法是使用XmlValue仅包装 XML 字符串的包装器类型:

public struct XmlValue
{
    public string Xml { get; }
    public XmlValue(string xml) => Xml = xml;
    public override string ToString() => Xml;

    public bool Equals(XmlValue other) => Xml == other.Xml;

    public override bool Equals(object obj) => obj is XmlValue other && Equals(other);

    public override int GetHashCode() => Xml != null ? Xml.GetHashCode() : 0;

    public static implicit operator XmlValue(string expandedName) => 
        expandedName != null ? new XmlValue(expandedName) : null;
}

所以我们可以自定义它的处理方式PostgreSqlXmlConverter

public class PostgreSqlXmlConverter : PostgreSqlStringConverter
{
    public override string ColumnDefinition => "XML";
    public override void InitDbParam(IDbDataParameter p, Type fieldType) => p.DbType = DbType.Xml;
    public override object ToDbValue(Type fieldType, object value) => value?.ToString();
    public override string ToQuotedString(Type fieldType, object value) => 
        base.ToQuotedString(fieldType, value.ToString());
}

您可以通过以下方式注册PostgreSqlDialect.Provider

PostgreSqlDialect.Provider.RegisterConverter<XmlValue>(new PostgreSqlXmlConverter());

您现在可以使用XmlValue数据类型创建表和插入行,例如:

public class XmlTest
{
    public int Id { get; set; }
    public XmlValue Xml { get; set; } 
}

您可以使用 PostgreSQL XML 函数创建、插入和查询:

using (var db = dbFactory.OpenDbConnection())
{
    db.DropAndCreateTable<XmlTest>();

    db.Insert(new XmlTest { Id = 1, Xml = @"<attendee>
<bio>
    <name>John Doe</name>
    <birthYear>1986</birthYear>
</bio>
<languages>
    <lang level=""5"">php</lang>
    <lang level=""4"">python</lang>
    <lang level=""2"">java</lang>
</languages>
</attendee>" });

        db.Insert(new XmlTest { Id = 2, Xml = @"<attendee>
<bio>
    <name>Tom Smith</name>
    <birthYear>1978</birthYear>
</bio>
<languages>
    <lang level=""5"">python</lang>
    <lang level=""3"">java</lang>
    <lang level=""1"">ruby</lang>
</languages>
</attendee>" });

    var results = db.Column<string>(@"SELECT
        (xpath('//bio/name/text()', Xml)::text[])[1]
        FROM xml_test 
        WHERE cast(xpath('//bio[birthYear>1980]', Xml) as text[]) != '{}'");
    results[0] //= John Doe
}

请注意,您的 PostgreSQL 安装需要配置 --with-libxml 以使用 XML 数据类型

XmlValue从 v5.7.1+ 开始提供此支持更改,现在可在 MyGet 上使用


推荐阅读