Configuração WCF sem um arquivo de configuração

90

Alguém conhece um bom exemplo de como expor um serviço WCF programaticamente sem o uso de um arquivo de configuração? Sei que o modelo de objeto de serviço é muito mais rico agora com o WCF, então sei que é possível. Eu simplesmente não vi um exemplo de como fazer isso. Por outro lado, gostaria de ver como é feito o consumo sem um arquivo de configuração também.

Antes que alguém pergunte, tenho uma necessidade muito específica de fazer isso sem arquivos de configuração. Normalmente não recomendaria tal prática, mas, como disse, há uma necessidade muito específica neste caso.

Kilhoffer
fonte
1
Por que você não recomendaria tal prática (expor o serviço programaticamente sem configuração)?
BornToCode

Respostas:

115

Consumir um serviço da web sem um arquivo de configuração é muito simples, como descobri. Você simplesmente precisa criar um objeto de ligação e um objeto de endereço e passá-los para o construtor do proxy do cliente ou para uma instância ChannelFactory genérica. Você pode consultar o app.config padrão para ver quais configurações usar e, em seguida, criar um método auxiliar estático em algum lugar que instancia seu 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
fonte
Eu pessoalmente gosto dessa abordagem para exemplos quando você usará o arquivo em uma questão diferente, por exemplo, se você criptografou seu app.config (ou arquivo de configuração equivalente) e não precisa usar o WCF integrado capacidades de leitura em uma conexão
Noah
18
Para uso de https, adicione binding.Security.Mode = BasicHttpSecurityMode.Transport;
ciscoheat
Isso funcionou muito bem para mim. A única diferença para mim é que também defino as informações de ReaderQuotas e Segurança. Usei o conselho do ciscoheat e configurei o Security.Transport.Mode para Transport se estiver usando https (para mim isso não é conhecido em tempo de compilação).
Kirk Liemohn
2
Acabei de verificar que todas as propriedades que estão sendo definidas são iguais aos padrões no WCF 4, fwiw. (Mas observe que o Security.Modepadrão é None.)
ladenedge
19

Se você estiver interessado em eliminar o uso da seção System.ServiceModel no web.config para hospedagem IIS, postei um exemplo de como fazer isso aqui ( http://bejabbers2.blogspot.com/2010/02/wcf -zero-config-in-net-35-part-ii.html ). Mostro como personalizar um ServiceHost para criar metadados e pontos de extremidade wshttpbinding. Eu faço isso de maneira geral, que não requer codificação adicional. Para aqueles que não estão atualizando imediatamente para o .NET 4.0, isso pode ser muito conveniente.

John Wigger
fonte
John, tenho certeza de que é uma ótima postagem no blog, mas já que houve uma resposta aceita de 17 meses atrás, há realmente algum propósito para sua resposta?
John Saunders
36
Já que esta é minha primeira resposta do Stack Overflow, pode não ser assim que as coisas são feitas normalmente. Conhecendo os livros Lowy e Bustamante, que são ótimas referências, acho que minha resposta vai muito além das amostras que eles oferecem. Eu uso o Stack Overflow principalmente quando procuro no Google, então eu leio posts mais antigos com frequência. Ter respostas mais atualizadas só ajuda na minha perspectiva. Pesquisei este post antes de escrever meu código para evitar reinventar a roda.
John Wigger
48
Como um usuário SO frequente, acho bastante desejável ler novas postagens sobre tópicos antigos. Ajuda-me a fazer melhor o meu trabalho, o que aumenta o valor deste site (visto que eu e outras pessoas o visitaremos mais). Em vez de ser um defensor das regras, por que não permitir que as pessoas discutam para que respostas melhores sejam descobertas? Não é esse o ponto?
7
Parece que John Saunders foi colocado em seu lugar com a resposta à sua própria pergunta (nenhuma das quais ele aceitou como a resposta, devo acrescentar). Pessoalmente, não tenho problemas com respostas tardias a perguntas e geralmente fico feliz em ver uma nova resposta a uma pergunta que fiz, meses, senão anos depois. Ironicamente, ganhei meu próprio distintivo de Necromante com minha resposta aceita para essa pergunta. :)
devios1
3
Eu tive o mesmo problema, e a resposta aceita não me ajudou, mas ajudou, viva pelas respostas tardias! Se não fosse pelas respostas tardias, eu teria que criar uma pergunta duplicada disso.
Didier A.
15

Aqui, este é um código completo e funcional. Acho que vai te ajudar muito. Eu estava pesquisando e nunca encontrei um código completo, por isso tentei colocar o código completo e funcionando. Boa sorte.

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
fonte
Muito bom exemplo! Você demonstra quase todos os aspectos da configuração manual. Bem feito!
Kilhoffer
5
Não entendo como EvalServiceClient se encaixa neste código. É referenciado, mas não definido. Por que o servidor está criando um cliente?
BlueMonkMN
5

Não é fácil do lado do servidor .

Para o lado do cliente, você pode usar ChannelFactory

Gulzar Nazim
fonte
3

Todas as configurações do WCF podem ser feitas de maneira programática. Portanto, é possível criar servidores e clientes sem um arquivo de configuração.

Eu recomendo o livro "Programming WCF Services" de Juval Lowy, que contém muitos exemplos de configuração programática.

Paul Lalonde
fonte
2

É muito fácil de fazer no lado do cliente e do servidor. O livro de Juval Lowy tem excelentes exemplos.

Quanto ao seu comentário sobre os arquivos de configuração, eu diria que os arquivos de configuração são o segundo homem pobre em fazer isso em código. Os arquivos de configuração são ótimos quando você controla cada cliente que se conectará ao seu servidor e garante que eles estejam atualizados e que os usuários não possam encontrá-los e alterar nada. Acho que o modelo de arquivo de configuração do WCF é limitante, ligeiramente difícil de projetar e um pesadelo de manutenção. Em suma, acho que foi uma decisão muito ruim da MS tornar os arquivos de configuração a maneira padrão de fazer as coisas.

EDIT: Uma das coisas que você não pode fazer com o arquivo de configuração é criar serviços com construtores não padrão. Isso leva a variáveis ​​estáticas / globais e singletons e outros tipos de não-sentido no WCF.

Steve
fonte
2

Achei a postagem do blog no link abaixo sobre esse tópico muito interessante.

Uma ideia de que gosto é ser capaz de simplesmente passar uma ligação ou comportamento ou seção XML de endereço da configuração para o objeto WCF apropriado e deixá-lo lidar com a atribuição das propriedades - atualmente, você não pode fazer isso.

Como outras pessoas na web, estou tendo problemas em relação à necessidade de minha implementação do WCF usar um arquivo de configuração diferente daquele do meu aplicativo de hospedagem (que é um serviço do Windows .NET 2.0).

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

Tom
fonte