通常被接受的方法来避免每个派生类的KnownType属性

是否有一种普遍接受的方法来避免在WCF服务上使用KnownType属性? 我一直在做一些研究,看起来有两种选择:

  1. 数据合同解析器
  2. NetDataContractSerializer

我不是每次添加新类型时都必须静态添加KnownType属性的忠实粉丝,因此想要避免它。

是否应该使用第三种选择? 如果是这样,它是什么? 如果没有,上述两个选项中的哪一个是正确的方法?

编辑 – 使用方法

第三种选择是使用reflection

[DataContract] [KnownType("DerivedTypes")] public abstract class FooBase { private static Type[] DerivedTypes() { return typeof(FooBase).GetDerivedTypes(Assembly.GetExecutingAssembly()).ToArray(); } } 

我想发布迄今为止我能想到的最简单,最优雅的解决方案。 如果另一个答案出现更好,我会继续这样做。 但就目前而言,这很有效。

只有一个 KnownType属性的基类指向名为DerivedTypes()的方法:

 [KnownType("DerivedTypes")] [DataContract] public abstract class TaskBase : EntityBase { // other class members here private static Type[] DerivedTypes() { return typeof(TaskBase).GetDerivedTypes(Assembly.GetExecutingAssembly()).ToArray(); } } 

GetDerivedTypes()方法,在单独的ReflectionUtility类中:

 public static IEnumerable GetDerivedTypes(this Type baseType, Assembly assembly) { var types = from t in assembly.GetTypes() where t.IsSubclassOf(baseType) select t; return types; } 

只要所有涉及的类都在同一个程序集中,Bob提到的方法就可以工作。

以下方法适用于程序集:

 [DataContract] [KnownType("GetDerivedTypes")] public class BaseClass { public static List DerivedTypes = new List(); private static IEnumerable GetDerivedTypes() { return DerivedTypes; } } [DataContract] public class DerivedClass : BaseClass { //static constructor static DerivedClass() { BaseClass.DerivedTypes.Add(typeof(DerivedClass)); } } 

您可以在自定义类型中实现IXmlSerializable并手动处理其复杂性。 您可以在下面找到示例代码:

 [XmlRoot("ComplexTypeA")] public class ComplexTypeA : IXmlSerializable { public int Value { get; set; } public void WriteXml (XmlWriter writer) { writer.WriteAttributeString("Type", this.GetType().FullName); writer.WriteValue(this.Value.ToString()); } public void ReadXml (XmlReader reader) { reader.MoveToContent(); if (reader.HasAttributes) { if (reader.GetAttribute("Type") == this.GetType().FullName) { this.Value = int.Parse(reader.ReadString()); } } } public XmlSchema GetSchema() { return(null); } } [XmlRoot("ComplexTypeB")] public class ComplexTypeB : IXmlSerializable { public string Value { get; set; } public void WriteXml (XmlWriter writer) { writer.WriteAttributeString("Type", this.GetType().FullName); writer.WriteValue(this.Value); } public void ReadXml (XmlReader reader) { reader.MoveToContent(); if (reader.HasAttributes) { if (reader.GetAttribute("Type") == this.GetType().FullName) { this.Value = reader.ReadString(); } } } public XmlSchema GetSchema() { return(null); } } [XmlRoot("ComplexTypeC")] public class ComplexTypeC : IXmlSerializable { public Object ComplexObj { get; set; } public void WriteXml (XmlWriter writer) { writer.WriteAttributeString("Type", this.GetType().FullName); if (this.ComplexObj != null) { writer.WriteAttributeString("IsNull", "False"); writer.WriteAttributeString("SubType", this.ComplexObj.GetType().FullName); if (this.ComplexObj is ComplexTypeA) { writer.WriteAttributeString("HasValue", "True"); XmlSerializer serializer = new XmlSerializer(typeof(ComplexTypeA)); serializer.Serialize(writer, this.ComplexObj as ComplexTypeA); } else if (tthis.ComplexObj is ComplexTypeB) { writer.WriteAttributeString("HasValue", "True"); XmlSerializer serializer = new XmlSerializer(typeof(ComplexTypeB)); serializer.Serialize(writer, this.ComplexObj as ComplexTypeB); } else { writer.WriteAttributeString("HasValue", "False"); } } else { writer.WriteAttributeString("IsNull", "True"); } } public void ReadXml (XmlReader reader) { reader.MoveToContent(); if (reader.HasAttributes) { if (reader.GetAttribute("Type") == this.GetType().FullName) { if ((reader.GetAttribute("IsNull") == "False") && (reader.GetAttribute("HasValue") == "True")) { if (reader.GetAttribute("SubType") == typeof(ComplexTypeA).FullName) { XmlSerializer serializer = new XmlSerializer(typeof(ComplexTypeA)); this.ComplexObj = serializer.Deserialize(reader) as ComplexTypeA; } else if (reader.GetAttribute("SubType") == typeof(ComplexTypeB).FullName) { XmlSerializer serializer = new XmlSerializer(typeof(ComplexTypeB)); this.ComplexObj = serializer.Deserialize(reader) as ComplexTypeB; } } } } } public XmlSchema GetSchema() { return(null); } } 

希望能帮助到你。

如果您不喜欢各处的属性,那么您可以使用配置文件。

          

我宁愿一次性提取我的自定义类型,并在序列化/反序列化期间使用它。 阅读这篇文章之后,我花了一些时间来了解在哪里注入这个类型列表对序列化程序对象有用。 答案很简单:此列表将用作序列化程序对象的构造函数的输入参数之一。

1-我使用两种静态通用方法进行序列化和反序列化,这可能或多或少与其他人一样,或者至少它与你的代码进行比较非常清楚:

  public static byte[] Serialize(T obj) { var serializer = new DataContractSerializer(typeof(T), MyGlobalObject.ResolveKnownTypes()); var stream = new MemoryStream(); using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) { serializer.WriteObject(writer, obj); } return stream.ToArray(); } public static T Deserialize(byte[] data) { var serializer = new DataContractSerializer(typeof(T), MyGlobalObject.ResolveKnownTypes()); using (var stream = new MemoryStream(data)) using (var reader = XmlDictionaryReader.CreateBinaryReader( stream, XmlDictionaryReaderQuotas.Max)) { return (T)serializer.ReadObject(reader); } } 

2-请注意DataContractSerializer的构造函数。 我们在那里有第二个参数,它是将已知类型注入序列化程序对象的入口点。

3-我正在使用静态方法从我自己的程序集中提取所有自己定义的类型。 您的静态方法的代码可能如下所示:

  private static Type[] KnownTypes { get; set; } public static Type[] ResolveKnownTypes() { if (MyGlobalObject.KnownTypes == null) { List t = new List(); List c = System.Reflection.Assembly.GetEntryAssembly().GetReferencedAssemblies().Where(b => b.Name == "DeveloperCode" | b.Name == "Library").ToList(); foreach (AssemblyName n in c) { System.Reflection.Assembly a = System.Reflection.Assembly.Load(n); t.AddRange(a.GetTypes().ToList()); } MyGlobalObject.KnownTypes = t.ToArray(); } return IOChannel.KnownTypes; } 

由于我没有参与WCF(我只需要文件操作的二进制序列化),我的解决方案可能不完全解决WCF架构,但必须从某个地方访问序列化器对象的构造函数。