是否有一种普遍接受的方法可以避免在WCF服务上使用KnownType属性?我一直在做一些研究,看起来有两种选择:
我不是每次添加新类型时都必须静态添加KnownType属性的忠实粉丝,因此希望避免它。
是否应该使用第三种选择?如果是这样,它是什么?如果没有,上述两种选择中的哪一种是正确的选择?
修改 - 使用方法
第三种选择是使用反射
[DataContract]
[KnownType("DerivedTypes")]
public abstract class FooBase
{
private static Type[] DerivedTypes()
{
return typeof(FooBase).GetDerivedTypes(Assembly.GetExecutingAssembly()).ToArray();
}
}
答案 0 :(得分:24)
我想发布一个迄今为止我能想到的最简单,最优雅的解决方案。如果另一个答案更好,我会继续这样做。但就目前而言,这很有效。
基类,只有一个 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;
}
答案 1 :(得分:8)
只要所有涉及的类都在同一个程序集中,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));
}
}
答案 2 :(得分:1)
如果您不喜欢各处的属性,那么您可以使用配置文件。
<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>
答案 3 :(得分:1)
您可以在自定义类型中实现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);
}
}
希望它有所帮助。
答案 4 :(得分:0)
我宁愿一次性提取我的自定义类型,并在序列化/反序列化期间使用它。阅读这篇文章之后,我花了一些时间来了解在哪里注入这个类型列表对序列化程序对象有用。答案很简单:这个列表将被用作序列化器对象的构造函数的输入参数之一。
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架构,但必须从某个地方访问序列化器对象的构造函数。
答案 5 :(得分:0)
这是我接受的答案的变体:
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));
}
区别是: