如何使用 ServiceStack 将 POCO 存储到具有复杂类型(对象和结构)的 MariaDB,如 JSON?

How to use ServiceStack to store POCOs to MariaDB having complex types (objects and structs) blobbed as JSON?

我有以下设置:C#、ServiceStack、MariaDB、带有对象和结构的 POCO,JSON。

主要问题是:如何使用 ServiceStack 将 POCO 存储到具有复杂类型(对象和结构)的 MariaDB,如 JSON 并且仍然有效 de/serialization 相同的 POCO?所有这些单个任务都受支持,但主要是因为结构,当所有这些任务放在一起时我遇到了问题。

...终于在写这篇文章的过程中我找到了一些解决方案,看起来像是我回答了我自己的问题,但我仍然想知道更多技术人员的答案,因为我找到的解决方案有点复杂,我想。细节和两个子问题稍后出现在上下文中。

对于篇幅以及由于我知识有限可能造成的错误信息,我们深表歉意。

简单的例子。这是我完成的最后一个工作。一开始没有SomeStruct.ToString()/Parse()方法,也没有JsConfig设置。

using Newtonsoft.Json;
using ServiceStack;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
using ServiceStack.Text;
using System.Diagnostics;

namespace Test
{
    public class MainObject
    {
        public int Id { get; set; }
        public string StringProp { get; set; }
        public SomeObject ObjectProp { get; set; }
        public SomeStruct StructProp { get; set; }
    }

    public class SomeObject
    {
        public string StringProp { get; set; }
    }

    public struct SomeStruct
    {
        public string StringProp { get; set; }

        public override string ToString()
        {
            // Unable to use .ToJson() here (ServiceStack does not serialize structs).
            // Unable to use ServiceStack's JSON.stringify here because it just takes ToString() => stack overflow.
            // => Therefore Newtonsoft.Json used.
            var serializedStruct = JsonConvert.SerializeObject(this);
            return serializedStruct;
        }

        public static SomeStruct Parse(string json)
        {
            // This method behaves differently for just deserialization or when part of Save().
            // Details in the text.
            // After playing with different options of altering the json input I ended with just taking what comes.
            // After all it is not necessary, but maybe useful in other situations.
            var structItem = JsonConvert.DeserializeObject<SomeStruct>(json);
            return structItem;
        }
    }

    internal class ServiceStackMariaDbStructTest
    {
        private readonly MainObject _mainObject = new MainObject
        {
            ObjectProp = new SomeObject { StringProp = "SomeObject's String" },
            StringProp = "MainObject's String",
            StructProp = new SomeStruct { StringProp = "SomeStruct's String" }
        };

        public ServiceStackMariaDbStructTest()
        {
            // This one line is needed to store complex types as blobbed JSON in MariaDB.
            MySqlDialect.Provider.StringSerializer = new JsonStringSerializer();

            JsConfig<SomeStruct>.RawSerializeFn = someStruct => JsonConvert.SerializeObject(someStruct);
            JsConfig<SomeStruct>.RawDeserializeFn = json => JsonConvert.DeserializeObject<SomeStruct>(json);
        }

        public void Test_Serialization()
        {
            try
            {
                var json = _mainObject.ToJson();
                if (!string.IsNullOrEmpty(json))
                {
                    var objBack = json.FromJson<MainObject>();
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        public void Test_Save()
        {
            var cs = "ConnectionStringToMariaDB";
            var dbf = new OrmLiteConnectionFactory(cs, MySqlDialect.Provider);
            using var db = dbf.OpenDbConnection();
            db.DropAndCreateTable<MainObject>();

            try
            {
                db.Save(_mainObject);
                var dbObject = db.SingleById<MainObject>(_mainObject.Id);
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
    }
}

什么(我认为)我知道/已经尝试过但一开始并没有帮助我自己解决它:

有没有更简单的解决办法?如果有人指出我更好的方向,我会很高兴。

可以接受的可能是两个(由于不同的情况,它们都可以访问):

最好的方法是不使用其他依赖项(例如 Newtonsoft.Json)来序列化结构。也许一些 JsConfig.ShallProcessStructs = true;警告:只是提示,从 2021-04-02 开始不起作用)在这种情况下会很好。

ServiceStack 将结构视为 单个标量值 类型,就像大多数核心 BCL 值类型(例如 TimeSpanDateTime 等) .重载 Parse()ToString() 方法以及 Struct 的 Constructor 让您可以控制自定义结构的 serialization/deserialization。

Docs have been corrected. Structs use Parse whilst classes use ParseJson/ParseJsv

如果您想序列化模型属性,我建议您使用 class,因为您正在寻找的行为是 POCO DTO 的行为。

如果您想在 RDBMS 中将结构序列化为 DTO,您可以尝试的替代方法是仅使用 JSON.NET 进行复杂类型序列化,例如:

public class JsonNetStringSerializer : IStringSerializer
{
    public To DeserializeFromString<To>(string serializedText) => 
        JsonConvert.DeserializeObject<To>(serializedText);

    public object DeserializeFromString(string serializedText, Type type) =>
        JsonConvert.DeserializeObject(serializedText, type);

    public string SerializeToString<TFrom>(TFrom from) => 
        JsonConvert.SerializeObject(from);
}

MySqlDialect.Provider.StringSerializer = new JsonNetStringSerializer();