Udostępnij za pośrednictwem


Projektowanie kontraktów usług

W tym temacie opisano, jakie kontrakty usług są zdefiniowane, jakie operacje są dostępne (oraz implikacje dla bazowych wymian komunikatów), jakie typy danych są używane, oraz inne problemy, które ułatwiają projektowanie operacji spełniających wymagania scenariusza.

Tworzenie kontraktu usługi

Usługi uwidaczniają szereg operacji. W aplikacjach programu Windows Communication Foundation (WCF) zdefiniuj operacje, tworząc metodę i oznaczając ją za pomocą atrybutu OperationContractAttribute . Następnie, aby utworzyć kontrakt usługi, pogrupuj operacje, deklarując je w interfejsie oznaczonym atrybutem ServiceContractAttribute lub definiując je w klasie oznaczonej tym samym atrybutem. (Aby zapoznać się z podstawowym przykładem, zobacz Instrukcje: definiowanie kontraktu usługi).

Wszystkie metody, które nie mają atrybutu OperationContractAttribute , nie są operacjami usług i nie są uwidocznione przez usługi WCF.

W tym temacie opisano następujące kwestie decyzyjne podczas projektowania kontraktu usługi:

  • Czy używać klas, czy interfejsów.

  • Jak określić typy danych, które chcesz wymienić.

  • Typy wzorców wymiany, których można użyć.

  • Określa, czy można jawnie określić wymagania dotyczące zabezpieczeń w ramach umowy.

  • Ograniczenia dotyczące danych wejściowych i wyjściowych operacji.

Klasy lub interfejsy

Zarówno klasy, jak i interfejsy reprezentują grupowanie funkcji, a zatem oba te klasy mogą służyć do definiowania kontraktu usługi WCF. Zaleca się jednak używanie interfejsów, ponieważ bezpośrednio modelują kontrakty usług. Bez implementacji interfejsy nie definiują więcej niż grupowanie metod z określonymi podpisami. Zaimplementuj interfejs kontraktu usługi i zaimplementowano usługę WCF.

Wszystkie zalety interfejsów zarządzanych mają zastosowanie do interfejsów kontraktów usług:

  • Interfejsy kontraktów usług mogą rozszerzać dowolną liczbę innych interfejsów kontraktów usług.

  • Pojedyncza klasa może implementować dowolną liczbę kontraktów usług, implementując te interfejsy kontraktów usług.

  • Implementację kontraktu usługi można zmodyfikować, zmieniając implementację interfejsu, a kontrakt usługi pozostaje taki sam.

  • Możesz wersję usługi, implementując stary interfejs i nowy. Starzy klienci łączą się z oryginalną wersją, podczas gdy nowsi klienci mogą łączyć się z nowszą wersją.

Uwaga

Podczas dziedziczenia z innych interfejsów kontraktu usługi nie można zastąpić właściwości operacji, takich jak nazwa lub przestrzeń nazw. Jeśli spróbujesz to zrobić, utworzysz nową operację w bieżącym kontrakcie usługi.

Przykład użycia interfejsu do utworzenia kontraktu usługi można znaleźć w temacie How to: Create a Service with a Contract Interface (Jak utworzyć usługę za pomocą interfejsu kontraktu).

Można jednak użyć klasy do zdefiniowania kontraktu usługi i zaimplementowania tego kontraktu w tym samym czasie. Zaletą tworzenia usług przez zastosowanie ServiceContractAttribute i OperationContractAttribute bezpośrednio do klasy i metod odpowiednio w klasie i metod w klasie jest szybkość i prostota. Wadą jest to, że klasy zarządzane nie obsługują wielu dziedziczenia, a w rezultacie mogą implementować tylko jeden kontrakt usługi naraz. Ponadto wszelkie modyfikacje sygnatur klasy lub metody modyfikują kontrakt publiczny dla tej usługi, co może uniemożliwić niezmodyfikowanym klientom korzystanie z usługi. Aby uzyskać więcej informacji, zobacz Implementowanie kontraktów usług.

Przykład, który używa klasy do tworzenia kontraktu usługi i implementowania go w tym samym czasie, zobacz How to: Create a Service with a Contract Class (Jak utworzyć usługę z klasą kontraktu).

