Я пытаюсь реализовать контракт на обслуживание, который содержит метод, который берет универсальный интерфейс, и что самому универсальному интерфейсу дают интерфейсный параметр. Я украсил сервисный интерфейс ServiceKnownType, я украсил реализацию услуги обычным KnownType, и я украсил datacontract реализацию обычным 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>
{
}
Для записи у меня есть Пакет там, потому что кажется, что можно только выразить knowntype для универсального типа однажды - это, кажется, испускает BatchOfanyType, но я не уверен, как обработать это.
Исключение, которое я получаю, - то, " Добавьте, что любые типы, не известные статически списку известных типов - например, при помощи атрибута KnownTypeAttribute или путем добавления их к списку известных типов, передали DataContractSerializer".
Действительно ли там что-нибудь очевидно, что я делаю неправильно? Универсальные интерфейсы интерфейсов просто не поддерживаются? Для записи я нахожусь на C# 2.0 и.NET 3.0 для этого проекта.
Вы можете использовать интерфейсы в определениях контрактов на обслуживание, если действительно хотите, при условии, что вы включаете известные типы, как вы это делаете (с небольшой корректировкой, см. Ниже).
По всей видимости, использование интерфейса в качестве параметра универсального типа слишком далеко отходит от 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.
И последнее, что касается использования интерфейсов в контракте службы: если вы генерируете ссылку на службу из них, он будет вводить все параметры интерфейса как объект
, поскольку исходный тип интерфейса не является частью метаданных. Вы можете обмениваться контрактами через ссылку на сборку или вручную реорганизовать сгенерированный прокси, чтобы исправить это, но в целом использование интерфейсов с WCF, вероятно, больше проблем, чем оно того стоит.
WCF - это система, основанная на сообщениях SOA - она может посылать что угодно по проводам в сериализованном формате XML, который может быть выражен в XML-схеме.
К сожалению, XML-схема не знает ни интерфейсов, ни дженериков, так что нет - вы не можете сериализовать их в общем виде - вам нужно использовать конкретные типы.
Вы не можете сериализовать интерфейс. Интерфейс просто определяет контракт, а не объект. Думаю, единственным исключением из этого правила является интерфейс ISerializable.
Универсальные шаблоны можно сериализовать, но с некоторыми ограничениями. Например, с учетом контракта данных:
[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);
И окно сообщения с "Моя тестовая строка" отображается.
Обратите внимание, что сама служба не является универсальной, но используется контракт данных. Кроме того, контракт данных, сгенерированный на стороне клиента, не является универсальным, а скорее является «сплющенным» классом, который имеет значение свойства типа строка:
[System.Runtime.Serialization.DataMemberAttribute()]
public string Value
{
get {...}
set {...}
}