AqlaSolutions / AqlaSerializer

Binary serializer with full .NET support!
http://www.aqla.net
Other
17 stars 3 forks source link

How to setup Aqla for complicated object relationships #44

Closed inethui closed 2 years ago

inethui commented 2 years ago

I have classes with the relationships depicted in the attached class diagram: image

How to setup Aqla for those classes? It seems that the straightforward setup for each relationship doesn't work. Our code assigns an instance of class "NameAndId" to "Property._owner", which is type "INameAndId". When we try to serialize "Property" object, we get exception saying unexpected sub-type "NameAndId" for "Property._owner". "Property._owner" is defined as a dynamic type.

Any suggestions? Thanks

inethui commented 2 years ago

One thing worth to mention, if I remove the relationship between "IEntityNameAndId" and "NameAndIdBase", then the serialization of "Property" works. Don't know why. Any idea?

inethui commented 2 years ago

This is the unit test code to replicate the issue:

using AqlaSerializer;
using System;
using System.Diagnostics;
using System.IO;
using Xunit;

    public class UnitTest5
    {
        private void SetupModel()
        {
            var metaType2 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(Property), false);
            metaType2.DefaultFormat = AqlaSerializer.ValueFormat.Reference;
            metaType2.UseConstructor = false;
            metaType2.IgnoreListHandling = true;

            var metaType3 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(IEntityNameAndId), false);
            metaType3.DefaultFormat = AqlaSerializer.ValueFormat.Reference;
            metaType3.UseConstructor = false;
            metaType3.IgnoreListHandling = true;

            var metaType4 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(INameAndId), false);
            metaType4.DefaultFormat = AqlaSerializer.ValueFormat.Reference;
            metaType4.UseConstructor = false;
            metaType4.IgnoreListHandling = true;
            AqlaSerializer.Meta.MetaType metaType5 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(IEntityNameAndId)];
            metaType5.AddSubType(1, typeof(INameAndId));
            AqlaSerializer.Meta.MetaType metaType6 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(Property)];
            var metaField1 = metaType6.AddField(1, "_owner");
            metaField1.SetSettings(x => { x.V.Format = AqlaSerializer.ValueFormat.Reference; });
            metaField1.SetSettings(x => x.V.WriteAsDynamicType = true, 0);

            var metaType7 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(NameAndIdBase), false);
            metaType7.DefaultFormat = AqlaSerializer.ValueFormat.Reference;
            metaType7.UseConstructor = false;
            metaType7.IgnoreListHandling = true;
            AqlaSerializer.Meta.MetaType metaType8 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(NameAndIdBase)];
            var metaField2 = metaType8.AddField(1, "_id");
            metaField2.SetSettings(x => { x.V.Format = AqlaSerializer.ValueFormat.Compact; });
            var metaField3 = metaType8.AddField(2, "_name");
            metaField3.SetSettings(x => { x.V.Format = AqlaSerializer.ValueFormat.Compact; });

            AqlaSerializer.Meta.MetaType metaType10 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(IEntityNameAndId)];
            metaType10.AddSubType(2, typeof(NameAndIdBase));

            var metaType11 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(NameAndId), false);
            metaType11.DefaultFormat = AqlaSerializer.ValueFormat.Reference;
            metaType11.UseConstructor = false;
            metaType11.IgnoreListHandling = true;
            AqlaSerializer.Meta.MetaType metaType12 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(NameAndIdBase)];
            metaType12.AddSubType(3, typeof(NameAndId));
        }

        [Fact]
        public void TestSerialization()
        {
            SetupModel();

            var property = new Property();
            property.Owner = new NameAndId(101, "test");

            var stream = new MemoryStream();
            try
            {
                Serializer.Serialize(stream, property);
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Failed to serialize property: {e.Message}");
                throw;
            }
        }
    }

    public class Property
    {
        private INameAndId _owner;
        public INameAndId Owner { get => _owner; set => _owner = value; }
    }

    public sealed class NameAndId : NameAndIdBase, INameAndId
    {
        public NameAndId(int id, string name)
            : base(id, name)
        {
        }
    }

    public class NameAndIdBase : IEntityNameAndId
    {
        private readonly string _name = string.Empty;
        private readonly int _id;

        public NameAndIdBase(int id, string name)
        {
            _id = id;
            _name = name;
        }

        public int Id => _id;
        public string Name => _name;
    }

    public interface INameAndId : IEntityNameAndId
    {
    }

    public interface IEntityNameAndId
    {
        int Id { get; }
        string Name { get; }
    }
inethui commented 2 years ago

If we remove the following two lines from the "SetupModel" method, the test passes. I don't know why.

AqlaSerializer.Meta.MetaType metaType10 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(IEntityNameAndId)];
metaType10.AddSubType(2, typeof(NameAndIdBase));
inethui commented 2 years ago

Found a workaround on our side.