W tym momencie należy zrozumieć różnicę między definiowaniem kontraktu usługi przy użyciu interfejsu a użyciem klasy. Następnym krokiem jest podjęcie decyzji o tym, jakie dane mogą być przekazywane między usługą a jej klientami.

Parametry i wartości zwracane

Każda operacja ma wartość zwracaną i parametr, nawet jeśli są to void. Jednak w przeciwieństwie do metody lokalnej, w której można przekazywać odwołania do obiektów z jednego obiektu do innego, operacje usługi nie przekazują odwołań do obiektów. Zamiast tego przekazują kopie obiektów.

Jest to istotne, ponieważ każdy typ używany w parametrze lub zwracanej wartości musi być serializowalny; oznacza to, że należy przekonwertować obiekt tego typu na strumień bajtów i ze strumienia bajtów do obiektu.

Typy pierwotne są domyślnie serializowalne, podobnie jak wiele typów w programie .NET Framework.

Uwaga

Wartość nazw parametrów w podpisie operacji jest częścią kontraktu i uwzględnia wielkość liter. Jeśli chcesz użyć tej samej nazwy parametru lokalnie, ale zmodyfikować nazwę w opublikowanych metadanych, zobacz System.ServiceModel.MessageParameterAttribute.

Kontrakty danych

Aplikacje zorientowane na usługi, takie jak aplikacje programu Windows Communication Foundation (WCF), są przeznaczone do współdziałania z największą liczbą aplikacji klienckich zarówno na platformach firmy Microsoft, jak i innych niż Microsoft. W przypadku najszerszego możliwego współdziałania zaleca się oznaczenie typów za pomocą DataContractAttribute atrybutów i DataMemberAttribute w celu utworzenia kontraktu danych, który jest częścią kontraktu usługi opisującego dane wymieniane przez operacje usługi.

Kontrakty danych są kontraktami w stylu zgody: żaden typ lub składowa danych nie jest serializowany, chyba że jawnie zastosujesz atrybut kontraktu danych. Kontrakty danych nie są powiązane z zakresem dostępu kodu zarządzanego: prywatne elementy członkowskie danych mogą być serializowane i wysyłane w innym miejscu, aby uzyskać dostęp publicznie. (Aby zapoznać się z podstawowym przykładem kontraktu danych, zobacz Instrukcje: tworzenie podstawowego kontraktu danych dla klasy lub struktury). Program WCF obsługuje definicję podstawowych komunikatów PROTOKOŁU SOAP, które umożliwiają działanie operacji, a także serializację typów danych do i z treści komunikatów. Tak długo, jak typy danych można serializować, nie trzeba myśleć o podstawowej infrastrukturze wymiany komunikatów podczas projektowania operacji.

Chociaż typowa aplikacja WCF używa DataContractAttribute atrybutów i DataMemberAttribute do tworzenia kontraktów danych dla operacji, można użyć innych mechanizmów serializacji. ISerializableStandard , SerializableAttributei IXmlSerializable mechanizmy działają tak, aby obsługiwać serializacji typów danych do bazowych komunikatów PROTOKOŁU SOAP, które przenoszą je z jednej aplikacji do innej. Można stosować więcej strategii serializacji, jeśli typy danych wymagają specjalnej obsługi. Aby uzyskać więcej informacji na temat opcji serializacji typów danych w aplikacjach WCF, zobacz Określanie transferu danych w kontraktach usług.

Mapowanie parametrów i zwracanie wartości do wymiany komunikatów

Operacje usługi są obsługiwane przez podstawową wymianę komunikatów PROTOKOŁU SOAP, które przesyłają dane aplikacji tam i z powrotem, oprócz danych wymaganych przez aplikację do obsługi niektórych standardowych funkcji zabezpieczeń, transakcji i sesji. Ponieważ tak jest, podpis operacji usługi nakazuje określony podstawowy wzorzec wymiany komunikatów (MEP), który może obsługiwać transfer danych i funkcje wymagane przez operację. W modelu programowania WCF można określić trzy wzorce: żądanie/odpowiedź, jednokierunkowe i dwukierunkowe wzorce komunikatów.

Żądanie/odpowiedź

