WCF: интерфейсы, дженерики и обслуживание.
-
21-09-2019 - |
Вопрос
У меня есть следующее:
[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(SportProgram))]
[ServiceKnownType(typeof(ActionResult<SportProgram>))]
public interface ISportProgramBl
{
[OperationContract]
IActionResult<ISportProgram> Get(IActionParameters parameters);
}
Когда я запускаю метод получения, я получаю следующую ошибку:
Была ошибка при попытке сериализовать параметр http://tempuri.org/:getresult. Анкет Сообщение innerexception было «тип» pps.core.domainmodel.support.action.actionResult`1 [[pps.core.domainmodel.sportprogram.isportprogram, pps.core.domainmodel, версия = 1,0.0.0, культура = нейтральная, publickeytoken = null]] 'с именем договора данных' actionResultOfAnyType: http://schemas.datacontract.org/2004/07/pps.core.domainmodel.support.action'не ожидается. Добавьте любые типы, не известные статически в список известных типов - например, с использованием атрибута известных типовпийт или добавления их в список известных типов, передаваемых DataContractSerializer. '. Пожалуйста, смотрите Innerexception для получения более подробной информации.
Из этой ошибки я вижу, что она может разрешить действие, но не может разрешить IsportProgram, хотя у меня есть Service Skellype (typeof (ActionResult <portprogram>)) На моем сервисном интерфейсе ...
Примечание Это образованная эталонная заглушка выглядит так, поэтому я вижу, что известные типы приводятся правильно:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SportProgramStb.ISportProgramBl")]
public interface ISportProgramBl {
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISportProgramBl/Get", ReplyAction="http://tempuri.org/ISportProgramBl/GetResponse")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.SportProgram.SportProgram))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionParameters))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionResult<PPS.Core.DomainModel.SportProgram.SportProgram>))]
object Get(object parameters);
}
Почему это идет не так ???? Обратите внимание, что он проходит через службу WCF правильно ... но он бросает исключение, когда результат возвращается.
Наконец, ActionResult выглядит так:
public interface IActionResult<T>
{
T Result { get; set; }
}
Ура Энтони
Решение
Ну, я думаю, что это еще один случай, когда SOA против OOP «несоответствие импеданса». Два мира довольно отдельные.
В WCF все, что передается от клиента на сервер, передается как сериализованные сообщения - Ссылки не используются.
Это означает: все, что вы хотите сериализовать на клиенту, отправить его на сервер, десериализировать и использовать его там, должно быть конкретный - Вы не можете передать интерфейсы, вы не можете использовать «не разрешенные» дженерики - вам нужно изложить это. По сути, все, что передается от клиента через проволоку на сервер, должно быть выражено в схеме XML.
Это имеет много последствий:
- Нет интерфейсов - вы не можете передать интерфейсы - вам нужно работать с конкретными типами
- Нет «автоматического» наследования - вы не можете просто определить базовый класс и передать полученные классы на основе его - тех, кто также должен быть специфичен (это то, для чего предназначен атрибут службы.
- Нет автоматических дженериков - опять же, вам нужно использовать конкретные типы
Это может звучать как множество ограничений - но это потому, что WCF использует все общение на основе сообщений - оно не может иметь дело с ссылками, наследством, дженериками и т. Д. - вам нужно это изложить.
Так что у меня действительно нет ответа для вас как такового - я просто думаю, что вам нужно переосмыслить свою стратегию и изменить способ обмена информацией вашего клиента и сервера.
Марк
PS: Я провел еще несколько исследований, и вопреки всем, что мое понимание, кажется, есть способ сериализовать все, что основано на интерфейсе и/или абстрактном базовом классе по всему проволоку, если вы можете быть уверены, что это всегда. Сеть на любом конце провода (т.е. нет совместимый с EG Java).
Видеть Пост в блоге Aaron Skonnard на NetDataContractSerializer и другой Сообщение блога а также еще один показывая, как использовать NetDataContractSerializer, чтобы иметь возможность передать такие вещи, как IPerson
как параметры для ваших методов.
Другие советы
Это одна из проблем, с которыми я решаю ServiceStack.net - Мой открытый исходный код .NET и Mono Web Services Framework.
Служебный стек находился под сильным влиянием Martin Fowlers Permoning Pattern Объект объекта Поскольку это позволяет вам просто использовать DTO для определения ваших веб -сервисов - т.е. SOA Way :).
Я избегаю этого ограничения, которое присуще WCF, генерируя свои собственные WSDL, которые ведут себя так, как вы ожидаете. В качестве преимущества замены сложной конфигурации WCF / модели ServiceContract - SOAP Web Services также работает на Mono - Посмотрите на живую демонстрацию.
Это старый вопрос, и хотя принятый ответ абсолютно правильный, я наступил на это в поисках аналогичной проблемы и подумал, что смогу поделиться своим опытом. Это часто головная боль, но можно использовать дженерики в сочетании с интерфейсами с WCF. Вот рабочий пример другой (аналогичной) реализации, которую я сделал:
[ServiceContract]
[ServiceKnownType(typeof(CollectionWrapper<IAssociation>))]
public interface IService :
{
[OperationContract]
ICollectionWrapper<IAssociation> FindAssociation(string name, int pageSize, int page);
}
public interface ICollectionWrapper<TModel>
{
int TotalCount { get; set; }
IEnumerable<TModel> Items { get; set; }
}
[KnownType(typeof(OrganizationDto))]
[KnownType(typeof(CompanyDto))]
public class CollectionWrapper<TModel> : ICollectionWrapper<TModel>
{
[DataMember]
public int TotalCount { get; set; }
[DataMember]
public IEnumerable<TModel> Items { get; set; }
}
public class CompanyDto : IAssociation
{
public int Id { get; set; }
public string Name { get; set; }
}
public class OrganizationDto : IAssociation
{
public int Id { get; set; }
public string Name { get; set; }
}
Ключ здесь - использовать комбинацию KnownType
а также ServiceKnownType
.
Так что в вашем случае вы можете сделать что -то вроде этого:
[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(ActionResult<ISportProgram>))] // Actual implementation of container, but interface of generic.
public interface ISportProgramBl
{
[OperationContract]
IActionResult<ISportProgram> Get(IActionParameters parameters);
}
[KnownType(typeof(SportProgram))] // Actual implementation here.
public class ActionResult<T>
{
// Other stuff here
T FooModel { get; set; }
}
Это будет работать, если у вас есть общий контракт (доступ к фактическому интерфейсу обслуживания) и потребляет контракт с ChannelFactory<ISportProgramBl>
. Анкет Я не знаю, работает ли это с сервисным ссылком.
Однако, похоже, есть некоторые проблемы с реализацией, как упомянуто здесь:
WCF с интерфейсом и общей моделью
И еще один похожий вопрос задал и ответил здесь:
Вы возвращаете илиста T. Именно в том, что у системы есть проблемы, выясняющие, что такое.
Не уверен, можно ли вернуть интерфейс, а не тип.
Вы указываете интерфейсы в объектах, а не конкретных типах?
PPS.Core.DomainModel.Support.Action.ActionListResult<IList<PPS.Core.DomainModel.SportProgram.ISportProgram>>
Редактировать:
Я говорю, что это все конкретные типы, которые вы передаете в генериках (в том числе в субъекте через интерфейсы), передаваемые в списке известных типов. У нас были проблемы с последовательностью, где все типы не были известны.