2013-06-17 2 views
6

Когда я вызываю метод моей WCF службы Soap, выдается ошибка и появляется сообщение об ошибке в файле svlog:Проблемы WCF с типами?

Тип «xxx.ActiveDirectoryService.classes.WCF.Message» с именем контракта данных «Message : http://schemas.datacontract.org/2004/07/xxx.ActiveDirectoryService.classes.WCF 'не ожидается. Подумайте об использовании DataContractResolver или добавьте любые типы, не известные статически в список известных типов - например, с помощью атрибута KnownTypeAttribute или путем добавления их в список известных типов, переданных DataContractSerializer.

Я попытался использовать KnownType здесь и там, но без успеха (я должен признать, что не уверен, что получил его использование на 100% вправо).

Вот мои Интерфейс/классы:

[ServiceContract] 
public interface IActiveDirectory 
{ 
    [OperationContract] 
    [WebGet] 
    void Dummy(); 

    [OperationContract] 
    [WebGet] 
    AbstractMessage Dummy2(); 

    [OperationContract] 
    [WebGet] 
    AbstractMessage Dummy3(); 

    [OperationContract] 
    [WebGet] 
    AbstractMessage SetPassWord(string customer, string customerPassword, string userLogin, string userPassword); 
} 

[DataContract] 
public abstract class AbstractMessage 
{ 
    [DataMember] 
    public virtual bool IsError { get; set; } 

    [DataMember] 
    public virtual string ErrorMessage { get; set; } 

    [DataMember] 
    public virtual string ReturnValue { get; set; } 
} 

public class Message : AbstractMessage 
{ 
<...> 
} 


[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] 
[KnownType(typeof(AbstractMessage))] 
public class ActiveDirectory : IActiveDirectory 
{ 
    public void Dummy() 
    { 
    } 

    public AbstractMessage Dummy2() 
    { 
     return new AbstractMessage(); 
    } 

    public AbstractMessage Dummy3() 
    { 
     return new Message(); 
    } 

    public AbstractMessage SetPassWord(string customer, string customerPassword, string userLogin, string userPassword) 
    { 
    <...> 

     return message; // message is of type Message 
    } 
} 

Edit: метод12AM35 GMT+1

Я добавил пустышки().

  • Вызов Dummy от клиента работает отлично.
  • Вызов Dummy2 от клиента работает отлично.
  • Вызов Dummy3 из клиента вызывает ту же ошибку.

Редактировать12AM39 GMT+1

внесении следующие изменения не помогло.

[DataContract] 
[KnownType(typeof(AbstractMessage))] 
public class Message : AbstractMessage 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] 
[KnownType(typeof(AbstractMessage))] 
[KnownType(typeof(Message))] 
public class ActiveDirectory : IActiveDirectory 

Edit:13AM31 GMT+1

Если установить Dummy3 тип возвращаемого на сообщение, вызовы Dummy3 в коде клиента работы.
Есть что-то странное с WCF + Полиморфизм ...

ответ

3

Вам необходимо, чтобы службы WCF возвращали DTO.Они не могут быть интерфейсами, и если вы хотите вернуть абстрактные базовые классы, тогда вам нужно будет сообщить сериализатору данных, который реализует те типы, которые вы намереваетесь вернуться.

Он уже знает о типе AbstractMessage (как о его части договора операции), но все реализации этого типа, которые явно не объявлены в контракте на операцию, должны быть объявлены в известных типах.

попробуйте добавить следующее:

[ServiceContract] 
[ServiceKnownType(typeof(Message))] 
public interface IActiveDirectory 
{ 
    ... 
} 

Здесь вы сообщаете контракт serialiser данных, что эта услуга может вернуться (или ожидать) объектов типа Message в качестве аргументов своих методов.

и это должно работать, а также:

[DataContract] 
[KnownType(typeof(Message))] 
public abstract class AbstractMessage 
{ 
    ... 
} 

, как вы хотите сказать, контракт данных serialiser, что Message является известным типом AbstractMessage

Я считаю, что ваши изменения не работают, как раньше KnownTypes в сервисе вместо ServiceKnownTypes, и вы попытались применить известный тип к производному классу, а не к родительскому классу, что и используется вами на этом языке (Message is AbstractMessage), но в WCF вам нужно перевернуть его over и поместить производные реализации в родительский класс (AbstractMessage h как сообщение реализации), которое может быть ограничительным.

вы сказали: There 's something odd with WCF + Polymorphism...

доверие мне, его лучше не думать о WCF, как поддержка polymophism, так как она не делает. Вы просто возвращаете DTO, и если вы хотите попробовать и сделать эти полиморфные, вы столкнетесь с множеством проблем. Полиморфизм реализуется с использованием интерфейсов, и вы не можете использовать интерфейсы в WCF. Если вы используете абстрактные классы, то из-за отсутствия множественного наследования в C# вы скоро поймете, что DTO может представлять только один вид вашего объекта, и вы не можете создать DTO, который представляет несколько интерфейсов класса модели домена. (См this question по данному вопросу)

Смотрите мой answer here подробности о том, как вы можете передать на знание известных типов, либо через KnownTypes, ServiceKnownTypes или конфигурации.

+0

[KnownType (typeof (Message))] отлично работает, но мне пришлось обновить ссылки моего клиента (чего я не делал до нескольких минут назад). – Serge

0

У меня была эта проблема один раз. В моем случае, и, насколько я смог узнать, wcf-сериализаторы требуют не-абстрактных типов для работы. Поэтому в моем случае переход от абстрактных классов и интерфейсов к нормальным объектам заставлял его работать.

+0

Я пробовал то, что вы сказали, но это не помогло. – Serge

3
[KnownType(typeof(AbstractMessage))] 

Компилятор уже понял, что вы возвращаете AbstractMessage. То, что он не может понять из описания, - это экземпляры, которые получены из AbstractMessage. Это те типы, которые вам нужно знать. Любой тип, который вы получили из AbstractMessage, и вы возвращаетесь туда, должен быть известным типом, и это должно быть объявлено на самом абстрактном сервере.

[KnownType(typeof(DerivedMessage1))] 
[KnownType(typeof(DerivedMessage2))] 
[KnownType(typeof(DerivedMessage3))] 

В вашем случае, вы должны атрибут сказать контракт данных сериалайзера: «Эй, как вы можете видеть, что я вернусь к AbstractMessage, но то, что вы не можете знать: на самом деле, это Message»:

[ServiceBehavior(...)] 
[KnownType(typeof(Message))] 
public class ActiveDirectory : IActiveDirectory 
{ 
    public AbstractMessage Dummy3() 
    { 
     return new Message(); 
    } 
} 
+0

Не могли бы вы проверить мои изменения в 12:39, потому что я отражаю то, что я понял из вашего ответа. – Serge

+0

Все выглядит нормально. Вам никогда не понадобится AbstractMessage как известный тип, потому что этот тип можно вывести из подписи метода. Вам нужно только атрибут KnownType в классе службы, когда он обрабатывает типы, которые не входят в пространство имен System или в подпись самого метода. – nvoigt

+0

Это не работает. – Serge

Смежные вопросы