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

时间:2013-04-25 16:38:36

标签: c# wcf datacontract known-types

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

  1. Data contract resolver
  2. NetDataContractSerializer
  3. 我不是每次添加新类型时都必须静态添加KnownType属性的忠实粉丝,因此希望避免它。

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

    修改 - 使用方法

    第三种选择是使用反射

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

6 个答案:

答案 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));
    }

区别是:

  1. 查看所有已加载的程序集。
  2. 检查我们感兴趣的一些位(如果您使用DataContractJsonSerializer,我认为这是必需的),例如作为具体类。
  3. 您可以在此处使用isSubclassOf,我一般倾向于使用IsAssignableFrom来捕获所有重写的变体。我认为它尤其适用于泛型。
  4. 利用KnownTypes接受IEnumerable(如果在这种情况下可能不重要),而不是转换为数组。