2011-05-23 37 views
3

观察这个简单的代码,使用protobuf网版:protobuf-net代理的这种用法有什么问题?

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: 

EDIT2

我稍微修改了代码来解决替代代码,但是这并不影响这个问题 - 它仍然存在。

EDIT3

我知道command2command含有不同顺序的对象。这在我的场景中是可以接受的。我期待command3相当于command2,但由于某种原因,我得到了这个例外。

+1

“我在做什么错了?” - 你没有给我们提供你所有的信息,即例外。这就是你做错了。请阅读http://tinyurl.com/so-hints。 – 2011-05-23 07:12:03

+0

我的错。你是绝对正确的。 – mark 2011-05-23 08:15:31

+0

好的,在这一点上,我们已经远远超出了我的知识......将会马克·格雷维尔。 – 2011-05-23 08:18:18

回答

1

哦,乍一看,这比我敢于的希望更容易。如果您使用的是自定义模型,则需要使用model上公开的方法。 v1 API(即Serializer.blah)现在只是一个间接的RuntimeTypeModel.Default

尝试:

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

还要注意的是,新创建的对象可能会在为null - 在我的本地摄制我说:

if (cmd == null) return null; 

给操作者。当我觉得不那么糟糕时,我会采取并看看是否有理由让图书馆确保非空。

+0

谢谢。您在您的博客中提到可以控制表示类型名称的字符串。你能举一个例子吗? (当你感觉更好时,那是) – mark 2011-05-23 19:15:17