Служба WCF, возвращающая массив словаря<string, object>

Я пытался использовать клиент SilverLight для вызова службы ASP.Net WCF, которая будет возвращать Dictionary<string, object>, Это работало хорошо, когда значения в словаре были простыми типами, такими как int, string или же Guid,

Тем не менее, теперь у меня есть сценарий, где мне нужно одно из значений, чтобы быть массивом Dictionary<string, object>! Все это прекрасно компилируется, и подпись службы не изменилась, но теперь вызов службы не выполняется.

Есть идеи как это исправить? Я пытаюсь аннотировать мой класс обслуживания и методы с KnownType а также ServiceKnownType атрибуты, но это не сработало.

Вот кусок кода:

[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 было "Тип" System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Версия =2.0.0.0, Культура = нейтральный, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Версия =2.0.0.0, Culture= нейтральный, 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 было "Тип" System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Версия =2.0.0.0, Culture = нейтральный, PublicKeyToken=b77a5c561934e089],[SilverlightApplication7.Web.ObjectHolder, SilverlightApplication7.Web, Версия =1.0.0.0, Культура = нейтральная, PublicKeyToken=null]][]'с именем контракта данных' ArrayOfArrayOfKeyValueOfstringObjectHolderWAwxSTlb: http://schemas.microsoft.com/2003/10/Serialization/Arrays'не ожидается. Добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, передаваемых в DataContractSerializer.'. Пожалуйста, смотрите InnerException для более подробной информации.

Я снова играл с ServiceKnownType за ObjectHolder, ObjectHolder[] и даже ObjectHolder[][] поскольку исключение упоминает "ArrayOfArrayOfKeyValueOfstringObjectHolder".

Все еще нет решения.

3 ответа

Прежде всего вы должны настроить трассировку WCF в файле приложения cofig, который может помочь вам понять, что происходит под капотом служебных коммуникаций. В этом случае вы можете легко получить все ошибки, возникающие в процессе общения.

Теперь давайте попробуем решить вашу проблему. Я почти уверен, что проблема в известных типах. В сервис-ориентированном мире вы должны вручную определить все конкретные типы, которые могут участвовать в контракте на обслуживание, потому что 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 включает информацию о типе CLR в сериализованный XML, а DataContractSerializer - нет. Поэтому NetDataContractSerializer может использоваться при сериализации и десериализации с любыми типами CLR без каких-либо вещей KnownTypes.

Попробуйте определить класс, который имеет единственное свойство. Это свойство представляет собой словарь строки, объекта.

Пометьте класс с помощью DataContract / DataMember. Затем определите ваш интерфейс, используя этот класс.

Однако теперь у меня есть сценарий, в котором мне нужно, чтобы одно из значений было массивом словаря!

Проблема в том, что WCF не знает, как сериализовать / десериализовать массив объектов, которые не помечены DataMember атрибут и / или не добавлен в ServiceKnownTypes.

Например, если есть какой-то сервисный метод, который принимает произвольный объект в качестве параметра

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)

Есть идеи как это исправить?

Попробуйте добавить <ServiceKnownType(GetType(Dictionary(Of String, Object)()))> приписывать.

Другие вопросы по тегам