Desenvolvimento - ASP. NET

Client Application Services

Client Application Services trata-se de um novo conjunto de funcionalidades que habilita os mais diversos tipos de aplicações a autenticar usuários, recuperar suas respectivas roles e ainda persistir informações de cada um desses usuários em um servidor qualquer.

por Israel Aéce



function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i A nova versão do Visual Studio (codename "Orcas") traz uma série de novas funcionalidades a nível de linguagens e de aplicações. No caso das linguagens, podemos citar algumas técnicas que já foram abordadas neste outro artigo quando falamos sobre o LINQ. Quando falamos a nível de aplicações, muitas funcionalidades foram criadas e, uma delas, que é chamada de Client Application Services, é tema deste artigo.

Client Application Services trata-se de um novo conjunto de funcionalidades que habilita os mais diversos tipos de aplicações a autenticar usuários, recuperar suas respectivas roles e ainda persistir informações de cada um desses usuários em um servidor qualquer. A principal vantagem do Client Application Services é que ele trabalha em conjunto com uma aplicação Web, ou seja, você pode ter uma aplicação Web que disponibiliza todos esses serviços para os usuários que a acessam e também disponibilizar essa mesma infra-estrutura para que, por exemplo, uma aplicação Windows Forms possa fazer o uso disso, centralizando todo o acesso e gerenciamento de usuários em um único local.

É importante dizer que este artigo está baseado na versão Beta 1 do "Orcas" (versão 3.5 do .NET Framework) e está sujeito a mudanças até que a versão final seja lançada. A maior parte de todas as classes que precisamos manipular para habilitar esse recurso estão contidas dentro do namespace System.Web.Extensions (System.Web.Extensions.dll) que também possui toda a funcionalidade server-side do ASP.NET AJAX e que, por padrão, é automaticamente adicionada ao GAC (Global Assembly Cache) durante a instalação do .NET Framework.

As classes que iremos utilizar no decorrer do artigo estão (grande parte delas) divididas entre três principais namespaces, a saber: System.Web.ApplicationServices, System.Web.ClientServices e System.Web.ClientServices.Providers. O primeiro deles, System.Web.ApplicationServices, contém classes que são utilizadas do lado do servidor e que são a "ponte" entre a requisição e o recurso solicitado; já o namespace System.Web.ClientServices possui duas classes que representam uma principal e uma identity para as aplicações que fazem o uso do Client Application Services e, além delas, há uma classes para trabalho offline que veremos mais tarde, ainda neste artigo. Finalmente, temos o namespaceSystem.Web.ClientServices.Providers que, por sua vez, além de possuir várias classes importantes para o runtime fornece os providers para que o cliente possa especificar na aplicação. Isso é algo bem semelhante ao que já acontece com a configuração do membership em uma aplicação Web, com exceção de que a requisição é encaminhada para o servidor que centraliza esses dados. A imagem abaixo ilustra todas as classes que compõem cada um desses namespaces:

Figura 1 - Classes que compõem os namespaces que citamos acima.

Para que possamos disponibilizar essa funcionalidade, devemos primeiramente configurar a aplicação Web, ou seja, definindo os providers de membership, roles e profile. Neste passo não há nenhum segredo em relação ao que já fazíamos na versão 2.0 do ASP.NET. Sendo assim, eu irei assumir que você já tenha os devidos conhecimentos com relação a isso e, caso não possua, aconselho ler este artigo antes de prosseguir com esta leitura.

Já para o nosso exemplo eu irei utilizar os providers que se baseiam em um banco de dados SQL Server, a saber: SqlMembershipProvider, SqlRoleProvider e SqlProfileProvider. O artigo faz uso de uma base de dados chamada aspnetdb.mdf que faz o uso do SQL Server 2005 Express e foi executado o utilitário aspnet_regsql.exe para criar toda a infra-estrutura necessária dentro da base de dados para suportar todas essas funcionalidades. É importante dizer também que você não verá explicitamente definido cada um dos providers no arquivo Web.Config pois, como estou utilizando as configurações padrões, todos eles já estão definidos "em nível de máquina", ou melhor, no arquivo machine.config.

Quando criamos uma aplicação Web dentro do "Orcas", uma nova seção é embutida por padrão dentro do arquivo Web.Config da aplicação. Esta seção é chamada de system.web.extensions e possui alguns elementos que iremos utilizar neste artigo para que seja possível habilitar as funcionalidades (profile, membership e role) para o Client Application Services. Entre esses atributos, temos:

