У клиента WCF возникли проблемы распознавания сервисныхknowntypes?
-
27-09-2019 - |
Вопрос
Как бы я сообщить WCF Service, что известно, чтобы использовать при прохождении данных обратно к клиенту?
Я знаю, что могу использовать [ServiceKnownType]
Атрибут, который заставляет вызов сервиса работать штрафом с тестового сервера WCF, однако он все еще не удается от клиента. Я что-то упускаю здесь?
[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();
Сообщение об ошибке от клиента:
{«Элемент» http://schemas.datacontrtract.org/2004/07/baseclassz "содержит данные из типа, который отображает название« http://schemas.datacontract.org/2004/07/subclassa ». Deserializer Не знает о любом типе, который отображает это имя. Рассмотрим использование DataconTractresolver или добавить тип, соответствующий «подклассию» к списку известных типов - например, с помощью атрибута SEVELYBEEPEATTRIBUTE или добавлением его в список известных типов передается на datacontractserializer. "}
Сериализация / десериализация объекта на сервере WCF с использованием DataContractserializer и список известных пятен работает нормально.
ОБНОВИТЬ: Похоже, что я могу заставить клиента правильно прочитать объект, если я добавляю атрибуты известных пятен в базовый класс, но я все еще ищу об этом, если это возможно, по возможности, поскольку базовый класс используется для многих предметов, и я не Хотите изменить атрибуты SewiveType на базовом классе в любое время добавляете новый элемент.
[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ
{
...
}
Решение
Чтобы избежать сдерживания вашего сервисного кода, поместите известные типы в Web.config службы:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="SomeNs.BaseClassZ, SomeAssembly">
<knownType type="SomeNs.SubClassA, SomeAssembly" />
<knownType type="SomeNs.SubClassB, SomeAssembly" />
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Если вы хотите сделать это по коду, вам нужно использовать этот атрибут на сервисном интерфейсе, а не на методе работы, но я бы предпочел декларативный подход:
[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
[OperationContract]
BaseClassZ GetObject();
}
ОБНОВИТЬ:
Я поставил Образец проекта иллюстрирующее использование Web.config для настройки известных типов, которые являются моим предпочтительным подходом. И другой Образец проекта демонстрируя второй подход.
Обновление 2:
После того, как посмотрел на обновленный код с помощью клиента приложения Silverlight, мы замечаем следующее определение:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {
[System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);
System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);
[System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);
MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}
Обратите внимание, как BeginGetSingle
метод содержит известные атрибуты типа, когда BeginGetMany
Метод не. На самом деле эти атрибуты должны быть размещены на определении обслуживания, чтобы класс выглядел так.
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
[System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);
System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);
[System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);
MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}
Как это автогенеративный класс, может быть ошибка в Slsvcutil.exe. а также svcutil.exe
Как это проявляет то же поведение. Помещение известных атрибутов типа на их правильное место решает проблему. Проблема в том, что этот класс аутогенерируется инструментом, и если вы пытаетесь восстановить его из WSDL, он снова будет запущен.
Так кажется, если у вас есть следующее определение сервиса:
[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
[OperationContract]
BaseClassZ[] GetMany();
[OperationContract]
BaseClassZ GetSingle();
}
И, используемые здесь 3 договора данных, передаются между клиентом и сервером при импорте определения службы, метод, который возвращает коллекцию, не получает правильные атрибуты известных типов в сгенерированном клиентском прокси. Может быть, это по дизайну.
Другие советы
Я провел часы сегодня на том, что, как лучше, я могу сказать, является той же проблемой. Для меня решение было использовать метод AddGenericResolver от библиотеки сервопривода Idesignerex.
Примечание: .NET 4.0 Требуется, как он использует Datacontractresolver.
Вы можете найти это на IDesign Загрузки Страница.
Все, что мне нужно было сделать в моем случае, было добавить следующую строку кода:
Client.AddGenericResolver( typeof ( K2Source ) );
Я надеюсь, что это поможет кому-то еще, сэкономить несколько часов!
Вы можете найти больше информации в Книге «Программирование WCF Services: Овладение WCF и Azure AppFabric Service Service», Juval Lowy
Есть еще один способ сделать это. Вместо того, чтобы использовать «Добавить ссылку на обслуживание», вы код прокси-классов. Это немного больше кодирования изначально, но дает вам гораздо более стабильное и надежное решение. Мы обнаружили, что это экономит время в США в долгосрочной перспективе.
Видеть: http://www.dnrtv.com/default.aspx?shownum=122.
Примечание. Это только работает, если у вас есть контроль над сервером, так и клиентом.