VS生成的服务客户端抛出反序列化异常

时间:2012-06-15 12:02:22

标签: wcf .net-4.0 deserialization

我们的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类没有生成KnownTypeAttribute

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

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

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

感谢您的时间。

2 个答案:

答案 0 :(得分:1)

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

以下代码使用List<ParamCollectionElement>代替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;
    }
} 

答案 1 :(得分:0)

确保您的枚举上有[DataContract],并且每个枚举成员都有[EnumMember]

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

这应该导致在客户端中构建proxy-enum(及其成员值),而无需手动更改Reference.cs文件。