1

Banana List 包含对 Fruits List 中对象的引用。水果列表中的香蕉一个接一个地放置。此外,它们具有递增的数字。(添加和删除香蕉应该保留这些属性) 问题是在反序列化后 Bananas List 和 Fruits List in Basket 似乎包含不同的对象。有办法避免吗?

我的第二个问题是关于 Banana 中的 ContainedBy 属性。Cos Bananas 知道它们在哪里,它们指向特定的容器,但在反序列化之后,它们再次指向上帝知道的位置(特定于它们所在的其他篮子)。问题在哪里?

    [ProtoContract]
    [ProtoInclude(500, typeof(Basket))]
    public interface IContainer
    {
        [ProtoMember(1, OverwriteList = true)]
        public List<Banana> Bananas { get; set; }

        [ProtoMember(2, OverwriteList = true)]
        public List<IFruit> Fruits { get; set; }

    }

    [ProtoContract]
    public class Basket : IContainer
    {
        [ProtoMember(1, OverwriteList = true)]
        public List<Banana> Bananas { get; set; }

        [ProtoMember(2, OverwriteList = true)]
        public List<IFruit> Fruits { get; set; }

        public void AddBanana()
        {
            var number = Bananas.Count;
            var newBanana = new Banana(number) {ContainedBy = this};
            var lastBanana = Bananas[Bananas.Count - 1];
            var index = Fruits.LastIndexOf(lastBanana);

            Friuts.Insert(index + 1, newBanana);
            Bananas.Add(newBanana);
        }

        public void DeleteBanana(Banana banana)
        {
            Bananas.Remove(banana);
            Fruits.Remove(banana);
            var n = 0;
            foreach (var b in Bananas)
            {
                b.Number = n++;
            }
        }
    }

    [ProtoContract]
    [ProtoInclude(600, typeof(Banana))]
    public interface IFruit
    {
        [ProtoMember(1, AsReference = true)]
        IContainer ContainedBy { get; set; }
    }

    [ProtoContract]
    public class Banana : IFruit
    {
        [ProtoMember(1)]
        public int Number { get; set; }
        [ProtoMember(2, AsReference = true)]
        IContainer ContainedBy { get; set; }
    }

Marc Gravell 给出的更新#1答案对上述问题非常有效。但我的目标功能是容器字典,它给出了一个例外:“反序列化期间引用跟踪的对象更改了引用”。下面列出的更改适用于 Marc Gravell 的答案。

[ProtoContract]
class RootObject
{
    [ProtoMember(1, OverwriteList = true, AsReference = true)]
    public Dictionary<string, IContainer> Dictionary { get; set; }
}

...

internal static class Program
{
    private static void Main()
    {
        var basketA = new Basket();
        basketA.AddBanana();
        var basketB = new Basket();
        basketB.AddBanana();
        var root = new RootObject {Dictionary = new Dictionary<string, IContainer>()};
        root.Dictionary.Add("A",basketA);
        root.Dictionary.Add("B",basketB);

        RootObject clone;
        using (var file = File.Create("tmp.bin"))
        {
            Serializer.Serialize<RootObject>(file, root);
        }

        using (var file = File.OpenRead("tmp.bin"))
        {
            clone = Serializer.Deserialize<RootObject>(file);
        }

        foreach (var container in clone.Dictionary.Values) //Exception
        {          
        Console.WriteLine(container.Bananas.Count == container.Fruits.Count); //true
        Console.WriteLine(ReferenceEquals(
           container.Bananas[0],
           container.Fruits[0])); // true
        Console.WriteLine(ReferenceEquals(
            container.Fruits[0].ContainedBy,
            container)); // false

        }           
    }
}

显然,将 RootObject 中的 Dictionary 标记从[ProtoMember(1, OverwriteList = true, AsReference = true)]to更改为[ProtoMember(1, OverwriteList = true)]有助于解决异常,但引用 ContainedBy 会丢失。

4

1 回答 1

2

默认情况下,protobuf-net 是树序列化器,而不是图形序列化器;所以在你告诉它尊重所涉及的引用之前,它会分别序列化香蕉和水果下的信息。此外,您需要注意双重序列化,如此处所述。同一篇文章还描述了接口及其类之间的当前“需要工作”区域,这可能会在这里影响您;我想我已经通过交换Fruitsand的顺序在很大程度上解决了这个问题Bananas

请注意,我删除了“lastBanana / index”代码,原因很简单,即当列表为空时它会爆炸,而且我不想预先阻止你的意图 - 我会让你重新添加那个代码。

不过,我的主要想法很简单:看起来您正在序列化您的主域模型。这本身并没有,但是:如果它开始变得棘手,那么我总是建议:切换到更简单的 DTO 模型进行序列化,并映射到 DTO/域模型/映射到 DTO/域模型。

无论如何,以下似乎有效:

using ProtoBuf;
using System;
using System.Collections.Generic;

[ProtoContract]
class RootObject
{
    [ProtoMember(1, AsReference = true)]
    public IContainer Container { get; set; }
}

[ProtoContract]
[ProtoInclude(500, typeof(Basket))]
public interface IContainer
{
    [ProtoMember(2, OverwriteList = true, AsReference = true)]
    List<Banana> Bananas { get; set; }

    [ProtoMember(1, OverwriteList = true, AsReference = true)]
    List<IFruit> Fruits { get; set; }

}

[ProtoContract]
public class Basket : IContainer
{
    public List<Banana> Bananas { get; set; }

    public List<IFruit> Fruits { get; set; }

    public void AddBanana()
    {
        if (Bananas == null) Bananas = new List<Banana>();
        if (Fruits == null) Fruits = new List<IFruit>();
        var number = Bananas.Count;
        var newBanana = new Banana { Number = number, ContainedBy = this };

        // var lastBanana = Bananas[Bananas.Count - 1];
        // var index = Fruits.LastIndexOf(lastBanana);

        // Fruits.Insert(index + 1, newBanana);

        Fruits.Add(newBanana);
        Bananas.Add(newBanana);
    }

    public void DeleteBanana(Banana banana)
    {
        Bananas.Remove(banana);
        Fruits.Remove(banana);
        var n = 0;
        foreach (Banana b in Bananas)
        {
            b.Number = n++;
        }
    }
}

[ProtoContract]
[ProtoInclude(600, typeof(Banana))]
public interface IFruit
{
    [ProtoMember(1, AsReference = true)]
    IContainer ContainedBy { get; set; }
}

[ProtoContract]
public class Banana : IFruit
{
    [ProtoMember(1)]
    public int Number { get; set; }

    public IContainer ContainedBy { get; set; }
}


static class Program
{
    static void Main()
    {
        var basket = new Basket();
        var root = new RootObject { Container = basket };
        basket.AddBanana();

        var clone = Serializer.DeepClone(root);
        Console.WriteLine(clone.Container.Fruits.Count == 1); // true
        Console.WriteLine(clone.Container.Bananas.Count == 1); // true
        Console.WriteLine(ReferenceEquals(
            clone.Container.Bananas[0],
            clone.Container.Fruits[0])); // true
        Console.WriteLine(ReferenceEquals(
            clone.Container.Fruits[0].ContainedBy,
            clone.Container)); // true
    }
}
于 2012-10-01T06:16:19.380 回答