Elemento Descrição
profileService Permite habilitar o profile para que as aplicações o utilizem. Esse elemento possui um atributo chamado enabled que recebe um valor booleano indicando se o mesmo está ou não ativo. Além deste atributo, ele ainda possui dois outros, não menos importantes, que são listados abaixo:

  • readAccessProperties: Recebe todas as propriedades, separadas por vírgula, que somente terão permissão para leitura. Essas propriedades devem estar especificadas no profile da aplicação Web.
  • writeAccessProperties: Recebe todas as propriedades, separadas por vírgula, que somente terão permissão para escrita. Essas propriedades devem estar especificadas no profile da aplicação Web.
authenticationService Esse elemento também contém um atributo chamado enabled que indica se o serviço de autenticação está ou não ativo.
roleService Assim como os elementos anteriores, o roleService também conta com um atributo chamado enabled que indica se o serviço de roles está ou não ativo.
function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i

Formas de Acesso

Esses serviços já estão disponíveis desde as primeiras versões do Atlas que, por sua vez, fornece um proxy que habilita as aplicações baseadas em browser (que também é uma aplicação cliente) acessar esses recursos sem a necessidade de causar postbacks. Se analisarmos os internals, veremos que o trabalho de executar o processamento destes serviços está contido dentro do módulo chamado ScriptModule (namespace System.Web.Handlers). Veremos mais detalhes sobre essa forma de acesso um pouco mais adiante, quando falaremos sobre o consumo dessas funcionalidades em aplicações Windows. Além da forma de acesso anterior, também é possível acessá-los através do WCF (Windows Communication Foundation) onde você pode, via endpoints, expor essas funcionalidades.

Independente de qual seja a forma de acesso que você estiver utilizando, há classes dentro do namespace System.Web.ApplicationServices que contém toda a infra-estrutura para tratar tais requisições. Essas classes são AuthenticationService, RoleService e ProfileService, que já estão devidamente decorados com o atributo ServiceContract para suportar a exposição via WCF. Essas classes delegam a cada um dos providers concretos o trabalho de autenticação, autorização e gerenciamento dos profiles, verificando antes se o recurso está ou não habilitado, verificação que é feita baseando-se nos atributos enabled de cada um dos elementos da tabela acima.

O código abaixo ilustra o arquivo Web.Config da aplicação Web que irá disponibilizar os serviços para que os clientes possam consumí-los; mas vale lembrar que alguns trecho do arquivo foram suprimidos para poupar espaço. Se repararmos, há uma seção chamada system.web.extensions que contém todas as configurações que habilitam cada um dos serviços que desejamos disponibilizar.

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="false">
      <assemblies>
        <add assembly="System.Core"/>
        <add assembly="System.Web.Extensions" />
      </assemblies>
    </compilation>
    <authentication mode="Forms" />
    <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add
          verb="*"
          path="*.asmx"
          validate="false"
          type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions" />
      <add
          verb="*"
          path="*_AppService.axd"
          validate="false"
          type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions" />
      <add
          verb="GET,HEAD"
          path="ScriptResource.axd"
          type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions" />
    </httpHandlers>
    <httpModules>
      <add
          name="ScriptModule"
          type="System.Web.Handlers.ScriptModule, System.Web.Extensions" />
    </httpModules>
    <anonymousIdentification enabled="true"/>
    <profile enabled="true">
      <properties>
        <add
            name="Parametro"
            type="System.String"
            allowAnonymous="true"/>
      </properties>
    </profile>
  </system.web>
  <system.web.extensions>
    <scripting>
      <webServices>
        <profileService
            enabled="true"
            readAccessProperties="Parametro"
            writeAccessProperties="Parametro" />
        <authenticationService
            enabled="true"
            requireSSL="false" />
        <roleService
            enabled="true"/>        
      </webServices>
    </scripting>
  </system.web.extensions>
</configuration>
Web.config

Depois dessa configuração, agora já podemos utilizar os recursos habilitados em nossas aplicações clientes. O "Orcas" acrescentou nas propriedades de vários projetos, entre eles Windows Forms e WPF, uma aba chamada Services. É dentro desta aba que vamos habilitar a utilização dos recursos que queremos acessar em nossa aplicação Windows. A imagem abaixo ilustra essa nova tela:

