C# Protobuf无注释的网络序列化

C# Protobuf无注释的网络序列化,c#,serialization,annotations,protobuf-net,C#,Serialization,Annotations,Protobuf Net,我看了答案,我的处境是,我不需要维护向后兼容性,我必须有一个解决方案,不必用protobuf net所需的属性装饰几十个类。因此我尝试使用RuntimeTypeModel.Default.InferTagFromNameDefault=true但我可能没有正确使用它,因为Serializer.Serialize调用仍然会抛出一个请求约定的异常。这是我的快速测试,我做错了什么 public enum CompanyTypes { None, Small, Big, Enterprise,

我看了答案,我的处境是,我不需要维护向后兼容性,我必须有一个解决方案,不必用protobuf net所需的属性装饰几十个类。因此我尝试使用
RuntimeTypeModel.Default.InferTagFromNameDefault=true
但我可能没有正确使用它,因为Serializer.Serialize调用仍然会抛出一个请求约定的异常。这是我的快速测试,我做错了什么

public enum CompanyTypes
{
    None, Small, Big, Enterprise, Startup
}

public class BaseUser
{
    public string SSN { get; set; }    
}

public class User : BaseUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public DateTime BirthDate { get; set; }
    public List<string> Friends { get; set; }
    public Company Company { get; set; }
}

public class Company
{
    public string Name { get; set; }
    public string Address { get; set; }
    public CompanyTypes Type { get; set; }
    public List<Product> Products { get; set; }
}

public class Product
{
    public string Name { get; set; }
    public string Sku { get; set; }
}

[TestClass]
public class SerializationTest
{
    [TestMethod]
    public void SerializeDeserializeTest()
    {
        var user = new User
                       {
                           Age = 10,
                           BirthDate = DateTime.Now.AddYears(-10),
                           FirstName = "Test First",
                           LastName = "Test Last",
                           Friends = new List<string> { "Bob", "John" },
                           Company = new Company
                                         {
                                             Name = "Test Company",
                                             Address = "Timbuktu",
                                             Type = CompanyTypes.Startup,
                                             Products = new List<Product>
                                             {
                                                new Product{Name="Nerf Rocket", Sku="12324AC"},
                                                new Product{Name="Nerf Dart", Sku="DHSN123"}
                                             }
                                         }
                       };

        RuntimeTypeModel.Default.InferTagFromNameDefault = true;
        using (var memoryStream = new MemoryStream())
        {
            Serializer.Serialize(memoryStream, user);
            var serialized = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
        }
    }
}
公共枚举公司类型
{
无、小、大、企业、初创企业
}
公共类基类用户
{
公共字符串SSN{get;set;}
}
公共类用户:BaseUser
{
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共整数{get;set;}
公共日期时间出生日期{get;set;}
公共列表朋友{get;set;}
上市公司{get;set;}
}
公营公司
{
公共字符串名称{get;set;}
公共字符串地址{get;set;}
公共公司类型{get;set;}
公共列表产品{get;set;}
}
公共类产品
{
公共字符串名称{get;set;}
公共字符串Sku{get;set;}
}
[测试类]
公共类序列化测试
{
[测试方法]
public void序列化反序列化测试()
{
var user=新用户
{
年龄=10岁,
生日=DateTime.Now.AddYears(-10),
FirstName=“首先测试”,
LastName=“最后测试”,
Friends=新列表{“Bob”,“John”},
公司=新公司
{
Name=“测试公司”,
地址=“廷巴克图”,
类型=公司类型。启动,
产品=新列表
{
新产品{Name=“Nerf Rocket”,Sku=“12324AC”},
新产品{Name=“Nerf Dart”,Sku=“DHSN123”}
}
}
};
RuntimeTypeModel.Default.InferTagFromNameDefault=true;
使用(var memoryStream=new memoryStream())
{
Serializer.Serialize(memoryStream,user);
var serialized=Convert.ToBase64String(memoryStream.GetBuffer(),0,(int)memoryStream.Length);
}
}
}

InferTagFromName
(它是双胞胎,
InferTagFromNameDefault
)只有在需要解析成员的标签号时才伸出手来;它们不影响哪些成员需要序列化(因此目前的答案是:无,即使系统知道它们)。您可能选择的选项是
ImplicitFields
,但该选项目前仅作为
[ProtoContract(…)]
标记可用。如果您不介意做一点注释,一个实用的解决方案可能是:

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
用户
公司
产品
,以及
基本用户
更复杂的内容(因为继承):

