Клиент WCF испытывает проблемы с распознаванием ServiceKnownTypes?

Как мне сообщить службе WCF, какие KnownTypes использовать при передаче данных клиенту?

Я знаю, что могу использовать[ServiceKnownType] атрибут, который заставляет вызов службы работать нормально с тестового сервера WCF, однако он все равно не выполняется с клиента. Я что-то здесь упускаю?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

Сообщение об ошибке от клиента:

{"Элемент" http://schemas.datacontract.org/2004/07/BaseClassZ "содержит данные из типа, который отображается на имя" http://schemas.datacontract.org/2004/07/SubClassA ". Десериализатор не знает ни одного типа, который сопоставляется с этим именем. Попробуйте использовать DataContractResolver или добавить тип, соответствующий SubClassA, в список известных типов, например, используя атрибут KnownTypeAttribute или добавив его в список известных типов. передано в DataContractSerializer. "}

Сериализация / десериализация объекта на сервере WCF с использованием DataContractSerializer и списка KnownTypes работает нормально.

ОБНОВИТЬ: Похоже, я могу заставить клиента правильно прочитать объект, если я добавлю атрибуты KnownType в базовый класс, но я все еще ищу способ обойти это, если это возможно, так как базовый класс используется для большого количества элементов, и я не Я хочу изменить атрибуты KnownType базового класса каждый раз, когда добавляю новый элемент.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
 Aardvark09 нояб. 2010 г., 18:30
Я вижу множество документации и примеров MSDN, которые, кажется, делают это возможным, но, черт побери, могу ли я заставить его работать! Добавление щедрости ...

Ответы на вопрос(3)

чтобы использовать «добавить ссылку на сервис», вы кодируете прокси-классы. Сначала это немного больше кода, но дает вам гораздо более стабильное и надежное решение. Мы обнаружили, что это экономит наше время в долгосрочной перспективе.

Увидеть:http://www.dnrtv.com/default.aspx?showNum=122

Примечание: это работает, только если у вас есть контроль над сервером и клиентом.

что, насколько я могу судить, является точно такой же проблемой. Решением для меня было использование метода AddGenericResolver из библиотеки ServiceModelEx IDesign.

ПРИМЕЧАНИЕ: .NET 4.0 требуется, поскольку он используетDataContractResolver

Вы можете найти это наСтраница загрузок IDesign.

Все, что мне нужно было сделать в моем случае, это добавить следующую строку кода:

Client.AddGenericResolver( typeof ( K2Source ) );

Я надеюсь, что это поможет кому-то еще сэкономить несколько часов!

Вы можете найти больше информации в книге Юваля Лоуи "Программирование сервисов WCF: освоение WCF и сервисная шина Azure AppFabric".

Решение Вопроса

поместите известные типы в 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 контракта данных, используемые здесь, совместно используются клиентом и сервером при импорте определения службы, метод, который возвращает коллекцию, не получает правильные атрибуты известного типа в сгенерированном клиентском прокси. Может быть, это по замыслу.

 Aardvark09 нояб. 2010 г., 21:44
Я загрузил проект, который воспроизводит мою проблему:jump.fm/BGCRU Клиент Silverlight, надеюсь, вы можете построить это. Я вижу, что «одиночный» вызов работает, «много» - нет, понятно почему в reference.cs, но не понятноЗачем это не генерирует это так !!
 Darin Dimitrov09 нояб. 2010 г., 19:38
Возможно, вы могли бы загрузить пример решения, иллюстрирующего проблему, чтобы я мог взглянуть на то, в чем может быть проблема, но обычно, если вы начнете с моего примера, проблем быть не должно.
 Darin Dimitrov09 нояб. 2010 г., 23:01
@ Aardvark, пожалуйста, смотрите мое ОБНОВЛЕНИЕ 2.
 Aardvark09 нояб. 2010 г., 18:48
Требуется ли классу BaseClassZ {...} [KnownType (typeof (SubClassA))] и [KnownType (typeof (SubClassB))] для вашего второго примера?
 Aardvark09 нояб. 2010 г., 19:11
Могу поспорить, что это как-то связано с тем, что я снова использую типы из общей сборки.
 Aardvark09 нояб. 2010 г., 19:59
(спасибо, ооочень большое BTW) Я думаю, что слишком быстро сдался с вашим последним примером, я думаю, что мне просто нужно правильно настроить параметры в app.config
 Darin Dimitrov09 нояб. 2010 г., 19:03
@ Aardvark, нет, это не так. Вы можете оформитьпример проекта Я положил, чтобы проиллюстрировать это.
 Aardvark09 нояб. 2010 г., 19:31
Я должен был уточнить. Я пытаюсь вернуть коллекцию объектов (IEnumerable <BaseClass>). Базовый класс и его производные типы существуют в собственной сборке, на которую ссылаются как клиент, так и сервер. Когда я добавляю клиентский прокси, он использует этот существующий тип, но не может десериализовать результаты в производные классы. (может быть, мне следует создать совершенно новый вопрос, так какЗачем я делаю это?)
 Darin Dimitrov09 нояб. 2010 г., 19:33
Вмой пример базовый и производный классы находятся в своей собственной сборке, на которую ссылается клиент (фактически это сама сборка службы). Разница лишь в том, что вы определяетеIEnumerable<BaseClass> в качестве типа возврата в то время как в моем примере я использую напрямуюBaseClass но это совершенно не связано. Если в моем примере вы замените его наIEnumerable<BaseClass> это все еще работает. Также очевидно, что у меня есть контракты на данные, сервисный контракт и реализация в одной сборке, что может быть не в вашем случае, но это не проблема.
 Aardvark10 нояб. 2010 г., 00:29
@Darin Dimitrov, если вы удалили вызов GetSingle, оставив только ссылку BaseClassZ в возвращаемом значении массива, (sl) svcutil по-прежнему будет генерировать сломанный клиентский прокси-сервер, независимо от того, куда я поместил объявления ServiceKnowType в интерфейсе. Это похоже на ошибку. Я могу добавить фиктивный параметр или целый метод, чтобы обмануть клиентский прокси для генерации правильного кода. Я могу записать инцидент поддержки MSDN / попытаться проколоть некоторые контакты в Редмонде по этому поводу.
 Darin Dimitrov09 нояб. 2010 г., 19:19
@ Аардварк, вотпример с общей сборкой. Обратите внимание, как известные типы настраиваются в app.config наклиент.
 Aardvark09 нояб. 2010 г., 22:20
Использование клиента приложения командной строки (не Silverlight), по-видимому, приводит к той же проблеме, если вы гарантируете ссылку на эту общую сборку перед добавлением ссылки на веб-службу.

Ваш ответ на вопрос