Figura 2 - Aba Services em projetos cliente.

Como podemos reparar, há um CheckBox que habilita a utilização do Client Application Services. Quando habilitado, todos os campos que estão abaixo ficam disponíveis para que você possa especificar a URL de cada Web site onde está cada uma das respectivas funcionalidades (authentication, authorization e profile). Como no caso do exemplo todas as funcionalidades estão contidas dentro de um único site, a mesma URL é especificada em todos os campos. Uma vez que você salvar as informações, um arquivo App.Config é criado com a especificação dos providers, assim como uma aplicação Web. O código abaixo mostra o arquivo App.Config criado depois de definido os application services:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add
      key="ClientSettingsProvider.ServiceUri"
      value="http://localhost/Beta.WebUI/Profile_JSON_AppService.axd" />
  </appSettings>
  <system.web>
    <membership defaultProvider="ClientAuthenticationMembershipProvider">
      <providers>
        <add
      name="ClientAuthenticationMembershipProvider"
      type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, 
      System.Web.Extensions"
          serviceUri="http://localhost/Beta.WebUI/Authentication_JSON_AppService.axd"
          credentialsProvider="Beta.WinUI.Form1, Beta.WinUI" />
      </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
      <providers>
        <add
          name="ClientRoleProvider"
          type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions"
          serviceUri="http://localhost/Beta.WebUI/Role_JSON_AppService.axd"
          cacheTimeout="86400" />
      </providers>
    </roleManager>
  </system.web>
</configuration>
App.config

Analisando o código acima, temos uma key chamada ClientSettingsProvider.ServiceUri que armazena o endereço até o local onde o profile está sendo disponibilizado. Em seguida, temos uma infra-estrutura de elementos muito semelhante ao que já existe atualmente em uma aplicação Web, que são os elementos membership e roleMananger. A diferença aqui é que eles não utilizam um provider como SqlMembershipProvider ou o SqlRoleProvider e sim os providers que estão definidos dentro do namespace System.Web.ClientServices.Providers. Como optamos pela autenticação via FormsAuthentication, estaremos utilizando no exemplo o provider ClientFormsAuthenticationMembershipProvider e também o ClientRoleProvider para o gerenciamento das roles. A principal diferença nestes providers é que eles invocam o método remotamente, e a forma de acesso dependerá de como você disponibilizou essas funcionalidades na aplicação Web, ou seja, da forma "tradicional", que é através de uma requisição HTTP (via classes HttpWebRequest e HttpWebResponse), serializando os dados utilizando JSON ou, se desejar, via WCF. É importante mencionar que é necessário que você faça referência aos Assemblies System.Web.dll e System.Web.Extensions.dll na aplicação cliente. function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i Em ambos os casos, membership e roleManager, há um atributo chamado serviceUri que especifica o URL até a aplicação que disponibiliza o serviço. São os valores definidos para este atributo que o runtime determina para onde e como fazer a requisição. Se o endereço finalizar com *.svc (que identifica um endpoint WCF) a requisição é realizada através do WCF; caso contrário, a requisição é feita via requisição HTTP/JSON, e o que determinará qual será o serviço a ser executado (membership, roles ou provider) é o "nome do recurso" solicitado: Authentication_JSON_AppService.axd, Role_JSON_AppService.axd ou Profile_JSON_AppService.axd.

No caso do elemento membership há um atributo que merece ser comentado. Trata-se do atributo credentialsProvider, que também pode ser definido a partir da aba Service que vimos um pouco acima. Esse atributo permite-nos definir o Type de algum objeto da aplicação que fornecerá uma instância do tipo ClientFormsAuthenticationCredentials representando as credenciais do usuário, que será enviada para a aplicação Web quando o método de Login for invocado. O tipo que for definido obrigatoriamente deve implementar a Interface IClientFormsAuthenticationCredentialsProvider que possui apenas um método chamado GetCredentials que, muitas vezes, é utilizado no formulário de login da aplicação. O trecho de código abaixo ilustra a utilização dessa técnica em um formulário Windows Forms que implementa a Interface IClientFormsAuthenticationCredentialsProvider:

using System.Windows.Forms;
using System.Web.Security;
using System.Web.ClientServices.Providers;