Wzorzec żądania/odpowiedzi jest taki, w którym nadawca żądania (aplikacja kliencka) otrzymuje odpowiedź, z którą jest skorelowane żądanie. Jest to domyślny protokół MEP, ponieważ obsługuje operację, w której co najmniej jeden parametr jest przekazywany do operacji, a wartość zwracana jest przekazywana z powrotem do elementu wywołującego. Na przykład poniższy przykład kodu w języku C# przedstawia podstawową operację usługi, która przyjmuje jeden ciąg i zwraca ciąg.

[OperationContractAttribute]  
string Hello(string greeting);  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<OperationContractAttribute()>  
Function Hello (ByVal greeting As String) As String  

Ten podpis operacji określa formę podstawowej wymiany komunikatów. Jeśli żadna korelacja nie istniała, program WCF nie może określić, dla której operacji ma być przeznaczona wartość zwracana.

Należy pamiętać, że jeśli nie określisz innego podstawowego wzorca komunikatu, nawet operacje usługi, które zwracają void wartość (Nothing w Visual Basic), to wymiany komunikatów żądań/odpowiedzi. Wynikiem operacji jest to, że chyba że klient wywołuje operację asynchronicznie, klient przestaje przetwarzać do momentu odebrania komunikatu zwrotnego, mimo że ten komunikat jest pusty w normalnym przypadku. Poniższy przykład kodu w języku C# przedstawia operację, która nie zwraca się, dopóki klient nie otrzyma pustego komunikatu w odpowiedzi.

[OperationContractAttribute]  
void Hello(string greeting);  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<OperationContractAttribute()>  
Sub Hello (ByVal greeting As String)  

Powyższy przykład może spowolnić wydajność i czas odpowiedzi klienta, jeśli wykonanie operacji trwa długo, ale istnieją zalety operacji żądania/odpowiedzi, nawet gdy zwracają voidwartość . Najbardziej oczywistym jest to, że błędy protokołu SOAP mogą być zwracane w komunikacie odpowiedzi, co wskazuje, że wystąpił warunek błędu związany z usługą, niezależnie od tego, czy w komunikacji, czy w przetwarzaniu. Błędy protokołu SOAP określone w kontrakcie usługi są przekazywane do aplikacji klienckiej jako obiektu, gdzie parametr typu jest typem FaultException<TDetail> określonym w kontrakcie usługi. Dzięki temu klienci powiadamiania o warunkach błędów w usługach WCF są łatwe. Aby uzyskać więcej informacji na temat wyjątków, błędów protokołu SOAP i obsługi błędów, zobacz Określanie i obsługa błędów w kontraktach i usługach. Aby zapoznać się z przykładem usługi żądania/odpowiedzi i klienta, zobacz Instrukcje: tworzenie kontraktu żądania i odpowiedzi. Aby uzyskać więcej informacji na temat problemów ze wzorcem odpowiedzi na żądanie, zobacz Usługi żądań i odpowiedzi.

Jednokierunkowe

Jeśli klient aplikacji usługi WCF nie powinien czekać na ukończenie operacji i nie przetwarza błędów protokołu SOAP, operacja może określić wzorzec jednokierunkowego komunikatu. Jednokierunkowa operacja jest operacją, w której klient wywołuje operację i kontynuuje przetwarzanie po zapisaniu komunikatu w sieci przez program WCF. Zazwyczaj oznacza to, że jeśli dane wysyłane w komunikacie wychodzącym są bardzo duże, klient kontynuuje działanie niemal natychmiast (chyba że wystąpił błąd podczas wysyłania danych). Ten typ wzorca wymiany komunikatów obsługuje zachowanie podobne do zdarzeń od klienta do aplikacji usługi.

Wymiana komunikatów, w której jest wysyłany jeden komunikat i żadna nie jest odbierana, nie może obsługiwać operacji usługi, która określa wartość zwracaną inną niż void; w tym przypadku InvalidOperationException zgłaszany jest wyjątek.

Żaden komunikat powrotny nie oznacza również, że nie może zostać zwrócony błąd PROTOKOŁU SOAP, aby wskazać błędy podczas przetwarzania lub komunikacji. (Komunikacja informacji o błędach, gdy operacje są operacjami jednokierunkowymi, wymagają wzorca dwukierunkowej wymiany komunikatów).

