WCF: возможна ли сериализация универсальных интерфейсов? - PullRequest
6 голосов
/ 26 июля 2010

Я пытаюсь реализовать контракт на обслуживание, который содержит метод, который принимает универсальный интерфейс, и самому универсальному интерфейсу присваивается параметр интерфейса. Я украсил интерфейс сервиса с помощью ServiceKnownType, я украсил реализацию сервиса с помощью обычного KnownType, и я украсил реализацию сервиса обработки данных с помощью обычного KnownType:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallbacks))]
[ServiceKnownType(typeof(Batch<object>))]
[ServiceKnownType(typeof(Command))]
public interface IActions
{
    [OperationContract]
    IResponse TakeAction(IBatch<ICommand> commands);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
[KnownType(typeof(Batch<object>))]
[KnownType(typeof(Command))]
internal class Actions : IActions
{
}

[DataContract]
[KnownType(typeof(Command))]
public class Batch<T> : IBatch<T>
{
}

Для справки, у меня есть Batch там, потому что кажется, что вы можете выразить ноу-тип для универсального типа только один раз - похоже, он испускает BatchOfanyType, но я не уверен, как с этим справиться.

Исключение, которое я получаю: «Добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, передаваемых в DataContractSerializer».

Есть что-нибудь очевидное, что я делаю не так? Общие интерфейсы интерфейсов просто не поддерживаются? Для записи я нахожусь на C # 2.0 и .NET 3.0 для этого проекта.

Ответы [ 4 ]

12 голосов
/ 26 июля 2010

Вы можете использовать интерфейсы в определениях контрактов на обслуживание, если вы действительно хотите, если вы включаете известные типы, как вы делаете (с небольшой корректировкой, см. Ниже).

Очевидно, что использование интерфейса в качестве параметра универсального типа приводит к тому, что это слишком далеко для C # 3.0. Я изменил атрибут известного типа на

[ServiceKnownType(typeof(Batch<Command>))]
public interface IActions
{
}

Что заставляет это работать, в точку. Сериализация и десериализация сами по себе будут работать, но тогда вы столкнетесь с этим исключением:

Невозможно привести объект типа 'Batch`1 [Command]'. набрать 'IBatch`1 [ICommand]'.

Для этого приведения в действие вам нужна языковая поддержка ковариации универсального типа, что было введено в C # 4.0. Чтобы это работало в C # 4.0, вам нужно добавить модификатор дисперсии:

public interface IBatch<out T>
{
}

Тогда это работает отлично ... к сожалению, вы не используете C # 4.0.

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

2 голосов
/ 26 июля 2010

WCF - это система, основанная на сообщениях SOA - она ​​может передавать по сети все, что угодно, в сериализованном формате XML, которое может быть выражено в схеме XML.

К сожалению, XML-схема не знает ничего, ни интерфейсов, ни обобщений, поэтому нет - вы не можете обобщенно их сериализовать - вам нужно использовать конкретные типы.

1 голос
/ 27 июля 2010

Обобщения могут быть сериализованы, но с некоторыми ограничениями. Например, учитывая данные контракта:

[DataContract]
public class Foo<T>
{
     [DataMember]
     public T Value { get; set; }
}

И договор на обслуживание:

[ServiceContract]
public interface IService1
{
     [OperationContract]
     Foo<String> GetData();
}

И реализация сервиса:

public class Service1 : IService1
{
   public Foo<string> GetData()
   {
       return new Foo<string>() { Value = "My test string" };
   }
}

После установки служебной ссылки на вышеуказанную услугу можно запустить этот код:

ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();

ServiceReference1.FooOfstring temp = client.GetData();

MessageBox.Show(temp.Value);

И появится окно с сообщением «Моя тестовая строка».

Обратите внимание, что сама служба не является общей, но используется контракт с данными. Кроме того, контракт данных, сгенерированный на стороне клиента, является не универсальным, а скорее «плоским» классом, который имеет значение свойства типа string:

[System.Runtime.Serialization.DataMemberAttribute()]
public string Value 
{ 
   get {...} 
   set {...} 
}
1 голос
/ 26 июля 2010

Вы не можете сериализовать интерфейс.Интерфейс просто определяет контракт, а не объект.Я предполагаю, что единственным исключением является интерфейс ISerializable.

...