public partial class Login : Form, IClientFormsAuthenticationCredentialsProvider
{
    private void btnLogin_Click(object sender, EventArgs e)
    {
        if(Membership.ValidateUser(string.Empty, string.Empty))
        {
            string[] roles = Roles.GetRolesForUser("IA");
            this.lblMensagem.Text = "Usuário válido";
        }
        else
        {
            this.lblMensagem.Text = "Usuário inválido";
        }
    }

    public ClientFormsAuthenticationCredentials GetCredentials()
    {
        return new ClientFormsAuthenticationCredentials(
            this.txtLogin.Text.Trim(), 
            this.txtPassword.Text.Trim(),
            false);
    }
}
Imports System.Windows.Forms
Imports System.Web.Security
Imports System.Web.ClientServices.Providers

Public Partial Class Login
    Inherits Form
    Implements IClientFormsAuthenticationCredentialsProvider

    Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        If Membership.ValidateUser(String.Empty, String.Empty) Then
            Dim roles() As String = Roles.GetRolesForUser("IA")
            Me.lblMensagem.Text = "Usuário válido"
        Else
            Me.lblMensagem.Text = "Usuário inválido"
        End If
    End Sub

    Public Function GetCredentials() As ClientFormsAuthenticationCredentials _
        Implements IClientFormsAuthenticationCredentialsProvider.GetCredentials

        Return New ClientFormsAuthenticationCredentials( _
            Me.txtLogin.Text.Trim(), _
            Me.txtPassword.Text.Trim(), _
            False);
    End Function
End Class
C# VB.NET

Dentro do evento Click do botão btnLogin invocamos o método estático ValidateUser da classe Membership (System.Web.Security) que retorna um valor booleano indicando se as credenciais informadas são ou não válidas. O ponto curioso é que ao invés de passarmos o login e senha para este método, passamos os valores em branco. Isso acontece porque as credenciais serão extraídas automaticamente a partir do tipo especificado no atributo credentialsProvider do elemento membership.

Além disso, podemos reparar que fazemos a utilização das classes Membership e Roles, assim como já acontece em aplicações Web. A classe Membership trata-se de estática que possui um membro interno chamado s_Provider do tipo MembershipProvider (a classe base para as classes concretas de Membership, como por exemplo o SqlMembershipProvider), o qual receberá a instância da classe concreta. Essa inicialização acontece quando um método interno chamado Initialize desta mesma classe é executado. Ele se encarrega de extrair o provider especificado no arquivo Web.Config (que no nosso caso é o ClientFormsAuthenticationMembershipProvider) e instanciá-los para que, quando chamarmos os métodos e propriedades, já sejam efetivamente os métodos e propriedades da classe concreta que queremos utilizar. O funcionamento é também semelhante para a classe Roles.

Todos os providers específicos e que são utilizados pelo Client Application Services adotam a mesma hierarquia em relação aos outros providers que já conhecemos, ou seja, no caso do membership, as classes ClientFormsAuthenticationMembershipProvider e ClientWindowsAuthenticationMembershipProvider herdam de uma classe abstrata chamada MembershipProvider; já a classe ClientRoleProvider herda da classe abstrata RoleProvider e, finalmente, a classe ClientSettingsRoleProvider herda diretamente de SettingsProvider. Como podemos perceber, os providers que são prefixados com o valor "Client" são os providers utilizados pelas Client Applications e estão disponíveis dentro do namespace System.Web.ClientServices.Providers. A imagem abaixo ilustra a hierarquia destas classes:

Figura 3 - Hierarquia dos providers cliente.

A diferença entre os providers de autenticação (ClientFormsAuthenticationMembershipProvider e o ClientWindowsAuthenticationMembershipProvider) é que o primeiro deles, deve ser utilizado em aplicações cliente quando a forma de autenticação da aplicação Web é via FormsAuthentication; já a segunda é quando a forma de autenticação da aplicação Web se baseia na autenticação do Windows.

Importante: É importante dizer que apesar de vários métodos da classe Membership e Roles estarem disponíveis, muitos deles não são suportados "by design". Sendo assim, atente-se em chamar os métodos destas classes e certifique-se de que eles realmente possuem a implementação da funcionalidade a qual cada um deles se destina a realizar.