Aby określić jednokierunkową wymianę komunikatów dla operacji zwracającej voidwartość , ustaw IsOneWay właściwość na true, jak w poniższym przykładzie kodu w języku C#.

[OperationContractAttribute(IsOneWay=true)]  
void Hello(string greeting);  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<OperationContractAttribute(IsOneWay := True)>  
Sub Hello (ByVal greeting As String)  

Ta metoda jest identyczna z powyższym przykładem żądania/odpowiedzi, ale ustawienie IsOneWay właściwości true oznacza, że mimo że metoda jest identyczna, operacja usługi nie wysyła komunikatu zwrotnego, a klienci zwracają natychmiast po przekazaniu komunikatu wychodzącego do warstwy kanału. Przykład można znaleźć w temacie How to: Create a One-Way Contract (Instrukcje: tworzenie kontraktu jednokierunkowego). Aby uzyskać więcej informacji na temat wzorca jednokierunkowego, zobacz Usługi jednokierunkowe.

Dupleks

Wzorzec dupleksu charakteryzuje się możliwością zarówno usługi, jak i klienta do wysyłania komunikatów do siebie niezależnie, niezależnie od tego, czy używasz jednokierunkowej, czy komunikatów żądań/odpowiedzi. Ta forma dwukierunkowej komunikacji jest przydatna w przypadku usług, które muszą komunikować się bezpośrednio z klientem lub zapewnić środowisko asynchroniczne po obu stronach wymiany komunikatów, w tym zachowanie podobne do zdarzeń.

Wzorzec dwukierunkowy jest nieco bardziej złożony niż wzorce żądania/odpowiedzi lub jednokierunkowe ze względu na dodatkowy mechanizm komunikacji z klientem.

Aby zaprojektować kontrakt dwukierunkowy, należy również zaprojektować kontrakt wywołania zwrotnego i przypisać typ tego kontraktu wywołania zwrotnego do CallbackContract właściwości atrybutu ServiceContractAttribute , który oznacza kontrakt usługi.

Aby zaimplementować wzorzec dwukierunkowy, należy utworzyć drugi interfejs zawierający deklaracje metod wywoływane na kliencie.

Aby zapoznać się z przykładem tworzenia usługi i klienta, który uzyskuje dostęp do tej usługi, zobacz How to: Create a Duplex Contract (Jak utworzyć kontrakt dwukierunkowy) i How to: Usługi programu Access with a Duplex Contract (Instrukcje: Usługi programu Access z umową dwukierunkową). Aby zapoznać się z przykładem roboczym, zobacz Dupleks. Aby uzyskać więcej informacji na temat problemów z używaniem kontraktów dwukierunkowych, zobacz Usługi dwustronne.

Uwaga

Gdy usługa odbiera komunikat dwukierunkowy, analizuje ReplyTo element w tej wiadomości przychodzącej, aby określić, gdzie wysłać odpowiedź. Jeśli kanał używany do odbierania komunikatu nie jest zabezpieczony, niezaufany klient może wysłać złośliwy komunikat z maszyną ReplyTodocelową , co prowadzi do odmowy usługi (DOS) tej maszyny docelowej.

Parametry out i ref

W większości przypadków można użyć in parametrów (ByValw Visual Basic) i refout parametrów (ByRef w Visual Basic). Ponieważ oba out parametry i ref wskazują, że dane są zwracane z operacji, podpis operacji, taki jak poniżej, określa, że operacja żądania/odpowiedzi jest wymagana, mimo że podpis operacji zwraca wartość void.

[ServiceContractAttribute]  
public interface IMyContract  
{  
  [OperationContractAttribute]  
  public void PopulateData(ref CustomDataType data);  
}  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<ServiceContractAttribute()> _  
Public Interface IMyContract  
  <OperationContractAttribute()> _  
  Public Sub PopulateData(ByRef data As CustomDataType)  
End Interface  

