Служба WCF, возвращающая массив словаря <string, object> - PullRequest
6 голосов
/ 29 января 2010

Я пытался использовать клиент 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 ]

8 голосов
/ 30 января 2010

Прежде всего вы должны настроить трассировку 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.

0 голосов
/ 31 мая 2015

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

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

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

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)()))>.

0 голосов
/ 30 января 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...