Ativação do host baseando-se na memória

O WCF fornece uma opção que nos permite configurar a porcentagem mínima de memória disponível na máquina antes de ativar o serviço. Para customizar isso, recorremos a propriedade (atributo) MinFreeMemoryPercentageToActivateService da classe (elemento) ServiceHostingEnvironmentSection. Essa propriedade recebe um número inteiro, que corresponde a quantidade de memória disponível que a máquina onde o serviço está rodando deverá ter disponível.

Como essa configuração recorre a métodos não gerenciados para fazer essa verificação, a aplicação (serviço) deverá estar rodando em full-trust. Caso contrário, uma exceção do tipo SecurityException será disparada e, consequentemente, o serviço não estará disponível para receber requisições.

Novos métodos para Enumeradores

A partir da versão 4.0 do .NET Framework, teremos dois novos métodos estáticos para a manipulação de enumeradores fornecidos pela classe Enum: TryParse<TEnum> e HasFlag.

Como podemos perceber, o primeiro deles, TryParse<TEnum>, trata-se de um método genérico, que recebe dois parâmetros. O primeiro deles, é uma string contendo o nome ou valor do item a ser pesquisado dentro do enumerador, já informado pelo argumento genérico TEnum; já o segundo parâmetro (de saída), é do tipo TEnum, pois caso a conversão seja possível, esse resultado já traz a informação tipada. E ainda, este método retorna um valor boleano, indicando se a conversão foi ou não possível, ao contrário do método Parse, que dispara uma exceção. Esse método trabalha de forma semalhante ao método TryParse das estruturas DateTime, Int32, entre outras que já fazem parte do .NET Framework. Abaixo temos o exemplo da sua utilização:

public enum Itens { Read, Write, FullControl, None }

Itens permissoes = Itens.None;

if (Enum.TryParse<Itens>(“Read”, out temp).ToString())
    Console.WriteLine(“Nível de Permissão: {0}”, temp);
else
    Console.WriteLine(“Não foi possível determinar o tipo de Permissão.”);

Além desse método, a Microsoft também está adicionando o método de instância chamado HasFlag, que retorna uma valor boleano indicando se o valor existe ou não dentro daquele enumerador. Com ele, a partir da variável que armazena o(s) enumerador(es) selecionado(s), podemos invocá-lo e passarmos o item a ser verificado, assim como podemos reparar no exemplo abaixo:

Itens permissoes = Itens.Read | Itens.Write;

if (permissoes.HasFlag(Itens.FullControl))
    Console.WriteLine(“Você tem permissão total.”);

Copiando Streams

Uma operação muito comum é a cópia de streams. Há diversas situações onde você pode querer usar isso, como por exemplo, armazenar um conteúdo de um arquivo em memória, para não precisar acessar o arquivo físico toda vez que precisar dele. Antes do .NET Framework 4.0, com o stream de origem em mãos, precisávamos armazenar os blocos em um buffer temporário, e em seguida, copiar para o stream de destino.

Apesar de não ser uma tarefa muito difícil de fazer, ela é propícia a erros, já que você precisa ficar manipulando índices para invocar os métodos Read e Write. Com o .NET Framework 4.0, a Microsoft acaba de adicionar um método (de instância) chamado CopyTo na classe Stream. Com ele, todas as classes que derivam direta ou indiretamente dela, poderão copiar o conteúdo atual para um outro Stream. Tudo o que precisamos fazer é:

MemoryStream ms = new MemoryStream();

