Что не так с этим использованием суррогата protobuf-net? - PullRequest
3 голосов
/ 23 мая 2011

Соблюдайте этот простой код, используя protobuf-net v2:

  interface IObject { }

  [ProtoContract]
  class Person : IObject
  {
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    [ProtoMember(3, AsReference = true)]
    public Address Address { get; set; }
  }
  [ProtoContract]
  class Address : IObject
  {
    [ProtoMember(1)]
    public string Line1 { get; set; }
    [ProtoMember(2)]
    public string Line2 { get; set; }
  }

  class Command
  {
    public List<IObject> Objects { get; set; }
  }

  internal interface ICommandSurrogatePiece
  {
    IEnumerable<IObject> Objects { get; set; }
  }

  [ProtoContract]
  class CommandSurrogatePiece<T> : ICommandSurrogatePiece 
      where T : class, IObject
  {
    [ProtoMember(1)]
    public List<T> Objects { get; set; }

    #region ICommandSurrogatePiece Members

    IEnumerable<IObject> ICommandSurrogatePiece.Objects
    {
      get { return Objects; }
      set { Objects = value as List<T> ?? value.Cast<T>().ToList(); }
    }

    #endregion
  }

  [ProtoContract]
  class CommandSurrogate
  {
    public static implicit operator Command(CommandSurrogate surrogate)
    {
      var objects = surrogate.Pieces.SelectMany(c => c.Objects).ToList();
      return new Command { Objects = objects };
    }
    public static implicit operator CommandSurrogate(Command cmd)
    {
      var pieces = cmd.Objects.GroupBy(o => o.GetType(), 
          o => o, CreateCommandSurrogatePiece).ToList();
      return new CommandSurrogate { Pieces = pieces };
    }

    private static ICommandSurrogatePiece CreateCommandSurrogatePiece(
        Type type, IEnumerable<IObject> objects)
    {
      var piece = (ICommandSurrogatePiece)Activator.CreateInstance(
          typeof(CommandSurrogatePiece<>).MakeGenericType(type));
      piece.Objects = objects;
      return piece;
    }

    [ProtoMember(1, DynamicType = true)]
    public List<ICommandSurrogatePiece> Pieces { get; set; }
  }

  class Program
  {
    static void Main()
    {
      var person = new Person { Id = 12345, Name = "Fred", Address = 
          new Address { Line1 = "Flat 1", Line2 = "The Meadows" } };
      var person2 = new Person { Id = 2345, Name = "Fred kaka", Address = 
          new Address { Line1 = "Flat 12", Line2 = "The Meadows kuku" } };
      var address = 
          new Address { Line1 = "Flat 2", Line2 = "The Meadows Double" };
      var address2 = 
          new Address { Line1 = "Flat 2 bubub", 
              Line2 = "The Meadows Double kuku" };

      var model = TypeModel.Create();
      model.Add(typeof(CommandSurrogate), true);
      model.Add(typeof(Command), false).SetSurrogate(typeof(CommandSurrogate));
      var command = new Command { Objects = 
          new List<IObject> { person, address, person2, address2 } };
      var command2 = (Command)(CommandSurrogate)command;
      var command3 = Serializer.DeepClone(command);
    }
  }

Последняя строка завершается с ошибкой.Что я делаю неправильно?Спасибо.

РЕДАКТИРОВАТЬ

System.InvalidOperationException occurred
  Message=Type is not expected, and no contract can be inferred: HelloProtoBuf.Command
  Source=protobuf-net
  StackTrace:
       at ProtoBuf.Meta.TypeModel.ThrowUnexpectedType(Type type)
  InnerException: 

РЕДАКТИРОВАТЬ2

Я немного изменил код, чтобы исправить суррогатный код, ноэто не влияет на проблему - она ​​остается.

EDIT3

Я знаю, что command2 и command содержат объекты в другом порядке.Это приемлемо в моем сценарии.Я ожидал, что command3 будет эквивалентно command2, но по какой-то причине я получаю это исключение.

1 Ответ

1 голос
/ 23 мая 2011

Ох, на второй взгляд, это проще, чем я смел надеяться. Если вы используете пользовательскую модель, вам нужно использовать методы, представленные в model. API v1 (т.е. Serializer.blah) теперь является просто косвенным указанием на RuntimeTypeModel.Default.

Попробуйте:

var command3 = (Command)model.DeepClone(command);

Обратите внимание, что вновь созданный объект может появиться как null - в моем локальном репро я добавил:

if (cmd == null) return null;

к оператору. Когда я почувствую себя не в своей тарелке, я возьму и посмотрю, имеет ли смысл для библиотеки обеспечить ненулевое значение.

...