Para finalizar a parte de autenticação e autorização, não podemos deixar de comentar sobre o ClientFormsIdentity e o ClientRolePrincipal. Essas duas classes implementam as Interfaces IIdentity e IPrincipal, respectivamente. O primeiro deles, ClientFormsIdentity, representa a identidade do usuário que efetuou o login na aplicação; já o ClientRolePrincipal refere aos direitos que o usuário possui na aplicação. A instância da classe ClientFormsIdentity é definida automaticamente para a propriedade CurrentPrincipal da classe Thread quando o login é efetuado com sucesso. Essas duas classes estão contidas dentro do namespace System.Web.ClientServices.

Através da Figura 2 podemos notar que o último campo que consta na aba Services é onde devemos informar a URL para o site que disponibiliza as informações de profile. O funcionamento interno para requisitar as informações remotas é bem parecido ao que já acontece com o membership e as roles. A novidade aqui é que, na aba Settings das propriedades do projeto, existe um novo botão chamado Load Web Settings que, através da URL informada anteriormente, irá requisitar as informações (inclusive metadados) e para cada propriedade que está sendo exposta pelo profile da aplicação Web requisitada, ele os criará na seção de Settings da aplicação. function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i Para todo item criado na seção Settings, independente se ele é ou não oriundo do profile de uma aplicação Web, é sempre criado uma propriedade com o mesmo nome e tipo em um arquivo (Visual C# ou Visual Basic .NET) de "code-behind". O que difere entre as propriedades "locais" e as propriedades que foram importadas do projeto Web é que, neste segundo caso, ela é decorada com um atributo chamado SettingsProviderAttribute, passando para o seu construtor o provider onde essa informação será efetivamente armazenada que, no nosso caso, é o ClientSettingsProvider. A imagem exibe a aba Settings em que podemos importar as propriedades do profile da aplicação Web previamente especificada na aba Services.

Figura 4 - Importando as propriedades da aplicação Web para a aplicação cliente.

Com isso realizado, já posso acessar ou definir as informações nessas propriedades (dependendo do que foi definido na aplicação Web). Para isso, basta simplesmente acessar como já estávamos fazendo com uma propriedade simples. O exemplo abaixo está também baseado no arquivo Web.Config da aplicação Web que foi criada mais acima. Lá você poderá comprovar a existência da propriedade chamada Parametro que foi definida no profile.

Properties.Settings.Default.Parametro = "123";
MessageBox.Show(Properties.Settings.Default.Parametro);
Properties.Settings.Default.Parametro = "123"
MessageBox.Show(Properties.Settings.Default.Parametro)
C# VB.NET

Acessando via WCF

Como foi mencionado acima, é possível também expor as funcionalidades de membership, roles e profiles a partir de serviços WCF (*.svc). Isso exigirá uma ligeira mudança na aplicação Web que expõe essas funcionalidades, ou seja, será necessário adicionar a este mesmo projeto um arquivo com extensão *.svc para cada uma das funcionalidades que deseja disponibilizar.

Para fazer o mesmo exemplo que vimos acima, mas utilizando WCF, devemos adicionar três arquivos ao projeto Web. Chamaremos cada um desses arquivos de: AuthenticationService.svc, RoleService.svc e ProfileService.svc. Para cada um desses arquivos é necessário alterar o valor do atributo Service da diretiva do mesmo, informando o tipo que será exposto. Como cada um dos arquivos tem uma finalidade diferente, então a configuração para cada um deles ficará:

//Arquivo AuthenticationService.svc
<% @ServiceHost Language="C#" Debug="true"
        Service="System.Web.ApplicationServices.AuthenticationService" %>

//Arquivo RoleService.svc
<% @ServiceHost Language="C#" Debug="true"
        Service="System.Web.ApplicationServices.RoleService" %>

//Arquivo ProfileService.svc
<% @ServiceHost Language="C#" Debug="true"
        Service="System.Web.ApplicationServices.ProfileService" %>
Arquivos *.svc

A única diferença que muda no código acima para uma aplicação que está sendo desenvolvida em Visual Basic .NET é que o atributo Language deve ser definido com o valor "VB". Neste cenário não precisamos nos preocupar com a infra-estrutura necessária (atributos ServiceContract e OperationContract) para expor uma classe via WCF, pois tudo isso já está definido nas classes AuthenticationService, RoleService e ProfileService.

Uma vez definidos os arquivos físicos, é necessário configurar os endpoints no arquivo Web.Config da mesma aplicação. Neste momento vamos definir para cada tipo de serviço o seu respectivo nome, binding e o contrato. Como o foco não é explicar cada um dos detalhes do WCF, vou assumir que você já conheça essas técnicas. Caso você ainda não esteja familiarizado com o WCF, então é necessário que você leia algum material a respeito para que possa entender o seu funcionamento e ajustar as configurações do mesmo de acordo com a sua necessidade.

O trecho de código abaixo ilustra todos os endpoints necessários para expor cada uma das funcionalidades que vimos até o momento. Reparem que o contrato que está definido para cada um desses endpoints reflete exatamente no nome da classe concreta que, como já sabemos, já expõe o contrato porque esta decorada com os atributos necessários.

<?xml version="1.0"?>
<configuration>
  <system.web>
    <authentication mode="Forms" />
    <anonymousIdentification enabled="true"/>
    <profile enabled="true">
      <properties>
        <add name="Parametro" type="System.String" allowAnonymous="true"/>
      </properties>
    </profile>
  </system.web>
  <system.web.extensions>
    <scripting>
      <webServices>
        <profileService
            enabled="true"
            readAccessProperties="Parametro"
            writeAccessProperties="Parametro" />
        <authenticationService
            enabled="true"
            requireSSL="false" />
        <roleService
            enabled="true"/>        
      </webServices>
    </scripting>
  </system.web.extensions>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <services>
      <service
        behaviorConfiguration="AuthenticationBehavior"
        name="System.Web.ApplicationServices.AuthenticationService">
        <endpoint
          binding="basicHttpBinding"
          name="AuthenticationService"
          contract="System.Web.ApplicationServices.AuthenticationService" />
      </service>
      <service
        behaviorConfiguration="RoleBehavior"
        name="System.Web.ApplicationServices.RoleService">
        <endpoint
          binding="basicHttpBinding"
          name="RoleService"
          contract="System.Web.ApplicationServices.RoleService" />
      </service>
      <service
        behaviorConfiguration="ProfileBehavior"
        name="System.Web.ApplicationServices.ProfileService">
        <endpoint
          binding="basicHttpBinding"
          name="ProfileService"
          contract="System.Web.ApplicationServices.ProfileService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="AuthenticationBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="RoleBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="ProfileBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Web.Config - WCF

Importante: Mesmo fazendo o acesso via WCF, é necessário que as funcionalidades que vão ser disponibilizadas estejam devidamente habilitadas. Caso contrário, uma exceção do tipo InvalidOperationException é atirada para o cliente, informando que o recurso está desabilitado.

A configuração do Client Application Services quando estamos expondo as funcionalidades através de WCF é exatamente a mesma quando estamos falando do lado do cliente (quem consome essas funcionalidades) em relação ao exemplo anterior. Basicamente, a única diferença está na configuração do arquivo App.Config do lado do cliente onde, no atributo serviceUri, você deverá especificar a URL do serviço WCF (*.svc) ao invés do *.axd, que é colocado por padrão quando não é informado. Internamente, o runtime determina a forma de requisição analisando a extensão do recurso (arquivo) informado neste atributo.

Versão Beta 1 - Orcas

A configuração do Client Application Services utilizando WCF como forma de acesso infelizmente não está funcionando na versão Beta 1 do Visual Studio Orcas. Apesar da configuração externa ser o que vimos acima, internamente há alguns problemas com relação ao contrato dos serviços que impedem que a requisição seja efetuada com sucesso. Inclusive essa falha já foi reportada para a Microsoft e, provavelmente, será consertada nas próximas versões.


Se desejarmos, há uma alternativa onde podemos acessar os serviços WCF diretamente, assim como já fazemos. Essa técnica permitirá acessarmos as funcionalidades que estão sendo disponibilizadas pela aplicação Web. O ponto negativo que vejo aqui é que, desta forma, nós ficamos responsáveis por criar e definir a identity e a principal da thread que é encarregada pela execução da aplicação, o que acontece automaticamente quando utilizamos o ClientFormsAuthenticationMembershipProvider. Uma vez que referenciamos todos os serviços em um único cliente, temos:

Figura 5 - Serviços WCF instalados no cliente.

Com os serviços WCF devidamente referenciados na aplicação cliente, antes de consumí-los dentro da mesma é necessário abrir o arquivo App.Config desta mesma aplicação e definir o atributo allowCookies do binding especificado para true para que seja possível o WCF armazenar cookies no cliente e utilizá-los em futuras requisições. Depois disso, basta instanciarmos as classes que, na verdade, são os proxies de cada um dos serviços e chamar seus respectivos métodos. O trecho de código abaixo ilustra como devemos proceder para validar e autenticar um determinado usuário:

using (AuthenticationService.AuthenticationServiceClient auth =
    new AuthenticationService.AuthenticationServiceClient())
{
    if (auth.ValidateUser("Israel", "P@$$w0rd", string.Empty) &&
        auth.Login("Israel", "P@$$w0rd", string.Empty, true))
    {
        try
        {
            //Se o atributo allowCookies for definido como
            //False, o valor retornado pelo método IsLoggedIn
            //também será False.

            Console.WriteLine(auth.IsLoggedIn().ToString());
        }
        finally
        {
            auth.Logout();
        }
    }
}
Using auth As New AuthenticationService.AuthenticationServiceClient()
    If auth.ValidateUser("Israel", "P@$$w0rd", String.Empty) AndAlso _
        auth.Login("Israel", "P@$$w0rd", String.Empty, True) Then
        Try
            "Se o atributo allowCookies for definido como
            "False, o valor retornado pelo método IsLoggedIn
            "também será False.

            Console.WriteLine(auth.IsLoggedIn().ToString())
        Finally
            auth.Logout()
        End Try
    End If
End Using
C# VB.NET

Trabalhando em modo Offline

Um recurso que o Client Application Services também já possui é a possibilidade de trabalhar em modo offline. Esse recurso possibilita efetuar uma requisição para um serviço (seja ele membership, roles ou profile). Enquanto estiver no modo online, os dados relevantes são armazenados localmente para quando estiver offline. Quando você está em modo offline não haverá possibilidade de acessar um determinado recurso e, sendo assim, a sua aplicação poderia falhar. Felizmente o armazenamento local facilita isso, sem a necessidade de termos que escrever código para serializar/deserializar os objetos.

Há dois tipos de repositórios que são disponibilizados por esse recurso: sistema de arquivos ou um banco de dados local. Por padrão, será utilizado o sistema de arquivos, mas você pode determinar uma base de dados local (SQL Server Express) para armazenar essas informações. Para isso, você deve ir novamente até a aba Services do projeto cliente e, ao habilitar o Client Application Services, um botão chamado Advanced será habilitado e, ao clicar, você terá acesso a tela mostrada na figura abaixo, onde você pode marcar a opção User a database instead of the file system for local data storage. Ao marcá-la, o campo abaixo será disponibilizado para que você informe a connection string até o mesmo.

Figura 6 - Habilitando o armazenamento em um banco de dados.

Finalmente, o que vai determinar se a aplicação está ou não online é através da propriedade IsOffline da classe estática ConnectivityStatus que, por sua vez, está contida dentro do namespace System.Web.ClientServices. Quando desejar, basta você atribuir o valor True a esta propriedade, e assim os dados passarão a ser resgatados/atualizados localmente, dentro do repositório selecionado.

Conclusão: Uma das principais features que foram pedidas em fóruns e talvez à própria Microsoft era uma forma simples de conseguir integrar aplicações clientes, como Windows Forms, com toda a extensível infra-estrutura fornecida, inicialmente, para uma aplicação Web, que são os Provider Models. Felizmente isso foi acatado como uma necessidade pela Microsoft e nesta nova versão do .NET Framework/Visual Studio .NET já está disponível para a utilização e, no decorrer deste artigo, pudemos entender um pouco mais sobre suas configurações e também o seu comportamento interno.
Israel Aéce

Israel Aéce - Especialista em tecnologias de desenvolvimento Microsoft, atua como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. Como instrutor Microsoft, leciona sobre o desenvolvimento de aplicações .NET. É palestrante em diversos eventos Microsoft no Brasil e autor de diversos artigos que podem ser lidos a partir de seu site http://www.israelaece.com/. Possui as seguintes credenciais: MVP (Connected System Developer), MCP, MCAD, MCTS (Web, Windows, Distributed, ASP.NET 3.5, ADO.NET 3.5, Windows Forms 3.5 e WCF), MCPD (Web, Windows, Enterprise, ASP.NET 3.5 e Windows 3.5) e MCT.