Jedynymi wyjątkami są przypadki, w których podpis ma określoną strukturę. Na przykład można użyć NetMsmqBinding powiązania do komunikowania się z klientami tylko wtedy, gdy metoda używana do deklarowania operacji zwraca voidwartość ; nie może istnieć wartość wyjściowa, niezależnie od tego, czy jest to wartość zwracana, reflub out parametr.

Ponadto użycie parametrów out lub ref wymaga, aby operacja miała podstawowy komunikat odpowiedzi, aby przywrócić zmodyfikowany obiekt. Jeśli operacja jest operacją jednokierunkową, InvalidOperationException w czasie wykonywania jest zgłaszany wyjątek.

Określanie poziomu ochrony komunikatów w kontrakcie

Podczas projektowania kontraktu należy również zdecydować o poziomie ochrony komunikatów usług, które implementują kontrakt. Jest to konieczne tylko wtedy, gdy zabezpieczenia komunikatów są stosowane do powiązania w punkcie końcowym kontraktu. Jeśli powiązanie ma wyłączone zabezpieczenia (oznacza to, że jeśli powiązanie dostarczone przez system ustawia System.ServiceModel.SecurityMode wartość na wartość SecurityMode.None), nie trzeba decydować o poziomie ochrony komunikatów dla kontraktu. W większości przypadków powiązania dostarczone przez system z zastosowanymi zabezpieczeniami na poziomie komunikatu zapewniają wystarczający poziom ochrony i nie trzeba uwzględniać poziomu ochrony dla każdej operacji lub dla każdego komunikatu.

Poziom ochrony to wartość określająca, czy komunikaty (lub części komunikatów), które obsługują usługę, są podpisane, podpisane i szyfrowane, czy wysyłane bez podpisów lub szyfrowania. Poziom ochrony można ustawić w różnych zakresach: na poziomie usługi dla określonej operacji dla komunikatu w ramach tej operacji lub części komunikatu. Wartości ustawione w jednym zakresie stają się wartością domyślną dla mniejszych zakresów, chyba że jawnie przesłonięć. Jeśli konfiguracja powiązania nie może zapewnić wymaganego minimalnego poziomu ochrony dla kontraktu, zgłaszany jest wyjątek. A jeśli nie ustawiono jawnie wartości na poziomie ochrony w umowie, konfiguracja powiązania kontroluje poziom ochrony dla wszystkich komunikatów, jeśli powiązanie ma zabezpieczenia komunikatów. To jest zachowanie domyślne.

Ważne

Podjęcie decyzji, czy jawnie ustawić różne zakresy umowy na mniej niż pełny poziom ochrony, ProtectionLevel.EncryptAndSign jest zazwyczaj decyzją, która wymienia pewien stopień bezpieczeństwa w celu zwiększenia wydajności. W takich przypadkach decyzje muszą się obracać wokół operacji i wartości wymienianych danych. Aby uzyskać więcej informacji, zobacz Zabezpieczanie usług.

Na przykład poniższy przykład kodu nie ustawia ProtectionLevel właściwości lub ProtectionLevel w kontrakcie.

[ServiceContract]  
public interface ISampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute]  
  public int GetInt();
}  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<ServiceContractAttribute()> _  
Public Interface ISampleService  
  
  <OperationContractAttribute()> _  
  Public Function GetString()As String  
  
  <OperationContractAttribute()> _  
  Public Function GetData() As Integer  
  
End Interface  

Podczas interakcji z implementacją ISampleService w punkcie końcowym z wartością domyślną (domyślną WSHttpBinding , czyli Message), System.ServiceModel.SecurityModewszystkie komunikaty są szyfrowane i podpisane, ponieważ jest to domyślny poziom ochrony. Jednak gdy ISampleService usługa jest używana z wartością domyślną BasicHttpBinding (domyślną SecurityMode, czyli None), wszystkie komunikaty są wysyłane jako tekst, ponieważ nie ma żadnych zabezpieczeń dla tego powiązania i dlatego poziom ochrony jest ignorowany (oznacza to, że komunikaty nie są ani szyfrowane, ani podpisane). Jeśli element SecurityMode został zmieniony na Message, te komunikaty zostaną zaszyfrowane i podpisane (ponieważ będzie to teraz domyślny poziom ochrony powiązania).