注意,我们不必添加大量的每个成员注释。如果您确实是反属性的,那么也可以通过以下方式通过代码配置整个模型:

RuntimeTypeModel.Default.Add(typeof(Product), false).Add("Name", "Sku");
RuntimeTypeModel.Default.Add(typeof(Company), false).Add("Name", "Address",
         "Type", "Products");
RuntimeTypeModel.Default.Add(typeof(User), false).Add("FirstName", "LastName",
         "Age", "BirthDate", "Friends", "Company");
RuntimeTypeModel.Default.Add(typeof(BaseUser), false).Add(10, "SSN")
         .AddSubType(1, typeof(User));

在这个阶段,这是一个相当实验性的过程,但我已经创建了一个小型库,它接受大多数类型,并在运行时生成Protobuf网络序列化程序:

这个问题很老了,但可能有人会需要它。我实现了ProtobufSerializer类,它将在使用时构建您的类型图。您只需使用[KnownTypeAttribute][DataMember]/[IgnoreDataMember]属性对DTO进行注释。大多数情况下,这是某个人的另一个nuget项目的重构版本。这样,您就不需要在合同依赖项中包含protobuf:

    internal sealed class ProtobufSerializer
    {
        private readonly RuntimeTypeModel _model;
        private const BindingFlags Flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        private readonly Dictionary<Type, HashSet<Type>> _subTypes = new Dictionary<Type, HashSet<Type>>();
        private readonly ConcurrentDictionary<Type, bool> _builtTypes = new ConcurrentDictionary<Type, bool>();
        private static readonly Type[] ComplexPrimitives = new [] { typeof(object), typeof(ValueType), typeof(Enum), typeof(Array)};
        private readonly object _sync = new object();

        public ProtobufSerializer()
        {
            _model = TypeModel.Create();
        }

        public void Serialize(Stream s, object input)
        {
            EnsureType(input.GetType());
            _model.Serialize(s, input);
        }

        public T Deserialize<T>(Stream s)
        {
            EnsureType(typeof(T));
            return (T)_model.Deserialize(s, null, typeof(T));
        }

        public void EnsureType(Type type)
        {
            if (_builtTypes.ContainsKey(type))
            {
                return;
            }
            lock (_sync)
            {
                if (_builtTypes.ContainsKey(type))
                {
                    return;
                }
                var all = GetGraph(type).ToArray();
                foreach (var t in all)
                {
                    InternalBuild(t);
                }
            }
        }

        private void InternalBuild(Type type)
        {
            if (IsPrimitive(type))
            {
                return;
            }

            FlatBuild(type);
            EnsureBaseClasses(type);
            EnsureGenerics(type);

            _builtTypes.TryAdd(type, false);
        }

        private bool IsPrimitive(Type type)
        {
            return type == null || type.IsPrimitive || _model.CanSerializeBasicType(type) || _builtTypes.ContainsKey(type) || ComplexPrimitives.Contains(type);
        }

        private static IEnumerable<Type> GetGraph(Type type)
        {
            return type.TraverseDistinct(GetConnections).Distinct().OrderBy(x=> x.FullName);
        }

        private static Type GetParent(Type type)
        {
            return type.BaseType;
        }

        private static IEnumerable<Type> GetChildren(Type type)
        {
            var knownTypes = type.GetCustomAttributes(typeof(KnownTypeAttribute)).Cast<KnownTypeAttribute>().Select(x => x.Type).ToArray();
            foreach (var t in knownTypes)
            {
                yield return t;
            }

            var fields = GetFields(type);
            var props = GetProperties(type);
            foreach (var memberType in fields.Select(f => f.FieldType))
            {
                yield return memberType;
            }
            foreach (var memberType in props.Select(f => f.PropertyType))
            {
                yield return memberType;
            }
        }

        private static IEnumerable<Type> GetConnections(Type type)
        {
            var parent = GetParent(type);
            if (parent != null)
            {
                yield return parent;
            }
            var children = GetChildren(type);
            if (children != null)
            {
                foreach (var c in children)
                {
                    yield return c;
                }
            }
        }

        private void FlatBuild(Type type)
        {
            if(type.IsAbstract)
                return;

            var meta = _model.Add(type, false);
            var fields = GetFields(type);
            var props = GetProperties(type);
            meta.Add(fields.Select(m => m.Name).ToArray());
            meta.Add(props.Select(m => m.Name).ToArray());
            meta.UseConstructor = false;
            foreach (var memberType in fields.Select(f => f.FieldType).Where(t => !t.IsPrimitive))
            {
                InternalBuild(memberType);
            }
            foreach (var memberType in props.Select(f => f.PropertyType).Where(t => !t.IsPrimitive))
            {
                InternalBuild(memberType);
            }
        }

        private static FieldInfo[] GetFields(Type type)
        {
            return type.GetFields(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x => !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray();
        }

        private static PropertyInfo[] GetProperties(Type type)
        {
            return type.GetProperties(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x=> !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray();
        }

        private void EnsureBaseClasses(Type type)
        {
            var baseType = type.BaseType;
            var inheritingType = type;


            while (!IsPrimitive(baseType))
            {
                HashSet<Type> baseTypeEntry;

                if (!_subTypes.TryGetValue(baseType, out baseTypeEntry))
                {
                    baseTypeEntry = new HashSet<Type>();
                    _subTypes.Add(baseType, baseTypeEntry);
                }

                if (!baseTypeEntry.Contains(inheritingType))
                {
                    InternalBuild(baseType);
                    _model[baseType].AddSubType(baseTypeEntry.Count + 500, inheritingType);
                    baseTypeEntry.Add(inheritingType);
                }

                inheritingType = baseType;
                baseType = baseType.BaseType;
            }
        }

        private void EnsureGenerics(Type type)
        {
            if (type.IsGenericType || (type.BaseType != null && type.BaseType.IsGenericType))
            {
                var generics = type.IsGenericType ? type.GetGenericArguments() : type.BaseType.GetGenericArguments();

                foreach (var generic in generics)
                {
                    InternalBuild(generic);
                }
            }
        }
    }
内部密封类ProtobufSerializer
{
私有只读RuntimeTypeModel\u模型;
private const BindingFlags Flags=BindingFlags.flatterhierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
私有只读字典_subtype=新字典();
私有只读ConcurrentDictionary _builtTypes=new ConcurrentDictionary();
私有静态只读类型[]ComplexPrimitives=new[]{typeof(object)、typeof(ValueType)、typeof(Enum)、typeof(Array)};
私有只读对象_sync=新对象();
公共ProtobufSerializer()
{
_model=TypeModel.Create();
}
公共void序列化(流、对象输入)
{
EnsureType(input.GetType());
_model.Serialize(s,输入);
}
公共T反序列化(流s)
{
EnsureType(typeof(T));
返回(T)u模型。反序列化(s,null,typeof(T));
}
公共无效EnsureType(类型)
{
if(_buildtypes.ContainsKey(类型))
{
返回;
}
锁定(同步)
{
if(_buildtypes.ContainsKey(类型))
{
返回;
}
var all=GetGraph(type).ToArray();
foreach(var t总计)
{
内部构建(t);
}
}
}
私有内部生成(类型)
{
if(IsPrimitive(type))
{
返回;
}
扁平型;
第二类(类型);
(类型);
_builtTypes.TryAdd(type,false);
}
    internal sealed class ProtobufSerializer
    {
        private readonly RuntimeTypeModel _model;
        private const BindingFlags Flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        private readonly Dictionary<Type, HashSet<Type>> _subTypes = new Dictionary<Type, HashSet<Type>>();
        private readonly ConcurrentDictionary<Type, bool> _builtTypes = new ConcurrentDictionary<Type, bool>();
        private static readonly Type[] ComplexPrimitives = new [] { typeof(object), typeof(ValueType), typeof(Enum), typeof(Array)};
        private readonly object _sync = new object();

        public ProtobufSerializer()
        {
            _model = TypeModel.Create();
        }

        public void Serialize(Stream s, object input)
        {
            EnsureType(input.GetType());
            _model.Serialize(s, input);
        }

        public T Deserialize<T>(Stream s)
        {
            EnsureType(typeof(T));
            return (T)_model.Deserialize(s, null, typeof(T));
        }

        public void EnsureType(Type type)
        {
            if (_builtTypes.ContainsKey(type))
            {
                return;
            }
            lock (_sync)
            {
                if (_builtTypes.ContainsKey(type))
                {
                    return;
                }
                var all = GetGraph(type).ToArray();
                foreach (var t in all)
                {
                    InternalBuild(t);
                }
            }
        }

        private void InternalBuild(Type type)
        {
            if (IsPrimitive(type))
            {
                return;
            }

            FlatBuild(type);
            EnsureBaseClasses(type);
            EnsureGenerics(type);

            _builtTypes.TryAdd(type, false);
        }

        private bool IsPrimitive(Type type)
        {
            return type == null || type.IsPrimitive || _model.CanSerializeBasicType(type) || _builtTypes.ContainsKey(type) || ComplexPrimitives.Contains(type);
        }

        private static IEnumerable<Type> GetGraph(Type type)
        {
            return type.TraverseDistinct(GetConnections).Distinct().OrderBy(x=> x.FullName);
        }

        private static Type GetParent(Type type)
        {
            return type.BaseType;
        }

        private static IEnumerable<Type> GetChildren(Type type)
        {
            var knownTypes = type.GetCustomAttributes(typeof(KnownTypeAttribute)).Cast<KnownTypeAttribute>().Select(x => x.Type).ToArray();
            foreach (var t in knownTypes)
            {
                yield return t;
            }

            var fields = GetFields(type);
            var props = GetProperties(type);
            foreach (var memberType in fields.Select(f => f.FieldType))
            {
                yield return memberType;
            }
            foreach (var memberType in props.Select(f => f.PropertyType))
            {
                yield return memberType;
            }
        }

        private static IEnumerable<Type> GetConnections(Type type)
        {
            var parent = GetParent(type);
            if (parent != null)
            {
                yield return parent;
            }
            var children = GetChildren(type);
            if (children != null)
            {
                foreach (var c in children)
                {
                    yield return c;
                }
            }
        }

        private void FlatBuild(Type type)
        {
            if(type.IsAbstract)
                return;

            var meta = _model.Add(type, false);
            var fields = GetFields(type);
            var props = GetProperties(type);
            meta.Add(fields.Select(m => m.Name).ToArray());
            meta.Add(props.Select(m => m.Name).ToArray());
            meta.UseConstructor = false;
            foreach (var memberType in fields.Select(f => f.FieldType).Where(t => !t.IsPrimitive))
            {
                InternalBuild(memberType);
            }
            foreach (var memberType in props.Select(f => f.PropertyType).Where(t => !t.IsPrimitive))
            {
                InternalBuild(memberType);
            }
        }

        private static FieldInfo[] GetFields(Type type)
        {
            return type.GetFields(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x => !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray();
        }

        private static PropertyInfo[] GetProperties(Type type)
        {
            return type.GetProperties(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x=> !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray();
        }

        private void EnsureBaseClasses(Type type)
        {
            var baseType = type.BaseType;
            var inheritingType = type;


            while (!IsPrimitive(baseType))
            {
                HashSet<Type> baseTypeEntry;

                if (!_subTypes.TryGetValue(baseType, out baseTypeEntry))
                {
                    baseTypeEntry = new HashSet<Type>();
                    _subTypes.Add(baseType, baseTypeEntry);
                }

                if (!baseTypeEntry.Contains(inheritingType))
                {
                    InternalBuild(baseType);
                    _model[baseType].AddSubType(baseTypeEntry.Count + 500, inheritingType);
                    baseTypeEntry.Add(inheritingType);
                }

                inheritingType = baseType;
                baseType = baseType.BaseType;
            }
        }

        private void EnsureGenerics(Type type)
        {
            if (type.IsGenericType || (type.BaseType != null && type.BaseType.IsGenericType))
            {
                var generics = type.IsGenericType ? type.GetGenericArguments() : type.BaseType.GetGenericArguments();

                foreach (var generic in generics)
                {
                    InternalBuild(generic);
                }
            }
        }
    }
    public static IEnumerable<T> TraverseDistinct<T>(this T enumer, Func<T, IEnumerable<T>> getChildren)
    {
        return new[] { enumer }.TraverseDistinct(getChildren);
    }
    public static IEnumerable<T> TraverseDistinct<T>(this IEnumerable<T> enumer, Func<T, IEnumerable<T>> getChildren)
    {
        HashSet<T> visited = new HashSet<T>();
        Stack<T> stack = new Stack<T>();
        foreach (var e in enumer)
        {
            stack.Push(e);
        }
        while (stack.Count > 0)
        {
            var i = stack.Pop();
            yield return i;
            visited.Add(i);

            var children = getChildren(i);
            if (children != null)
            {
                foreach (var child in children)
                {
                    if (!visited.Contains(child))
                    {
                        stack.Push(child);
                    }
                }
            }
        }
    }