2013-06-17 7 views
6

Kiedy wywołać metodę mojego WCF usługi mydło, zostanie zgłoszony błąd i pojawia się błąd w pliku svlog:Problemy WCF z typami?

Type „xxx.ActiveDirectoryService.classes.WCF.Message” z nazwą zamówienia dane „Wiadomość : http://schemas.datacontract.org/2004/07/xxx.ActiveDirectoryService.classes.WCF "nie oczekuje się. Rozważ użycie DataContractResolver lub dodaj dowolne typy nieznane statycznie do listy znanych typów - na przykład za pomocą atrybutu KnownTypeAttribute lub dodając je do listy znanych typów przekazanych do DataContractSerializer.

Próbowałem używać KnownType tu i tam, ale bez powodzenia (muszę przyznać, nie jestem zbyt pewny, że jego użycie jest w 100% prawidłowe).

Oto moje interfejsu/klasy:

[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

dodałem Dummy() metoda.

  • Przywołanie manekina od klienta działa poprawnie.
  • Wywołanie Dummy2 od klienta działa poprawnie.
  • Wywołanie Dummy3 z klienta powoduje ten sam błąd.

Edit12AM39 GMT+1

dokonać następujących zmian nie pomogło.

[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

Jeżeli ustawić Dummy3 typ zwracany do wiadomości, wzywa do Dummy3 w pracy kodu klienta.
Coś dziwnego z WCF + polimorfizmem ...

Odpowiedz

3

Usługi WCF muszą zwrócić DTO.Nie mogą to być interfejsy, a jeśli chcesz zwrócić abstrakcyjne klasy bazowe, musisz podać operatorowi seriali danych, które implementacje typów faktycznie chcesz zwrócić.

Już wie o typie AbstractMessage (jako części umowy o operację), ale wszystkie tego typu implementacje, które nie są jawnie zadeklarowane w umowie operacyjnej, powinny być zadeklarowane w znanych typach.

warto dodać to:

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

Tutaj mówisz zamówieniu serialiser danych, usługa ta może zwrócić (lub oczekiwać) obiektów typu Message jako argumenty do jego metod.

i to powinno działać, a także:

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

jak chcesz powiedzieć serialiser umowne dane Message jest znanym rodzajem AbstractMessage

wierzę zmiany nie działa jak dawniej KnownTypes w usłudze zamiast ServiceKnownTypes i próbowałeś zastosować znany typ w klasie pochodnej zamiast w klasie nadrzędnej, do której jesteś przyzwyczajony w języku (Message is a AbstractMessage), ale w WCF musisz go odwrócić over i wstaw pochodne implementacje do klasy nadrzędnej (AbstractMessage h jako komunikat implementacji), który może być restrykcyjny.

mówiłeś: There 's something odd with WCF + Polymorphism...

uwierzcie mi, że lepiej nie myśleć o WCF jako wsparcie polymophism, ponieważ nie robi. Po prostu zwracasz DTO, a jeśli chcesz spróbować zrobić te polimorficzne, natkniesz się na wiele problemów. Polimorfizm jest implementowany za pomocą interfejsów i nie można używać interfejsów w WCF. Jeśli używasz klas abstrakcyjnych, to ze względu na brak wielokrotnego dziedziczenia w języku C# wkrótce zrozumiesz, że DTO może reprezentować tylko pojedynczy widok twojego obiektu i nie możesz utworzyć DTO, który reprezentuje wiele interfejsów klasy modelu domeny. (Patrz this question na ten temat)

Zobacz moją answer here o szczegóły w jaki sposób można przekazać wiedzy znanych typów, zarówno poprzez KnownTypes, ServiceKnownTypes lub konfiguracji.

+0

[KnownType (typeof (Message))] działa dobrze, ale musiałem zaktualizować referencje mojego klienta (czego nie zrobiłem aż kilka minut temu). – Serge

0

Raz miałem ten problem. W moim przypadku i o ile mogłem się dowiedzieć, serializatory wcf wymagają typów nie abstrakcyjnych do pracy propery. Tak więc w moim przypadku przejście z klas abstrakcyjnych i interfejsów do normalnych obiektów sprawiło, że zadziałało.

+0

Próbowałem, co powiedziałeś, ale to nie pomogło. – Serge

3
[KnownType(typeof(AbstractMessage))] 

Kompilator już zorientował się, że zwracasz AbstractMessage. Czego nie można zrozumieć z opisu, to instancje wywodzące się z AbstractMessage. Są to rodzaje, które musisz podać. Każdy typ pochodzący z AbstractMessage i powracający tam musi być znanym typem, a to musi zostać zadeklarowane jako na samym AbstractMessage.

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

W twoim przypadku trzeba atrybut powiedzieć serializatora kontrakt danych: „Hej, jak widać będę zwracać AbstractMessage, ale to, czego nie może wiedzieć: w rzeczywistości, to Message”:

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

Czy mógłbyś sprawdzić moją edycję 12:39, ponieważ odzwierciedla to, co zrozumiałem twoją odpowiedź. – Serge

+0

Wygląda dobrze. Nigdy nie będziesz potrzebował AbstractMessage jako znanego typu, ponieważ ten typ można wywnioskować z podpisu metody. Potrzebujesz tylko atrybutu KnownType w klasie usług, gdy obsługuje on typy, które nie są zawarte w przestrzeni nazw System lub w samym podpisie metody. – nvoigt

+0

To jednak nie działa. – Serge