Jeśli chcesz jawnie określić lub dostosować wymagania dotyczące ochrony dla kontraktu, ustaw ProtectionLevel właściwość (lub dowolną ProtectionLevel z właściwości w mniejszym zakresie) na poziom wymagany przez umowę serwisową. W takim przypadku użycie jawnego ustawienia wymaga powiązania, aby obsługiwać to ustawienie co najmniej dla używanego zakresu. Na przykład poniższy przykład kodu określa jedną ProtectionLevel wartość jawnie dla GetGuid operacji.

[ServiceContract]  
public interface IExplicitProtectionLevelSampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]  
  public int GetInt();
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]  
  public int GetGuid();
}  

Poniżej znajduje się odpowiedni kod języka Visual Basic.

<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
    <OperationContract()> _
    Public Function GetString() As String
    End Function
  
    <OperationContract(ProtectionLevel := ProtectionLevel.None)> _
    Public Function GetInt() As Integer
    End Function
  
    <OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
    Public Function GetGuid() As Integer
    End Function
  
End Interface  

Usługa, która implementuje ten IExplicitProtectionLevelSampleService kontrakt i ma punkt końcowy, który używa domyślnego WSHttpBinding (domyślnego System.ServiceModel.SecurityMode, czyli Message) ma następujące zachowanie:

  • Komunikaty GetString operacji są szyfrowane i podpisane.

  • Komunikaty GetInt operacji są wysyłane jako niezaszyfrowane i niepodpisane (czyli zwykły) tekst.

  • Operacja GetGuidSystem.Guid jest zwracana w komunikacie zaszyfrowanym i podpisanym.

Aby uzyskać więcej informacji na temat poziomów ochrony i sposobu ich używania, zobacz Opis poziomu ochrony. Aby uzyskać więcej informacji na temat zabezpieczeń, zobacz Zabezpieczanie usług.

Inne wymagania dotyczące podpisu operacji

Niektóre funkcje aplikacji wymagają określonego rodzaju podpisu operacji. Na przykład NetMsmqBinding powiązanie obsługuje trwałe usługi i klientów, w których aplikacja może ponownie uruchomić aplikację w trakcie komunikacji i odebrać miejsce, w którym została przerwana bez braku komunikatów. (Aby uzyskać więcej informacji, zobacz Kolejki w programie WCF). Jednak operacje trwałe muszą przyjmować tylko jeden in parametr i nie mają wartości zwracanej.

Innym przykładem jest użycie Stream typów w operacjach. Stream Ponieważ parametr zawiera całą treść komunikatu, jeśli dane wejściowe lub wyjściowe (czyli parametr, refout parametr lub wartość zwracana) mają typ Stream, musi to być jedyne dane wejściowe lub wyjściowe określone w operacji. Ponadto parametr lub zwracany typ musi mieć Streamwartość , System.ServiceModel.Channels.Messagelub System.Xml.Serialization.IXmlSerializable. Aby uzyskać więcej informacji na temat strumieni, zobacz Duże dane i przesyłanie strumieniowe.

Nazwy, przestrzenie nazw i zaciemnianie

Nazwy i przestrzenie nazw typów platformy .NET w definicji kontraktów i operacji są istotne, gdy kontrakty są konwertowane na WSDL i kiedy komunikaty kontraktów są tworzone i wysyłane. Dlatego zdecydowanie zaleca się, aby nazwy kontraktów usług i przestrzenie nazw zostały jawnie ustawione przy użyciu Name właściwości i Namespace wszystkich atrybutów kontraktu pomocniczego, takich jak ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttributei inne atrybuty kontraktu.

Jednym z tych wyników jest to, że jeśli nazwy i przestrzenie nazw nie są jawnie ustawione, użycie zaciemniania IL w zestawie zmienia nazwy typów kontraktów i przestrzeni nazw i powoduje modyfikację WSDL i wymiany przewodów, które zwykle kończą się niepowodzeniem. Jeśli nie ustawisz jawnie nazw kontraktów i przestrzeni nazw, ale zamierzasz używać zaciemniania, użyj ObfuscationAttribute atrybutów i ObfuscateAssemblyAttribute , aby zapobiec modyfikacji nazw typów kontraktów i przestrzeni nazw.

Zobacz też