Konfiguracja WCF bez pliku konfiguracyjnego

90

Czy ktoś zna dobry przykład tego, jak udostępniać usługę WCF programowo bez użycia pliku konfiguracji? Wiem, że model obiektów usług jest teraz znacznie bogatszy dzięki WCF, więc wiem, że jest to możliwe. Po prostu nie widziałem przykładu, jak to zrobić. I odwrotnie, chciałbym zobaczyć, jak odbywa się również konsumpcja bez pliku konfiguracyjnego.

Zanim ktoś zapyta, mam bardzo konkretną potrzebę zrobienia tego bez plików konfiguracyjnych. Normalnie nie polecałbym takiej praktyki, ale jak powiedziałem, w tym przypadku istnieje bardzo konkretna potrzeba.

Kilhoffer
źródło
1
Dlaczego nie poleciłbyś takiej praktyki (programowe wystawianie usługi bez konfiguracji)?
BornToCode

Odpowiedzi:

115

Jak odkryłem, korzystanie z usługi internetowej bez pliku konfiguracyjnego jest bardzo proste. Wystarczy utworzyć obiekt powiązania i obiekt adresowy i przekazać je albo do konstruktora serwera proxy klienta, albo do ogólnej instancji ChannelFactory. Możesz spojrzeć na domyślny plik app.config, aby zobaczyć, jakich ustawień użyć, a następnie utworzyć statyczną metodę pomocniczą w miejscu, w którym utworzy się Twój serwer proxy:

internal static MyServiceSoapClient CreateWebServiceInstance() {
    BasicHttpBinding binding = new BasicHttpBinding();
    // I think most (or all) of these are defaults--I just copied them from app.config:
    binding.SendTimeout = TimeSpan.FromMinutes( 1 );
    binding.OpenTimeout = TimeSpan.FromMinutes( 1 );
    binding.CloseTimeout = TimeSpan.FromMinutes( 1 );
    binding.ReceiveTimeout = TimeSpan.FromMinutes( 10 );
    binding.AllowCookies = false;
    binding.BypassProxyOnLocal = false;
    binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
    binding.MessageEncoding = WSMessageEncoding.Text;
    binding.TextEncoding = System.Text.Encoding.UTF8;
    binding.TransferMode = TransferMode.Buffered;
    binding.UseDefaultWebProxy = true;
    return new MyServiceSoapClient( binding, new EndpointAddress( "http://www.mysite.com/MyService.asmx" ) );
}
devios1
źródło
Osobiście podoba mi się to podejście w przypadku przykładów, kiedy zamierzasz używać pliku w innej sprawie, na przykład, jeśli zaszyfrowałeś swoją aplikację.config (lub równoważny plik konfiguracyjny) i nie musisz używać wbudowanego WCF możliwości czytania w połączeniu
Noah
18
W przypadku korzystania z protokołu HTTPS dodaj binding.Security.Mode = BasicHttpSecurityMode.Transport;
ciscoheat
U mnie to działało całkiem nieźle. Jedyną różnicą dla mnie jest to, że ustawiłem również ReaderQuotas i informacje o bezpieczeństwie. Skorzystałem z porady ciscoheat i ustawiłem Security.Transport.Mode na Transport jeśli używam https (dla mnie nie jest to znane w czasie kompilacji).
Kirk Liemohn
2
Właśnie sprawdziłem, że wszystkie ustawiane właściwości są równe domyślnym w WCF 4, fwiw. (Ale pamiętaj, że Security.Modedomyślnie None.)
ladenedge
19

