有没有一个被普遍接受的方式来避免使用对WCF服务KnownType属性? 我一直在做一些研究,它看起来像有两种选择:
- 数据契约解析
- NetDataContractSerializer
我不必添加静态属性KnownType我每次添加一个新的类型时的大风扇,因此想要避免它。
是否有应使用第三种选择? 如果是这样,是什么呢? 如果不是,其中上述两个选项是正确的路要走?
编辑-使用方法
第三个选择是使用反射
[DataContract]
[KnownType("DerivedTypes")]
public abstract class FooBase
{
private static Type[] DerivedTypes()
{
return typeof(FooBase).GetDerivedTypes(Assembly.GetExecutingAssembly()).ToArray();
}
}
Answer 1:
我想发布的东西似乎是我能想到的,到目前为止最简单,最优雅的解决方案。 如果另一个答案走来那最好不过,我也有去。 但现在,它运行良好。
基类,只有一个 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<Type> GetDerivedTypes(this Type baseType, Assembly assembly)
{
var types = from t in assembly.GetTypes()
where t.IsSubclassOf(baseType)
select t;
return types;
}
Answer 2:
由Bob提到的方法,将工作,只要所有参与类是在同一程序。
下面的方法将整个组件的工作:
[DataContract]
[KnownType("GetDerivedTypes")]
public class BaseClass
{
public static List<Type> DerivedTypes = new List<Type>();
private static IEnumerable<Type> GetDerivedTypes()
{
return DerivedTypes;
}
}
[DataContract]
public class DerivedClass : BaseClass
{
//static constructor
static DerivedClass()
{
BaseClass.DerivedTypes.Add(typeof(DerivedClass));
}
}
Answer 3:
您可以在自定义类型实现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);
}
}
希望能帮助到你。
Answer 4:
如果你不喜欢到处属性,那么你可以使用配置文件。
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type = "Contact,Host,Version=1.0.0.0,Culture=neutral,
PublicKeyToken=null">
<knownType type = "Customer,MyClassLibrary,Version=1.0.0.0,
Culture=neutral,PublicKeyToken=null"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Answer 5:
我宁愿我的提取自定义类型一下子和序列化/反序列化过程中使用它。 阅读这篇文章后,我花了一段时间来理解注入的类型此列表成为序列化对象有用。 答案是很简单:这个名单将被用作串行对象的构造函数的输入参数之一。
1 - 我使用了序列化和反序列化两个静态泛型方法,这可能是或多或少的方式人也做的工作,或者至少是制作你的代码的比较很清楚:
public static byte[] Serialize<T>(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<T>(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<Type> t = new List<Type>();
List<AssemblyName> 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架构,但必须有从什么地方进入串行对象的构造函数。
Answer 6:
下面是我对接受的答案变种:
private static IEnumerable<Type> GetKnownTypes() {
Type baseType = typeof(MyBaseType);
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.DefinedTypes)
.Where(x => x.IsClass && !x.IsAbstract && x.GetCustomAttribute<DataContractAttribute>() != null && baseType.IsAssignableFrom(x));
}
区别是:
- 着眼于所有加载的程序集。
- 检查一些比特我们感兴趣的是(DataContract我认为,如果你使用DataContractJsonSerializer是必需的),例如是一个具体的类。
- 你可以在这里使用isSubclassOf,我倾向于选择IsAssignableFrom一般捕获所有覆盖的变种。 特别是我认为它与仿制药。
- 利用KnownTypes接受IEnumerable的(如果它在这种情况下重要的,可能不是),而不是转换成一个数组。
文章来源: Generally accepted way to avoid KnownType attribute for every derived class