Как указать в общей конфигурации известный тип WCF?

У меня есть типаж, назовем его Data<TKey>. У меня также есть контракт службы WCF, который принимает тип (назовем его Wrapper) со свойством типа Object (по причинам, которые я не буду вдаваться в подробности, это не обязательно).

[DataContract]
public class Data<TKey> { ... }

[DataContract]
public class Wrapper
{
    [DataMember]
    public object DataItem { get; set; }
}

Прямо сейчас отправляю два класса IntData и LongData:

[DataContract]
public class IntData : Data<int> { /*empty*/ }

[DataContract]
public class LongData : Data<long> { /*empty*/ }

Оба они настроены в конфигурационном файле известных типов. Конфигурация выглядит примерно так:

<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Wrapper, TheirAssembly">
          <knownType type="IntData, MyAssembly"/>
          <knownType type="LongData, MyAssembly"/>
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

На данный момент все работает нормально.

Но я собираюсь добавить третий тип, и мне не нравятся ненужные, пустые классы .NET IntData и LongData. Они существуют только потому, что ...

Я не знаю, как указать универсальные типы в конфигурации WCF!

Я хочу сделать что-то подобное, но не знаю точного синтаксиса.

<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Wrapper, TheirAssembly">
          <!-- this syntax is wrong -->
          <knownType type="Data{System.Int32}, MyAssembly"/>
          <knownType type="Data{System.Int64}, MyAssembly"/>
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Каков правильный синтаксис для этого?

(Также обратите внимание, что я не могу использовать атрибуты [KnownType(...)] в Wrapper, поскольку это не мой тип. Конфигурация кажется единственным способом.)

ИЗМЕНИТЬ

Ответ @bartta работал хорошо. Однако обратите внимание, что изначально я получил эту ошибку:

Тип 'MyAssembly.Data`1 [System.Int64]' нельзя добавить в список известных типов, поскольку другой тип 'MyAssembly.Data`1 [System.Int32]' с тем же именем контракта данных 'http://www.mycompany.com/MyAssembly:Data 'уже присутствует.

Я не упомянул об этом в исходном вопросе, но мой тип имеет явное имя контракта данных. Что-то вроде этого:

[DataContract(Name = "Data")]
public class Data<TKey> { ... }

Вышеупомянутая ошибка возникала до тех пор, пока я не удалил значение свойства Name из атрибута. Надеюсь, что это тоже поможет кому-то другому. Я не знаю, какой формат работает в этом сценарии. Этого не произошло:

[DataContract(Name = "Data\`1")]
[DataContract(Name = "Data{TKey}")]

Кто-нибудь знает, как это сделать?

ИЗМЕНИТЬ 2

Еще раз спасибо @baretta, указавшему, что на самом деле правильный синтаксис:

[DataContract(Name = "Data{0}")]

person Drew Noakes    schedule 10.06.2009    source источник
comment
Да, знаю! :) Отредактировал свой ответ   -  person baretta    schedule 03.07.2009


Ответы (2)


Универсальный тип может быть создан из строки, если строка соответствует следующему шаблону: Имя класса, за которым следует символ "", за которым следует количество параметров типа (в данном случае это 1), за которым следуют параметры типа, заключенные в него внутри "[]" и с использованием запятой в качестве разделителя параметров типа.

<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Wrapper, TheirAssembly">
          <!-- this syntax is all good -->
          <knownType type="Data`1[System.Int32], MyAssembly"/>
          <knownType type="Data`1[System.Int64], MyAssembly"/>
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Изменить: я мог бы также добавить, что если необходимо указать информацию о сборке для параметров типа (хотя это не относится к материалам в mscorlib), тогда используется вложенный «[]».

<knownType type="Data`1[[System.Int32, mscorlib]], MyAssembly"/>

Изменить: вы можете настроить имена общих типов в контрактах данных, используя шаблон строкового формата.

[DataContract(Name = "Data{0}")]
public class Data<TKey>
{...}

По умолчанию имя, сгенерированное для типа Data ‹Int32>, выглядит как «DataOfInt32HJ67AK7Y», где «HJ67AK7Y» - это хэш, сгенерированный из строки «urn: default» или пространства имен вашего класса, если оно у вас есть. Но «Data {0}» даст ему имя «DataInt32».

Подробнее см. здесь. Взгляните на раздел «Настройка имен контрактов данных для универсальных типов» внизу страницы.

person baretta    schedule 10.06.2009
comment
Спасибо, Баретта. Этот краткий ответ сработал отлично, как только я разобрался с проблемой с атрибутом name (см. Мою правку). - person Drew Noakes; 03.07.2009
comment
Спасибо за ваше редактирование. Это отличный ответ, и если бы я мог проголосовать за него дважды, я бы :) - person Drew Noakes; 04.07.2009
comment
Небольшой совет: когда вы идете по этому пути, убедитесь, что регистрируемый тип является общедоступным. Это был момент, когда мы поняли, почему это решение не работает из коробки. - person Tedford; 23.08.2013

Из здесь ...

Известные типы также могут быть определены в конфигурации, как показано ниже.

<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
         <add type="MyCompany.Library.Shape`1,
              MyAssembly, Version=2.0.0.0, Culture=neutral,
              PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
            <knownType type="MyCompany.Library.Circle`1,
                       MyAssembly, Version=2.0.0.0, Culture=neutral,
                       PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
                    <parameter index="0"/>
            </knownType>
         </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Приведенная выше конфигурация указывает, что универсальный параметр для Circle совпадает с универсальным параметром для объявленного типа Shape. Конфигурация позволяет определять известный тип произвольной сложности. Например, если необходимо определить Circle ‹Dictionary‹ string, T >> как известный тип Shape ‹T> (конечно, это чисто академический характер), это можно сделать следующим образом.

<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
         <add type="MyCompany.Library.Shape`1,
              MyAssembly, Version=2.0.0.0, Culture=neutral,
              PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
            <knownType type="MyCompany.Library.Circle`1,
                       MyAssembly, Version=2.0.0.0, Culture=neutral,
                       PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
                   <parameter type="System.Collections.Generic.Dictionary`2">
                      <parameter type="System.String"/>
                      <parameter index="0"/>
                   </parameter>                
            </knownType>
         </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Обратите внимание на использование элемента конфигурации «параметр» с атрибутами «тип» и «индекс».

person Steve Dignan    schedule 10.06.2009