2009-08-24 10 views
5

Jestem całkiem nowy w WCF i mam tylko pytanie, jak poprawnie uzyskać dziedziczenie MessageContract. Uproszczona wersja mojej konfiguracji wygląda następująco: "podstawowy" typ komunikatu, a następnie kolejna wiadomość "testowa", która po niej dziedziczy.WCF MessageContract Inheritance

[MessageContract] 
public abstract class BaseMessage 
{ } 

[MessageContract] 
public class TestMessage : BaseMessage 
{ } 

Mam następnie asynchronicznego OperationContract na ServiceContract zdefiniowany jako:

[OperationContract(AsyncPattern = true)] 
IAsyncResult BeginFindRequest(BaseMessage request, AsyncCallback callback, object asyncState); 

problem, że jestem coraz to podczas wywoływania metody BeginFindRequest i przechodzącą w instancji TestMessage parametru żądanie, struktura WCF odformalizuje instancję TestMessage do BaseMessage po stronie usługi/serwera. Jak to jest zdefiniowane jako abstrakcyjne klasy, to wyniki w następujący błąd:

"The message cannot be deserialized into MessageContract type BaseMessage since it does not have a default (parameterless) constructor."

Z ograniczonych informacji, które mogę znaleźć na MessageContract dziedziczenia, wydaje się, że powinien po prostu pracować.

Moje pytanie brzmi: czego mi brakuje, aby to zadziałało; czy powinienem raczej zdefiniować oddzielne OperationContract na ServiceContract specjalnie dla tego typu - wadą jest to, że mogłem skończyć z wieloma dodatkowymi OperationContracts?

+0

PS - Używam net.tcp i NetDataContractSerializer (na obu klientach/serwerach). –

+0

Hej Frank - użyj "przycisku kodu" (tego z 010101010110), aby sformatować sekcje kodu jako takie. Ładuje je ładnie i składnia je podkreśli - gorąco polecam! –

+0

Dzięki Marc, zastanawiałeś się, jak to zrobiłeś! –

Odpowiedz

0

Czy można zmienić BaseMessage tak, aby był konkretną klasą z konstruktorem bez parametrów?

Komunikat o błędzie informuje, że nie ma możliwości zainicjowania obiektu typu BaseMessage, ponieważ jest on abstrakcyjny.

+0

Próbowałem tego, ale wtedy wiadomość jest deserializowana do tego typu, tracąc informacje o typie TestMessage. BeginFindRequest obecnie stosuje logikę w zależności od typu komunikatu, co oznacza, że ​​logika nie jest wykonywana, ponieważ typ to wtedy BaseMessage. –

3

OK, pierwsze pytanie brzmi: dlaczego tak naprawdę używasz umów z wiadomościami? Czy naprawdę tego potrzebujesz?

Zwykle umowy z wiadomościami są używane tylko wtedy, gdy trzeba ściśle kontrolować układ wiadomości SOAP, np. aby zaspokoić istniejący system, który musisz wywołać, który wymaga określonych nagłówków i podobnych.

"Normalne" wywołanie WCF praktycznie nie powinno wymagać zawarcia umowy z wiadomością.

Zdefiniujesz swoje zgłoszenia serwisowe (metody w Twojej usłudze), używając [ServiceContract], a struktury danych są przekazywane jako [DataContract]. Jeśli masz DataContract, masz więcej opcji, jak radzić sobie z dziedziczeniem/polimorfizmem w twojej usłudze (więcej niż przy konstruowaniu kontraktu z wiadomościami).

Marc

+0

Powodem, dla którego poszedłem do MessageContract, było przeczytanie artykułu z najlepszymi praktykami WCF, który, jak pamiętam, sugerował, że używanie ich przez Kontrakty Danych zawierało mniej wiadomości "wzdęcia". Próbuję znaleźć artykuł, do którego mogę się teraz odwołać. Spróbuję przekonwertować do DataContract, aby sprawdzić, czy to natychmiast rozwiązuje problem. Dzięki Marc –

+0

http://www.designpatternsfor.net/default.aspx?pid=99 - patrz lista od 5 (wzorzec komunikatu usługi). –

+0