using (FileStream fs = File.Open(“Arquivo.xml”, FileMode.Open)
    fs.CopyTo(ms);

Path.Combine

Até antes do .NET Framework 4.0, você possui um método estático chamado Combine, exposto pela classe Path. Dado duas strings para este método, ele retorna uma nova string contendo a combinação desses dois paths, colocando ou removendo barras quando necessário. Isso ajuda bastante, já que não precisamos ficar lidando com a string diretamente, verificando se existe ou não as barras para efetuar a concatenação.

Provavelmente você já deve ter se deparado com a situação onde você tem o path separado em mais partes. Com isso, o problema volta a acontecer, pois o método Combine não permite informarmos mais do que duas strings. A versão 4.0 do .NET Framework, a Microsoft adicionou uma nova versão (overload) deste método, que permite informarmos o path através de um paramarray, e com isso, podemos colocar quantas strings quisermos, que ele fará todo o trabalho necessário, e nos devolverá o resultado esperado. Veja o exemplo abaixo:

Console.Write(new StreamReader(Path.Combine(“d:Arquivos”, “Logs”, “Cobranca”, “2009.log”)).ReadToEnd());

Eventualmente, se você informar uma string vazia, ele automaticamente omitirá do resultado final. Além disso, a primeira string passada para este método, deverá sempre representar o path absoluto (“d:” ou “\arquivos”). Se ele encontrar qualquer outro path absoluto no meio, a operação será reiniciada, e todos os paths anteriores serão descartados.

Novos métodos da classe String

A versão 4.0 do .NET Framework, adiciona novos métodos estáticos na classe String, que possibilita trabalhar de forma muito mais simples do que anteriormente. O primeiro desses métodos é o IsNullOrWhiteSpace, que dado uma string, retorna um valor boleano indicando se é uma referência nula, vazio ou uma sequência de caracteres em branco, evitando a necessidade de chamar o método IsNullOrEmpty em conjunto com o método Trim.

Outra novidade é o método Concat<T>, que permite passar uma instância de alguma classe que implemente a Interface IEnumerable<T>. Isso ajudará, principalmente, quando estamos trabalhando com LINQ, e precisamos concaternar alguma propriedade retornada pela query, sem a necessidade de converter explicitamente em um array de strings. É importante dizer que esse método invoca o método ToString dos objetos (exposto pela classe System.Object), que obrigará você a customizá-lo, exibindo as informações necessárias, que na maioria das vezes, representa o estado do objeto. O exemplo abaixo ilustra o uso:

public class Usuario
{
    public string Nome { get; set; }
    public string Estado { get; set; }

    public override string ToString()
    {
        return this.Nome;
    }
}

string resultado = string.Concat((from u in listaDeUsuarios where u.Estado = “SP” select u));

Consumindo serviços via HTTPS

Quando você instala um certificado em um servidor Web, ele poderá ser utilizado por alguma aplicação (UI ou serviços) que é executada no mesmo. Ao vincular esse certificado através do binding de HTTPS no IIS, você poderá começar a fazer uso deste protocolo na sua aplicação. Agora, imagine que você tenha um certificado que foi emitido para um outro domínio, diferente daquele que você está utilizando. Por exemplo, veja que o certificado abaixo foi emitido para o “Dummy”, diferente do meu domínio utilizado pela aplicação, que é localhost.

Ao tentar acessar através do navegador, uma mensagem de aviso será exibida, indicando que o certificado que ele está utilizando é duvidoso, já que foi emitido para um domínio diferente daquele que ele foi vinculado. A imagem abaixo mostra essa mensagem:

Quando o usuário é interrogado, ele poderá decidir se continua ou não acessando aquele site, algo simples de fazer quando trata-se de aplicações que permitem a interação do usuário. Podemos também hospedar serviços no IIS, sejam eles WCF ou ASP.NET Web Services (ASMX), expondo através de HTTPS. Depois deste serviço referenciado na aplicação cliente, ao executá-la, você poderá se deparar com uma exceção do tipo SecurityNegotiationException, com a seguinte mensagem: Could not establish trust relationship for the SSL/TLS secure channel with authority [Servidor].

Durante a execução da aplicação cliente, antes dela efetivamente consumir o serviço, o WCF pergunta se ele deve ou não confiar no certificado, e o comportamento padrão é não. É exatamente o que acontece quando acessado através do navegador. Como o serviço não é interativo, você precisa de alguma forma, verificar se deseja ou não confiar naquele certificado que está sendo utilizado. Se não pudermos alterar o certificado, de uma forma semelhante a que vimos aqui, podemos escrever um código que permita efetuar essa verificação em tempo de execução.

Para isso, fazemos o uso da classe estática ServicePointManager, que está no namespace System.Net. Essa classe é responsável por gerenciar as conexões com a internet. Essa classe fornece uma propriedade chamada ServerCertificateValidationCallback, que deverá apontar para um método que segue a assinatura imposta pelo delegate RemoteCertificateValidationCallback. Abaixo temos um exemplo da sua utilização:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

A partir do momento que você configura esta propriedade, as requisições HTTPS que são realizadas, começam a ser verificadas pelo método vinculado. Repare que entre os parâmetros enviados para este método, um deles é o certificado que está sendo avaliado. E além disso, esse método deverá retornar um valor boleano, indicando se o certificado é ou não válido, de acordo com a regra que você especificar ali. É importante que você analise cuidadosamente antes de especificar isso no seu código, pois dessa forma, ele dará “bypass” em todos os certificados, independentemente se eles forem ou não válidos. Talvez isso é algo que seja legal em ambiente de desenvolvimento, mas em produção poderá representar uma vulnerabilidade de segurança.

.NET Reactive Framework

A Microsoft trabalha atualmente em um projeto chamado de .NET Reactive Framework (Rx). Em uma linguagem como o C#, este framework ajudará, ou melhor, mudará a forma com a qual escrevemos código para lidar com eventos e também com a programação assíncrona, que tem as características de reagirem quando algo acontece, por exemplo, quando o evento Click ocorre dentro da classe Button, um método é disparado para que a nossa aplicação reaja a ele; na programação assíncrona, utilizamos delegates para especificarmos callbacks, ou seja, quando o processo assíncrono finalizar, dispare o método “XPTO”.

Quando dizemos que teremos uma outra forma de trabalhar depois deste framework, é porque ele permitirá o que chamamos de LINQ To Events. Isso quer dizer que poderemos, de forma declarativa, definir uma sequência/combinação de eventos que estamos interessados, e quando ele(s) acontecer(em), nós seremos notificados. Antes de visualizar códigos que mostram isso, vamos recapitular como o Linq To Objects trabalha: basicamente tudo é baseado nas interfaces IEnumerable<T> e IEnumerator<T>, que utilizam o conceito de “pull” (puxar) para extrair os dados, iterando por alguma fonte de dados de forma síncrona.

Erik Meijer, que é o inventor do Rx Framework, descobriu que toda sequência que trabalha no formato “pull”, também pode trabalhar no formato “push” (empurrar), ou seja, são matematicamente denominadas como “Dual”. Para suportar a programação reativa, não podemos continuar utilizando as mesmas interfaces, justamente porque elas bloqueiam até que um próximo resultado esteja disponível. Sendo assim, a Microsoft introduziu duas novas interfaces: IObservable<T> e IObserver<T>. Através destas interfaces, você assinará uma coleção/tarefa qualquer, e quando uma nova informação estiver disponível, você será notificado, reagindo à este item.

Já vi comentários que isso será parte integrante ao .NET Framework 4.0, mas os tipos necessários para fazer isso tudo funcionar, estão contidos no assembly System.Reactive.dll, que vem com o Toolkit do Silverlight 3.0. A primeira interface, IObservable<T>, possui apenas um método chamado Subscribe. Este método recebe como parâmetro a instância de uma classe que implementa a interface IObserver<T>, que possui três métodos autoexplicativos: OnCompleted, OnError e OnNext. Em um exemplo simples, vamos percorrer uma coleção de números inteiros através desta nova forma. O primeiro passo é criar o observer:

public class VerificadorDeItens : IObserver<int>
{
    public void OnCompleted()
    {
        Console.WriteLine(“Acabou”);
    }

    public void OnError(Exception exception)
    {
        Console.WriteLine(“Erro”);
    }

    public void OnNext(int value)
    {
        Console.WriteLine(value);
    }
}

Depois dele devidamente criado, precisamos criar o “observable”. Ao invés de fazermos isso explicitamente, a Microsoft também disponibilizou uma classe chamada Observable, que está dentro namespace System.Linq. Essa classe fornece uma série de métodos de extensão, para transformar grande parte dos operadores LINQ em “observables”. Um desses métodos, ToObservable, é responsável por transformar um IEnumerable<T> em um IObservable<T>. Sendo assim, para percorrer um array de números inteiros no formato “push”, fazemos:

int[] ids = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ids.ToObservable().Subscribe(new VerificadorDeItens());

O que vimos acima, é somente um exemplo da diferença entre os modelos “pull” e “push”, mas o mais interessante é a capacidade de manipular, ou melhor, de reagir à eventos. Ao invés de criar os tratadores (event handlers) tradicionais, você poderá “assinar” esse evento, e quando ele acontecer, você será notificado. Veja o exemplo abaixo:

Observable
    .FromEvent<EventArgs>(this.button1, “Click”)
    .Subscribe(() => MessageBox.Show(“Ocorreu o evento Click”));

Um ponto importante aqui é que o método Subscribe retorna a instância de uma classe que implementa a interface IDisposable, que poderá ser utilizada mais tarde, quando não quisermos mais ser notificados. Abaixo temos o mesmo exemplo, só que se descartando as notificações quando não precisamos mais delas, e para isso, tudo o que precisamos fazer é invocar o método Dispose:

IDisposable dispose =
    Observable
        .FromEvent<EventArgs>(this.button1, “Click”)
        .Subscribe(() => MessageBox.Show(“Ocorreu o evento Click”));

//faz algo aqui, e quando não estiver mais
//interessado nas notificações, invoque o
//método Dispose.
dispose.Dispose();

Tudo isso visa, principalmente, a capacidade que teremos que efetuar queries LINQ em eventos, que como falei acima, permite vincular à eventos, e na medida com que eles forem sendo disparados, você será notificado de forma assíncrona. Para isso, vamos imaginar que temos duas classes diferentes, onde cada uma delas possui um evento específico, e um método que executa alguma tarefa, e quando necessário, invoca o respectivo evento. Abaixo temos as duas classes que utilizaremos como exemplo. Repare que não há nada de especial:

public class Classe1
{
    public event EventHandler MeuEvento;

    public void Executar()
    {
        if (this.MeuEvento != null)
            this.MeuEvento(this, EventArgs.Empty);
    }
}

public class Classe2
{
    public event EventHandler MeuOutroEvento;

    public void Executar()
    {
        if (this.MeuOutroEvento != null)
            this.MeuOutroEvento(this, EventArgs.Empty);
    }
}

Com essas classes criadas, vamos então utilizar o LINQ para interagir com os eventos:

Classe1 c1 = new Classe1();
Classe2 c2 = new Classe2();

IObservable<Event<EventArgs>> obsC1 =
    Observable.FromEvent<EventArgs>(c1, “MeuEvento”);

IObservable<Event<EventArgs>> obsC2 =
    Observable.FromEvent<EventArgs>(c2, “MeuOutroEvento”);

var q =
    from in obsC1
    from y in obsC2
    select new Unit();

q.Subscribe(() => MessageBox.Show(“Os eventos aconteceram!”));

c1.Executar();
c2.Executar();

Depois de instanciado a classe, utilizamos o método FromEvent, que dado o objeto e o nome do evento, cria um “observable” para o evento especificado. Com eles criados, o próximo passo é escrever a query LINQ, onde aninharemos os dois eventos na mesma query. Assim como toda query LINQ To Objects retorna um IEnumerable<T>, LINQ To Events retorna um IObservable<T>. Com isso, podemos utilizar o método Subscribe, para sermos notificados quando ambos eventos acontecerem. Assim que código executa o método “Executar” de “c1” e “c2”, a mensagem será exibida. Se comentarmos qualquer um dos métodos “Executar”, a mensagem não será disparada, pois de acordo com a query, nós estamos interessados em sermos notificados somente quando ambos eventos acontecerem.

Além de tudo isso, o .NET Reactive Framework ainda facilita muito a programação assíncrona. Sempre quando precisamos executar alguma tarefa em uma thread isolada, precisamos criar delegates, iniciar a tarefa através do método BeginInvoke, apontar (também via delegate) para o método de callback e, finalmente, recuperar o resultado utilizando o método EndInvoke. Isso é extremamente trabalhoso, e a complexidade aumenta ainda mais quando não se conhece exatamente como isso funciona. Suponhamos que temos a seguinte tarefa ser realizada:

public int Tarefa(int tempo)
{
    //Tarefa Custosa

    tempo = tempo * 1000;
    Thread.Sleep(tempo);
    return tempo;
}

Ao invés de criar toda aquela parafernalha para invocar e receber o resultado assincronamente, utilizando este novo framework, tudo o que precisamos fazer é:

new Func<int, int>(Tarefa)
    .ToAsync()
    .Invoke(3)
    .Subscribe((tempo) => { MessageBox.Show(“Tempo Esperado: ” + tempo.ToString()); });

No código acima, instanciamos o delegate Func<TResult, T> (que é bem conhecido), especificando que ele receberá e retornará um número inteiro. A partir daí, entra em cena o método de extensão chamado ToAsync (que foi aplicado aos delegates), que retorna uma versão assíncrona do mesmo delegate, e como já vimos acima, utilizamos o método Subscribe para criar o “observable”, que nos trará o resultado do processo, isso quando estamos interessados nele.

Esses são apenas alguns – pequenos – exemplos de alguns cenários que podemos utilizar esta novo framework, mas já podemos notar o quanto ganhamos em certas situações, como é o caso da programação assíncrona. Há muito ainda o que se explorar e, provavelmente, isso tende a evoluir cada vez mais, tornando assim, a programação, ou melhor, o C#, cada vez mais funcional.

Adicionando Membros em Tempo de Execução

Como sabemos, o C# 4.0 (que virá junto com o Visual Studio .NET 2010) já trará alguns aspectos de linguagens dinâmicas, que permitem a avaliação/checagem de um membro somente em tempo de execução. Para suportar esta funcionalidade, um novo “tipo” foi criado, conhecido como “dynamic”. Ao declarar uma variável do tipo “dynamic”, a checagem de existência de um determinado membro não acontecerá estaticamente, ou seja, independentemente se ele exista ou não, você somente saberá isso em tempo de execução.

Com este tipo especial, você somente conseguirá acessar os membros que, eventualmente, já existam. Mas e como você pode proceder, se quiser construir um tipo dinamicamente? É aqui que entra em cena a classe ExpandoObject (namespace System.Dynamic). Ao instanciar essa classe, você poderá criar tipos dinamicamente, definindo novas propriedades e métodos para este tipo. Para que isso funcione adequadamente, você precisará atribuir a instância desta classe à uma variável do tipo “dynamic”, caso contrário, como vimos acima, a checagem será efetuada durante a compilação, o que não permitirá a compilação. O código abaixo ilustra a utilização desta nova classe:

dynamic obj = new ExpandoObject();
obj.Nome = “Israel”;
obj.Endereco = new ExpandoObject();
obj.Endereco.Cidade = “Valinhos”;
obj.AlgumMetodo = new Action<string>(s => Console.WriteLine(s));

Console.WriteLine(obj.Endereco.Cidade);
obj.AlgumMetodo(“Teste”);

Como podemos notar, instanciamos a classe ExpandoObject para uma variável definida como dinâmica. Depois disso, quando queremos criar “sub-tipos”, basta instanciar uma nova classe ExpandoObject para ele, e seguir criando os novos membros a partir dali. Métodos também podem ser criados, e você pode recorrer a algum delegate já existente para definir a ação a ser executada quando ele for invocado.

Na verdade, esses membros não são incluídos dentro da classe ExpandoObject. A medida que você vai criando estes membros, ele irá armazenando em um dicionário, e quando requisitado, extrai e o executa. Essa funcionalidade é disponibilizada através da Interface IDynamicMetaObjectProvider. Acredito que a performance deva ser mais lenta do que o binding estático, mas é um preço que se deve pagar quando quiser interoperar com linguagens dinâmicas que, além da interoperabilidade com o mundo COM é, ao meu ver, os principais cenários para códigos deste tipo.

Movendo tipos entre Assemblies

Muitas vezes, quando estamos desenvolvendo algum componente, geralmente colocamos todos os tipos (classes, estruturas, interfaces, etc.) dentro do mesmo assembly, sem nos preocuparmos com possíveis reutilizações de tipos que, futuramente, possam acontecer. Uma vez que temos esse componente pronto, o referenciamos em diversas aplicações, que farão uso dos tipos que o mesmo fornece.

Mais tarde, você começa a notar a necessidade de precisar fazer uso destes tipos em outros assemblies que você venha a criar. Sim, referenciar esse primeiro assembly, que contém todos os tipos, resolveria o problema, mas você pode estar referenciando assemblies com tipos que, na verdade, não deveriam estar a disposição de certas aplicações.

Refatorar isso, levando esses tipos para outros assemblies é uma tarefa relativamente fácil, pois basta você tirar de um e colocar noutro. O problema é manter a compatibilidade com as aplicações que já usam esses tipos que serão movidos. Imagine que você tenha um componente chamado Componente1.dll com uma classe chamada DalHelper. Uma aplicação Windows (App.exe) referencia esse assembly e faz uso da classe DalHelper. E como falmos acima, mais tarde você achou necessário mover a essa classe para um outro assembly (Componente2.dll), que ficará mais coerente e facilitará a reutilização.

Se você remover a classe DalHelper, as aplicações que dependem daquela classe dentro do assembly Componente1.dll, irão quebrar. É importante dizer que a idéia aqui é você fazer essa alteração (mover o(s) tipo(s) entre assemblies), sem precisar redistribuir/recompilar as aplicações que já utilizam o Componente1.dll. E para resolver isso, utilizamos o atributo TypeForwardedToAttribute (namespace System.Runtime.CompilerServices), disponível a partir do .NET Framework 2.0. Quando aplicado este atributo em um assembly, ele redirecionará a requisição de um determinado tipo para algum outro lugar, outro assembly. Para ilustrarmos, imagine a seguinte situação:

[Componente1.dll]
namespace Common
{
    public class DalHelper
    {
        public string Execute() { }
    }
}

[App.exe]
Common.DalHelper dal = new Common.DalHelper();
MessageBox.Show(dal.Execute());

O que vamos fazer agora é criar um outro assembly (Componente2.dll), e mover a classe DalHelper para dentro ele, removendo essa classe do Componente1.dll. O código abaixo exibe a estrutura do assembly recém criado.

[Componente2.dll]
namespace Common
{
    public class DalHelper
    {
        public string Execute() { }
    }
}

Isso não basta, e quebrará as referências que existem para essa classe. No Componente1.dll ainda precisamos, explicitamente, direcionar a requisição desta classe, utilizando o atributo TypeForwardedToAttribute. Lembre-se que devemos referenciar o Componente2.dll no Componente1.dll. O código abaixo mostra como utilizar esse atributo, e note que a classe DalHelper que está referenciada está, agora, no Componente2.dll.

[assembly: TypeForwardedTo(typeof(Common.DalHelper))]

Para aquelas aplicações que referenciam o DalHelper do Compenente1.dll, ao enviar o Componente1.dll e Componente2.dll, você pode simplesmente substituir o Componente1.dll que não quebrará o binding, já que quando a requisição para a classe DalHelper chegar à ele, ele sabe onde estará esse tipo, e encaminhará para ele, e como podemos perceber, as aplicações continuam confiando apenas no Componente1.dll.

Podemos perceber que isso ajuda imensamente para refatorar o código e, principalmente, manter a compatibilidade com as aplicações que já fazem uso desses tipos. Novas aplicações que precisarem da classe DalHelper, tudo o que elas precisam fazer é referenciar apenas o assembly Componente2.dll. Mais fácil do que isso é tentar prever essa reusabilidade, criando os tipos separadamente antes mesmo de fazer a distribuição inicial.

Disponibilizando o WSDL Out-Of-Band

Quando criamos um serviço WCF, geralmente disponibilizamos um endpoint conhecido como MEX (Metadata Exchange), que tem a responsabilidade de expor o documento WSDL, para que os clientes sejam capazes de utilizar as informações fornecidas por ele, para criar toda a estrutura necessária para efetuar a comunicação com o respectivo serviço.

Os interessados em consumir aquele serviço, podem fazer uso de ferramentas que, dado o endereço até o documento WSDL, irão gerar o proxy localmente. Quando você faz a referência através do Visual Studio .NET ou do svcutil.exe, ao colocar o endereço do serviço, ele sai em busca de um endpoint que fornece o documento WSDL. Se esse endpoint não for encontrado, ele não conseguirá extrair as informações e, consequentemente, você não conseguirá referenciar o serviço.

Publicar o WSDL através de um endpoint é bem simples, mas temos sempre que diminuir a superfície de ataque, para assim evitar problemas que possam acontecer. Uma alternativa e tema deste artigo, é distribuir o documento WSDL “out-of-band”. Com isso, podemos enviar ou disponibilizar o documento que descreve o serviço para a pessoa certa, ou invés de deixar que todos o acessem.

Para isso, uma vez que o seu serviço estiver rodando, você pode extrair os documentos que o descrevem a partir do utilitário svcutil.exe. Ele tem uma opção que permite a exportação do WSDL, salvando isso em documentos locais, para que mais tarde você envie/disponibilize para os seus clientes. Sendo assim, tudo o que precisamos fazer é utilizar a opção /t:metadata, assim como é mostrado abaixo:

C:Temp>svcutil.exe /t:metadata http://localhost:9298/

Com isso, arquivos WSDL e XSD (que descrevem os tipos utilizados pelo serviço) serão gerados localmente. Quando o cliente receber esses arquivos, ele poderá gerar o proxy a partir do mesmo utilitário, só que ao invés de passar o endereço do serviço, ele informará o caminho até os documentos WSDL/XSD que compõem toda a descrição. Para gerar o proxy, informe o local dos arquivos e a linguagem, assim como é mostrado abaixo:

C:Temp>svcutil *.wsdl *.xsd /language:C#

Depois de executar esse comando, dois arquivos serão criados, sendo um deles o proxy em si e o outro o arquivo *.config, contendo todas as configurações necessárias para efetuar a comunicação com o serviço. Feito isso, tudo o que resta fazer, é adicionar esses arquivos na aplicação que irá consumir o serviço, e como o proxy nada mais é do que uma classe local, basta instanciá-la e invocar as operações.