类库基类无法添加ProtoInclude引用派生类,如何实现Protobuf序列化?
Protobuf-net 泛型继承序列化的跨项目引用问题解决
问题背景
现有C#类继承结构如下:
[ProtoContract] public class User : UserCommon<User, Database.User> [ProtoContract] public abstract partial class UserCommon<T, T2> : DatabaseCachedObject<T, T2, int> where T : class [ProtoContract] public abstract class DatabaseCachedObject<T, T2, T3> where T : class
按照protobuf-net的序列化要求,需要通过ProtoInclude标记声明继承关系,但由于UserCommon和DatabaseCachedObject位于类库项目中,无法直接引用业务项目里的User类型,导致常规的标记写法无法实现:
// 无法编译的写法:类库不能引用业务层的User类型 [ProtoContract] [ProtoInclude(1, typeof(User))] public abstract partial class UserCommon<T, T2> : DatabaseCachedObject<T, T2, int> where T : class [ProtoContract] [ProtoInclude(1, typeof(UserCommon))] public abstract class DatabaseCachedObject<T, T2, T3> where T : class
可行解决方案
1. 运行时动态注册继承关系
在业务项目的初始化阶段(比如启动时),通过RuntimeTypeModel动态配置基类和子类的继承关联,完全避开类库对业务类型的引用:
// 业务项目初始化代码中执行 var model = RuntimeTypeModel.Default; // 注册User与UserCommon泛型实例的继承关系 model.Add(typeof(UserCommon<User, Database.User>), true) .AddSubType(1, typeof(User)); // 注册UserCommon泛型实例与DatabaseCachedObject泛型实例的继承关系 model.Add(typeof(DatabaseCachedObject<User, Database.User, int>), true) .AddSubType(1, typeof(UserCommon<User, Database.User>));
这种方式把类型关联逻辑放到能访问所有类型的业务层,是最直接的解耦方案。
2. 子类反向标记基类
在业务层的User类上添加ProtoInclude,指向它的直接基类泛型实例,这样序列化User时能正确识别继承链:
[ProtoContract] [ProtoInclude(1, typeof(UserCommon<User, Database.User>))] public class User : UserCommon<User, Database.User>
如果序列化操作大多针对User而非基类,这种方式可以满足需求,无需修改类库代码。
3. 类库定义标记接口
在类库中定义一个空的标记接口,让所有业务层的子类实现该接口,然后在基类中通过接口声明ProtoInclude:
// 类库项目中定义 public interface IUserSubType { } [ProtoContract] [ProtoInclude(1, typeof(IUserSubType))] public abstract partial class UserCommon<T, T2> : DatabaseCachedObject<T, T2, int> where T : class, IUserSubType // 业务项目中实现接口 [ProtoContract] public class User : UserCommon<User, Database.User>, IUserSubType
需要注意的是,要确保protobuf-net的运行时模型能正确识别接口和实现类的映射,必要时可以在初始化时补充注册接口与子类的关系。
内容的提问来源于stack exchange,提问作者Tom Gullen
相关产品推荐
相关产品推荐

