WCF服务返回字典数组<string,object =“”> </string,>

时间:2010-01-29 05:23:12

标签: c# wcf dictionary object

我一直在尝试使用SilverLight客户端来调用返回Dictionary<string, object>的ASP.Net WCF服务。 当字典中的值是intstringGuid这样的简单类型时,这很好用。

但是,我现在有一个场景,我需要其中一个值为Dictionary<string, object>的数组!它编译得很好,服务的签名没有改变,但服务调用现在失败了。

任何想法如何解决?我尝试使用KnownTypeServiceKnownType属性来注释我的服务类和方法,但这不起作用。

这是一段代码:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
    [OperationContract]
    [ServiceKnownType(typeof(Dictionary<string, object>))]
    public Dictionary<string, object> GetObject()
    {
        return new Dictionary<string, object>()
            {
                { "pty1", 1 },
                { "pty2", Guid.NewGuid() },
                { "pty3", "blah" },
                { "pty4", new Dictionary<string, object>[]
                              {
                                  new Dictionary<string, object>()
                                      {
                                          { "pty1", 4 },
                                          { "pty2", Guid.NewGuid() },
                                          { "pty3", "blah" },
                                      }
                                   ,
                                   new Dictionary<string, object>()
                                      {
                                          { "pty1", 4 },
                                          { "pty2", Guid.NewGuid() },
                                          { "pty3", "blahblah" },
                                      }
                              }
            }
        };
    }
}

感谢您的回答。我打开了WCF跟踪,并且正如所怀疑的那样,序列化期间存在问题。 问题不在于Dictionary<string, object>的序列化,而是Array Dictionary<string, object>的序列化。

这里是WCF服务记录的异常。

  

尝试序列化参数时出错:GetObjectResult。 InnerException消息是'Type'System.Collections.Generic.Dictionary`2 [[System.String,mscorlib,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089],[System.Object,mscorlib,Version = 2.0。 0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]] []',数据协定名称为'ArrayOfArrayOfKeyValueOfstringanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays',不是预期的。将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。有关详细信息,请参阅InnerException。

我还尝试使用单个数据成员定义一个新的DataContract类,但它会导致同样的错误。

以下是代码,然后是WCF日志记录的异常。

[DataContract]
[KnownType(typeof(ObjectHolder))]
public class ObjectHolder
{
    [DataMember]
    public object Object { get; private set; }

    public ObjectHolder(object obj)
    {
        this.Object = obj;
    }
}
  

尝试序列化参数时出错:GetObjectResult。 InnerException消息是'Type'System.Collections.Generic.Dictionary`2 [[System.String,mscorlib,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089],[SilverlightApplication7.Web.ObjectHolder,SilverlightApplication7.Web,版本= 1.0.0.0,Culture = neutral,PublicKeyToken = null]] []',数据协定名称为'ArrayOfArrayOfKeyValueOfstringObjectHolderWAwxSTlb:http://schemas.microsoft.com/2003/10/Serialization/Arrays'不是预期的。将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。有关详细信息,请参阅InnerException。

我再次使用ServiceKnownType ObjectHolderObjectHolder[]甚至ObjectHolder[][],因为例外提到“ArrayOfArrayOfKeyValueOfstringObjectHolder”。

还没有解决方案。

3 个答案:

答案 0 :(得分:8)

首先,您应该在app cofig文件中configure WCF tracing,它可以帮助您了解服务通信的内容。在这种情况下,您可以轻松获得通信过程中发生的所有错误。

现在,让我们尝试解决您的问题。我几乎对已知类型的问题充满信心。在面向服务的世界中,您应该手动定义可以参与服务契约的所有具体类型,因为DataContractSerializer不会在序列化对象中提供此类信息。

在这种情况下,它意味着您应该执行以下操作:

[ServiceKnownType(typeof(string))] 
[ServiceKnownType(typeof(Guid))]
[ServiceKnownType(typeof(int))] // but I think DataContractSerializer can deal himself with primitives
[ServiceKnownType(typeof(YourClass))] //UserDefined types you should add manually
public Dictionary<string, object> GetObject()

另外我建议您不要在服务合同中使用对象,因为它非常容易出错。考虑一下,您或您的同事稍后修改一行代码:

new Dictionary<string, object>()
{
 { "pty1", 4 },
 { "pty2", Guid.NewGuid() },
 { "pty3", new SomeClass() }, //OOPS!!!
}

在这种情况下,当您的服务尝试返回此字典时,您会遇到运行时故障,因为DataContractSerializer在此合同中不期望SomeClass。

解决此问题的一种方法是创建单独的类型:

[DataContract]
[KnownType(typeof(Guid))]
[KnownType(typeof(SomeClass1))]
[KnownType(typeof(SomeClass2))]
public class MyType
{
  private MyType(object obj) {
     Object = obj;
  }

  public static MyType FromSomeClass1(SomeClass1 c1) {
    return new MyType(c1);
  }

  public static MyType FromSomeClass2(SomeClass2 c2) {
    return new MyType(c2);
  }

  public static MyType FromGuid(Guid guid) {
    return new MyType(guid);
  }

  [DataMember]
  public object Object { get; private set; }
}

在这种情况下,如果要添加一些新类型,则应添加工厂方法和KnownTypeAttribute(此方法更详细,但不易出错)。

但是,如果您的客户端和服务是在WCF上编写的,那么您可以牺牲面向服务的主要原则并使用NetDataContractSerializer代替DataContractSerializer。 NetDataContractSerializer旨在补充DataContractSerializer。您可以使用NetDataContractSerializer序列化类型,并使用DataContractSerializer反序列化。但NetDataContractSerializer在序列化XML中包含CLR类型信息,而DataContractSerializer则不包含。因此,NetDataContractSerializer可以用于任何CLR类型的序列化和反序列化,而不需要任何KnownTypes。

答案 1 :(得分:0)

尝试定义一个具有单个属性的类。该属性是字符串,对象的字典。

使用DataContract / DataMember标记该类。然后使用该类定义您的界面。

答案 2 :(得分:0)

  

但是,我现在有一个场景,我需要其中一个值为字典数组!

问题是WCF不知道如何序列化/反序列化未标记DataMember属性和/或未添加到{{1的对象数组}}第

例如,如果有一些服务方法,则将任意对象作为参数

ServiceKnownType

并且你希望传递,比如一个整数数组public interface IService function DoSomething(Parameter as Object) as integer end interface ,你必须显式添加这个整数类型数组来为已知类型提供服务。

Integer()

然后可以调用服务方法

<ServiceKnownType(GetType(Integer()))>
public interface IService
    function DoSomething(Parameter as Object) as integer
end interface
  

任何想法如何解决?

尝试添加dim Service as IService dim Argument as Integer() Service.DoSomething(Argument) 属性。