Jeśli jesteś zainteresowany wyeliminowaniem użycia sekcji System.ServiceModel w web.config dla hostingu IIS, zamieściłem tutaj przykład, jak to zrobić ( http://bejabbers2.blogspot.com/2010/02/wcf -zero-config-in-net-35-part-ii.html ). Pokazuję, jak dostosować ServiceHost, aby utworzyć punkty końcowe zarówno metadanych, jak i wshttpbinding. Robię to w sposób ogólny, który nie wymaga dodatkowego kodowania. Dla tych, którzy nie dokonują natychmiastowej aktualizacji do .NET 4.0, może to być całkiem wygodne.

John Wigger
źródło
John, jestem pewien, że to świetny wpis na blogu, ale skoro istnieje akceptowana odpowiedź sprzed 17 miesięcy, czy naprawdę ma jakiś cel dla twojej odpowiedzi?
John Saunders
36
Ponieważ jest to moja pierwsza odpowiedź na przepełnienie stosu, może to nie być sposób, w jaki zwykle się to robi. Znając książki Lowy i Bustamante, które są świetnymi referencjami, myślę, że moja odpowiedź wykracza daleko poza próbki, które oferują. Używam głównie przepełnienia stosu podczas googlowania, więc często czytam posty, które są starsze. Posiadanie bardziej aktualnych odpowiedzi pomaga tylko z mojej perspektywy. Wygooglowałem ten post przed napisaniem kodu, aby uniknąć ponownego wynalezienia koła.
John Wigger
48
Jako częsty użytkownik SO uważam, że bardzo pożądane jest czytanie nowych postów na stare tematy. Pomaga mi to lepiej wykonywać swoją pracę, co zwiększa wartość tej witryny (ponieważ ja i inni będą ją częściej odwiedzać). Zamiast trzymać się zasad, dlaczego nie pozwolić ludziom dyskutować, aby można było znaleźć lepsze odpowiedzi? Czy nie o to chodzi?
7
Wydaje się, że John Saunders został postawiony na swoim miejscu z odpowiedzią na jego własne pytanie (z którego żadnego nie przyjął jako odpowiedzi, którą mógłbym dodać). Osobiście nie mam problemu ze spóźnionymi odpowiedziami na pytania i zwykle jestem zachwycony, widząc nową odpowiedź na pytanie, które zadałem, miesiące, jeśli nie lata później. Jak na ironię, zdobyłem własną odznakę Nekromanty za zaakceptowaną odpowiedź na to pytanie. :)
devios1
3
Miałem ten sam problem, a zaakceptowana odpowiedź mi nie pomogła, ale to pomogło, niechętnie za późne odpowiedzi! Gdyby nie spóźnione odpowiedzi, musiałbym stworzyć zduplikowane pytanie.
Didier A.
15

Tutaj jest to kompletny i działający kod. Myślę, że to ci bardzo pomoże. Szukałem i nigdy nie znalazłem pełnego kodu, dlatego próbowałem umieścić kompletny i działający kod. Powodzenia.

public class ValidatorClass
{
    WSHttpBinding BindingConfig;
    EndpointIdentity DNSIdentity;
    Uri URI;
    ContractDescription ConfDescription;

    public ValidatorClass()
    {  
        // In constructor initializing configuration elements by code
        BindingConfig = ValidatorClass.ConfigBinding();
        DNSIdentity = ValidatorClass.ConfigEndPoint();
        URI = ValidatorClass.ConfigURI();
        ConfDescription = ValidatorClass.ConfigContractDescription();
    }


    public void MainOperation()
    {
         var Address = new EndpointAddress(URI, DNSIdentity);
         var Client = new EvalServiceClient(BindingConfig, Address);
         Client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
         Client.Endpoint.Contract = ConfDescription;
         Client.ClientCredentials.UserName.UserName = "companyUserName";
         Client.ClientCredentials.UserName.Password = "companyPassword";
         Client.Open();

         string CatchData = Client.CallServiceMethod();

         Client.Close();
    }



    public static WSHttpBinding ConfigBinding()
    {
        // ----- Programmatic definition of the SomeService Binding -----
        var wsHttpBinding = new WSHttpBinding();

        wsHttpBinding.Name = "BindingName";
        wsHttpBinding.CloseTimeout = TimeSpan.FromMinutes(1);
        wsHttpBinding.OpenTimeout = TimeSpan.FromMinutes(1);
        wsHttpBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
        wsHttpBinding.SendTimeout = TimeSpan.FromMinutes(1);
        wsHttpBinding.BypassProxyOnLocal = false;
        wsHttpBinding.TransactionFlow = false;
        wsHttpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
        wsHttpBinding.MaxBufferPoolSize = 524288;
        wsHttpBinding.MaxReceivedMessageSize = 65536;
        wsHttpBinding.MessageEncoding = WSMessageEncoding.Text;
        wsHttpBinding.TextEncoding = Encoding.UTF8;
        wsHttpBinding.UseDefaultWebProxy = true;
        wsHttpBinding.AllowCookies = false;

        wsHttpBinding.ReaderQuotas.MaxDepth = 32;
        wsHttpBinding.ReaderQuotas.MaxArrayLength = 16384;
        wsHttpBinding.ReaderQuotas.MaxStringContentLength = 8192;
        wsHttpBinding.ReaderQuotas.MaxBytesPerRead = 4096;
        wsHttpBinding.ReaderQuotas.MaxNameTableCharCount = 16384;

        wsHttpBinding.ReliableSession.Ordered = true;
        wsHttpBinding.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(10);
        wsHttpBinding.ReliableSession.Enabled = false;

        wsHttpBinding.Security.Mode = SecurityMode.Message;
        wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
        wsHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
        wsHttpBinding.Security.Transport.Realm = "";

        wsHttpBinding.Security.Message.NegotiateServiceCredential = true;
        wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
        wsHttpBinding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic256;
        // ----------- End Programmatic definition of the SomeServiceServiceBinding --------------

        return wsHttpBinding;

    }

