【问题标题】:VS-generated clients of my service throw Deserialization exceptions我的服务的 VS 生成的客户端抛出反序列化异常
【发布时间】:2012-06-20 04:20:00
【问题描述】:

我们的 WCF 服务只有一种方法:

[ServiceContract(Name = "Service", Namespace = "http://myservice/")]
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
public interface IService {
     Response Execute(Request request);
}

public class Service : IService {
     public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider) {
        return KnownTypesResolver.GetKnownTypes();
     }

     public Response Execute(Request request) { 
         return new MyResponse { Result = MyEnumHere.FirstValue }; 
     }
}

RequestResponse 类都包含一个 ParameterCollection 成员。

[Serializable]
[CollectionDataContract(Name = "ParameterCollection", Namespace = "http://myservice/")]
[KnownType("GetKnownTypes")]
public class ParameterCollection : Dictionary<string, object> {
        private static IEnumerable<Type> GetKnownTypes()
        {
            return KnownTypesResolver.GetKnownTypes();
        }
}

RequestResponse 的子类将它们的值存储到 ParameterCollection 值包中。

我正在使用 KnownTypesResolver 类来提供所有服务对象的类型信息。

public static class KnownTypesResolver {
     public static IEnumerable<Type> GetKnownTypes()
     {
         var asm = typeof(IService).Assembly;
         return asm
             .GetAllDerivedTypesOf<Response>() // an extension method
             .Concat(new Type[] {
                 typeof(MyEnumHere),
                 typeof(MyEnumHere?),
                 typeof(MyClassHere),
                 typeof(MyClassListHere),
             });
     }
}

如果我没记错的话,一切都应该有适当的类型信息,以便代理类生成工具在客户端生成定义良好的类。 但是,每当Response 子类之一(即MyResponse)包含诸如MyEnumHere 之类的枚举值时,WCF 就会开始抱怨反序列化程序不知道 MyEnumHere 值。它应该有。为此,我提供了KnownTypeAttribute

客户端代理类在 Reference.cs 文件中确实有一个MyEnumHere 枚举;问题是ParameterCollection 类没有为它生成KnownTypeAttributes。

我采用了手工编辑,并在生成的 Reference.cs 文件中包含以下几行:

//>
[KnownTypeAttribute(typeof(MyEnumHere))]
[KnownTypeAttribute(typeof(MyEnumHere?))]
[KnownTypeAttribute(typeof(MyClassHere))]
[KnownTypeAttribute(typeof(MyClassListHere))]
//<
public class ParameterCollection : Dictionary<string, object> { /* ... */ }

手工编辑生成的文件太可怕了。但这使客户工作。我究竟做错了什么?如何定义我的服务对象,以便生成的 VS 代理类从一开始就正确?

感谢您的宝贵时间。

【问题讨论】:

    标签: wcf .net-4.0 deserialization


    【解决方案1】:

    WCF 不适用于Dictionary,因为它不可互操作。您可以使用ArrayList 或自定义集合来确保您的数据正确序列化。

    下面的代码使用List&lt;ParamCollectionElement&gt; 而不是Dictionary。我还删除了一些多余的属性。

    [DataContract]
    public class Request
    {
        [DataMember]
        public ParameterCollection ParameterCollection { get; set; }
    }
    
    [DataContract]
    public class Response
    {
        [DataMember]
        public ParameterCollection ParameterCollection { get; set; }
    }
    
    [DataContract]
    public class MyResponse : Response
    {
        [DataMember]
        public MyEnumHere Result { get; set; }
    }
    
    public class ParamCollectionElement
    {
        public string Key { get; set; }
        public object Value { get; set; } 
    }
    
    
    [CollectionDataContract(Name = "ParameterCollection")]
    public class ParameterCollection : List<ParamCollectionElement> 
    {
    
    }
    
    public static class KnownTypesResolver
    {
        public static IEnumerable<Type> GetKnownTypes()
        {
            return
                new Type[] {
                    typeof(MyEnumHere),
                    typeof(MyEnumHere?),
                    typeof(Request),
                    typeof(Response),
                    typeof(MyResponse)
                };
        }
    }
    
    [DataContract]
    public enum MyEnumHere
    {
        [EnumMember]
        FirstValue,
        [EnumMember]
        SecondValue
    }
    
    [ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
    [ServiceContract(Name = "Service")]
    public interface IService
    {
        [OperationContract]
        Response Execute(Request request);
    }
    
    public class Service : IService
    {
        public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
        {
            return KnownTypesResolver.GetKnownTypes();
        }
    
        public Response Execute(Request request)
        {
            var result = new MyResponse
            {
                Result = MyEnumHere.FirstValue,
                ParameterCollection = new ParameterCollection()
            };
    
            result.ParameterCollection.Add(new ParamCollectionElement {Key = "one", Value = MyEnumHere.FirstValue});
            result.ParameterCollection.Add(new ParamCollectionElement { Key = "two", Value = new Response() });
            return result;
        }
    } 
    

    【讨论】:

    • 谢谢达尼茨基。您的更改确实修复了生成的文件,这是向前迈出的一大步。但是,客户端现在失去了像常规字典一样索引到 ParameterCollection 的能力,这是我宁愿拥有的(很难证明迭代集合以查看是否存在键值对)。接受这个作为答案。欢迎任何其他反馈。
    • @Raine 您可以使用自定义集合中的数据在客户端创建和加载字典。
    【解决方案2】:

    确保你的枚举中有[DataContract],每个枚举成员都有[EnumMember]

    [DataContract]
    public enum MyEnumHere
    {
        [EnumMember]
        SomeValue,
        [EnumMember]
        OtherValue,
        [EnumMember]
        OneMoreValue
    }
    

    这应该会导致在您的客户端中构建代理枚举(及其成员值),而无需手动更改 Reference.cs 文件。

    【讨论】:

    • 感谢您的回答。正如我在原帖中提到的,代理文件确实有一个 MyEnumHere 枚举,生成的枚举与服务器端定义完全相同。问题是反序列化器不知道这种类型,因为 Reference.cs 文件中缺少 KnownType 属性,我的目标是让它们自动生成。
    • 我的错,我读错了异常(关于枚举)。我假设这里的根本原因是能够序列化 Dictionary 的对象值。您能否将其定义为可序列化类型,从而排除对 KnownTypeAttributes 的需求?只是一个想法。
    • 听起来像一个变体类(我会在不同的字段中存储不同的类型)。我想它可能会有所帮助,但我相信我(以及服务消费者)将 ParameterCollection 类从字典更改为列表而不是将每个条目值更改为变体会更容易。话虽如此,感谢您的意见,我喜欢不同的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2019-10-26
    • 1970-01-01
    相关资源
    最近更新 更多