protobuf-net 代理的这种用法有什么问题?
使用 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
,但由于某种原因我得到了这个异常。
Observe this simple code, using 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);
}
}
The last line fails with an exception. What am I doing wrong?
Thanks.
EDIT
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
I have slightly modified the code to fix the surrogate code, but this does not affect the problem - it remains.
EDIT3
I am aware that command2
and command
contain objects in different order. This is acceptable in my scenario. I was expecting that command3
be equivalent to command2
, but for some reason I get that exception.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
哦,再看一遍,这比我想象的要容易。如果您使用自定义模型,则需要使用
model
上公开的方法。 v1 API(即Serializer.blah
)现在只是RuntimeTypeModel.Default
的间接调用。尝试:
另请注意,新创建的对象可能以
null
形式出现 - 在我的本地重现中,我向运算符添加了:。当我感觉不那么糟糕时,我会查看并查看库确保非空是否有意义。
Oh, on second glance this is easier than I dared hope. If you are using a custom model, you need to use the methods exposed on
model
. The v1 API (i.e.Serializer.blah
) is now just an indirection toRuntimeTypeModel.Default
.Try:
Note also that the newly created object may come in as
null
- in my local repro I added:to the operator. When I feel less grotty, I'll take and look and see if it makes sense for the library to ensure as non-null.