Dzięki za link; kwestia, którą Rob robił w tym artykule, wydaje mi się nieco "zbyt zaawansowana", jak na mój gust. W większości przypadków nie dba się o to "podwójne opakowanie", które krytykuje. Ponownie: jeśli naprawdę MUSISZ kontrolować dokładny układ wiadomości, to YES - użyj umów z wiadomościami. Ale dla mnie to podejście typu OSTATNIA OPOWIEŚĆ, jeśli nic innego nie działa. Nie polecam tego jako domyślnej praktyki. –

0

Błąd po prostu chce mieć domyślny pusty contructor że można go używać. Jednakże zgadzam się z marc_s; w projektach, nad którymi pracowałem rzadko korzystałem z kontraktu na wiadomości, jedyny przypadek, jaki pamiętam, był częścią usługi transferu plików, w której fragmenty plików były przekazywane w mesasges.

+0

Zgodnie z moim komentarzem do codemeit, usunięcie słowa kluczowego abstract umożliwia działanie deselizalizacji, ale pozostawiam obiekt BaseMessage, gdy powinienem mieć obiekt TestMessage. Mam zamiar przekonwertować do DataContract, aby sprawdzić, czy to rozwiązuje problem. –

+0

Czy możesz zachować to streszczenie, ale mimo to dać BaseMessage chronionego domyślnego konstruktora? –

0

Spróbuj zdobienia swojego [ServiceContract] za pomocą atrybutu KnownType. Ponieważ TestMessage nie jest "widoczny" z operacji publicznej, pomaga to hydraulikom wiedzieć, jak traktować go, gdy go zobaczy.

Jeśli ta powinna umożliwić [DataContract] być szeregowane jako TestMessage swój nadal prawdopodobnie trzeba obsłużyć wiele różnie wiadomości poprzez „is a” lub jakiś inny odlewu.

+0

Próbowałem atrybutów KnownType i ServiceKnownType, które nie pomogły, ale ponieważ korzystam z NetDataContractSerializer, nie powinno to być wymagane? –

+0

Interesujące ..no, nie powinieneś potrzebować KnownType/ServiceKnownType, jak rozumiem, ale to również oznacza, że ​​dzielenie się wspólną dll gdzieś z TestMessage zadeklarowane w nim, jeśli nie za pomocą KnownType/ServiceKnownType? W przeciwnym razie klient nie wiedziałby nic o TestMessage, tylko BaseMessage - tylko hipoteza tutaj, ponieważ moje doświadczenie z NetDataContractSerializer jest zerowe - Być może będę musiała szukać trudniej, aby znaleźć potrzebę, aby uzyskać pewne doświadczenie! :) Jak zmieniasz domyślny DataContractSerializer? Czy wymieniasz go, zanim usługa zostanie otwarta dla biznesu? –

+0

Tak, wszystkie wymagane biblioteki są współdzielone między serwerem/klientem. Jeśli chodzi o zastępowanie DataContractSerializer, wybrałem podejście oparte na metodach fabrycznych w odniesieniu do różnych dostępnych rozwiązań opartych na atrybutach - tak, że nie musiałem umieszczać więcej atrybutów wszędzie. Zasadniczo zwraca on servicehost/proxy z wszystkimi DataContractSerializerOperationBehaviors zamienionymi na moją własną NetDataContractSerializerOperationBehaviour - która zapewnia NetDataContractSerializer kiedy jest to wymagane. –

6

W końcu znalazłem ten wpis na blogu, który uderzył w sedno -

Unfortunately the way that contracts are expressed in WCF makes is very easy to forget what their purpose is: to define the messages send to the operation and being sent back from the operation. In reality you have to think “how would I express this data in XML?”. XML doesn’t support inheritance so whatever you put in the contract is going to have to have some way of mapping to XML. The data contracts used to define the messages are simply a .NET typed convenience for generating the XML for the data you want to pass – if you view them any other way you are destined for a world of pain. So think about the data you want to pass, not how it may happen to be represented in your business layer and design your DataContracts accordingly.

http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,a3775eb1-b441-43ad-b9f1-e4aaba404235.aspx

Więc będę refaktoryzacji w celu zapewnienia dodatkowej metody z wyraźną typu umów. Pozwoli mi to również wyczyścić implementację usługi, usuwając wszystkie sprawdzenia typów.

Dzięki za pomoc.

+1

Tak, tak - świat "OO" i świat "SOA" to dość różne bestie ... Wiadomości oparte na XML nie nadają się dobrze do dziedziczenia i innych OO-izmów, do których tak bardzo się przyzwyczailiśmy do.... –

Powiązane problemy