    public static Uri ConfigURI()
    {
        // ----- Programmatic definition of the Service URI configuration -----
        Uri URI = new Uri("http://localhost:8732/Design_Time_Addresses/TestWcfServiceLibrary/EvalService/");

        return URI;
    }

    public static EndpointIdentity ConfigEndPoint()
    {
        // ----- Programmatic definition of the Service EndPointIdentitiy configuration -----
        EndpointIdentity DNSIdentity = EndpointIdentity.CreateDnsIdentity("tempCert");

        return DNSIdentity;
    }


    public static ContractDescription ConfigContractDescription()
    {
        // ----- Programmatic definition of the Service ContractDescription Binding -----
        ContractDescription Contract = ContractDescription.GetContract(typeof(IEvalService), typeof(EvalServiceClient));

        return Contract;
    }
}
SM Khaled Reza
źródło
Bardzo fajny przykład! Pokazujesz prawie każdy aspekt ręcznej konfiguracji. Ładnie wykonane!
Kilhoffer
5
Nie rozumiem, jak EvalServiceClient pasuje do tego kodu. Jest wymieniony, ale nie zdefiniowany. Dlaczego serwer tworzy klienta?
BlueMonkMN
5

Nie jest to łatwe po stronie serwera .

Po stronie klienta możesz użyć ChannelFactory

Gulzar Nazim
źródło
3

Wszystkie konfiguracje WCF można wykonać programowo. Możliwe jest więc tworzenie serwerów i klientów bez pliku konfiguracyjnego.

Polecam książkę „Programming WCF Services” autorstwa Juvala Lowy, która zawiera wiele przykładów konfiguracji programowej.

Paul Lalonde
źródło
2

Jest to bardzo łatwe zarówno po stronie klienta, jak i serwera. Książka Juvala Lowy ma doskonałe przykłady.

Co do twojego komentarza na temat plików konfiguracyjnych, powiedziałbym, że pliki konfiguracyjne są sekundą dla biedaka, aby zrobić to w kodzie. Pliki konfiguracyjne są świetne, gdy kontrolujesz każdego klienta, który będzie łączył się z Twoim serwerem, i upewniasz się, że są aktualizowane, a użytkownicy nie mogą ich znaleźć i niczego zmienić. Uważam, że model pliku konfiguracji WCF jest ograniczający, średnio trudny do zaprojektowania i koszmar konserwacji. Podsumowując, myślę, że MS to bardzo zła decyzja, aby pliki konfiguracyjne stały się domyślnym sposobem wykonywania czynności.

EDYCJA: Jedną z rzeczy, których nie można zrobić z plikiem konfiguracyjnym, jest tworzenie usług z innymi niż domyślnymi konstruktorami. Prowadzi to do zmiennych statycznych / globalnych i pojedynczych oraz innych typów braku sensu w WCF.

Steve
źródło
2

Uważam, że wpis na blogu pod poniższym linkiem na ten temat jest bardzo interesujący.

Jednym z pomysłów, który mi się podoba, jest to, że można po prostu przekazać powiązanie, zachowanie lub adres sekcji XML z konfiguracji do odpowiedniego obiektu WCF i pozwolić mu obsłużyć przypisywanie właściwości - obecnie nie można tego zrobić.

Podobnie jak inni użytkownicy sieci Web, mam problemy z potrzebą implementacji programu WCF do korzystania z innego pliku konfiguracyjnego niż plik mojej aplikacji hostingowej (która jest usługą .NET 2.0 systemu Windows).

http://salvoz.com/blog/2007/12/09/programmatically-setting-wcf-configuration/

Ton
źródło