Pesquisar este blog

5 de março de 2010

C# + Remoting + Generics – Framework 2.0

Durante um projeto encontramos com o seguinte problema:

1 – As camadas da aplicação são divididas em projetos separados para facilitar o versionamento e o reuso em outros projetos e devido ao núcleo (Camadas até a classe de serviços) ser utilizado por várias interfaces (desktop, mobile, web).
2 – Como as camadas geravam class libraries (DLL) , o cliente exigia que as mesmas ficassem em um servidor de Componetes.
3 – O uso deste servidor era primordial, uma vez que o servidor da aplicação não tinha acesso ao servidor de Banco de dados
4 – A aplicação já estava desenvolvida, portanto readaptar o sistema para utilizar tudo baseado em Web Services, não era uma opção
Solução: A melhor solução encontrada foi o uso do Remoting, porém encontramos várias barreiras, pois a nossa camada de persistência utilizava muitos generics.
Conseguimos implementar a solução de Remoting ligando a camada de serviços à camada de interface e posto a seguir um exemplo da solução:

Para isto vamos considerar as seguintes classes no lado do Servidor de Componentes:

Classe de Domínio: Usuário – dentro de uma class library
namespace Dominio
{
    public class Usuario
    {
        private Double dblCodigo;
        public Double codigo
        {
            get { return dblCodigo; }
            set { dblCodigo = value; }
        }

        private String strNome;
        public String nome
        {
            get { return strNome; }
            set { strNome = value; }
        }

        private String strLogin;
        public String login
        {
            get { return strLogin; }
            set { strLogin = value; }
        }

        private String strSenha;
        public String senha
        {
            get { return strSenha; }
            set { strSenha = value; }
        }

    }
}

Classe de Serviço – Class Library
namespace Servico
{
    public class UsuarioListagem : MarshalByRefObject, InterfaceRemoting.IUsuarioListagem
{
  ///
        /// Retorna um datatable com uma lista de Usuários
        ///        
        public DataTable Listar()
        {
            Manager manager = new Manager();
            IList<Usuario> lstUsuario = null;
            //Chamada à persistenca para a listagem de usuários, esta retorna uma lista de usuários (Ilist
            lstUsuario = manager.Persistencia.usuario.listar();

            DataTable dtUsuario = new DataTable("USUARIO");
            dtUsuario.Columns.Add("codigo");
            dtUsuario.Columns.Add("nome");
            dtUsuario.Columns.Add("login");
            dtUsuario.Columns.Add("senha");
            
            foreach (Usuario objUsuario in lstUsuario)
            {
                DataRow linhaUsuario = dtUsuario.NewRow();

                linhaUsuario["codigo"] = objUsuario.codigo.ToString();               
                linhaUsuario["nome"] = objUsuario.nome.ToString();
                linhaUsuario["login"] = objUsuario.login.ToString();
                linhaUsuario["senha"] = objUsuario.senha.ToString();
               
                dtUsuario.Rows.Add(linhaUsuario);
            }
            return dtUsuario;
        }
}
}

Servidor Remoting – Console Application (podendo ser um Windows service)

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization.Formatters.Binary;
using Servico;
using System.Globalization;
using System.Threading;

namespace Server
{
    class Class1
    {
        //[STAThread]
        static void Main(string[] args)
        {
            try
            {
                RemotingConfiguration.Configure("Server.config");
                RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off;
                Console.WriteLine("Servidor de Remoting Inciado");

        //Esta instrução deve ser repetida para cada classe de Serviço
RemotingConfiguration.RegisterWellKnownServiceType(typeof(UsuarioListagem), typeof(UsuarioListagem).ToString(), WellKnownObjectMode.SingleCall);

                foreach (WellKnownServiceTypeEntry teste in RemotingConfiguration.GetRegisteredWellKnownServiceTypes())
                {

                    Console.WriteLine("Servidor inciado: nome - tipo: " + teste.ObjectUri.ToString() + " - " + teste.TypeName.ToString());
                }

                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

 Arquivo de Configuração do Servidor de Remoting ("Server.config")
xml version="1.0" encoding="utf-8" ?>
<configuration>
      <system.runtime.remoting>
            <application>
                  <channels>
                        <channel ref="tcp" port="80">
                             <clientProviders>
                                   <formatter ref="binary"/>
                             clientProviders>
                        channel> />
                  channels>
            application>
      system.runtime.remoting>
      <system.web>
            <globalization culture="pt-BR" uiCulture="pt-BR"/>
      system.web>
configuration>

Agora para o lado da interface, deve ser criada uma interface para o serviço:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace InterfaceRemoting
{
    public interface IUsuarioListagem
    {
        DataTable Listar();
    }
}

Dentro da interface, para “carregar” a interface com o objeto do domínio utilizamos da seguinte forma:
using InterfaceRemoting;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Tcp;
IUsuarioListagem objIUsuarioListagem = (IUsuarioListagem)Activator.GetObject(typeof(IUsuarioListagem), ConfigurationManager.AppSettings["ServicoUsuarioListagem"].ToString());

Assim conseguimos consumir o objIUsuarioListagem.Listar()

OBS: Somente conseguimos utilizar o remoting para métodos do serviço, não conseguimos implemtar para propriedades, pois estas não são automaticamente modificadas na interface quando modificadas dentro do serviço

3 comentários:

  1. Reiterando, para publicar essa solução criamos um programinha batch que ficava "escutando" no servidor a chamada da aplicação. Porém a forma solicitada pelo Cliente e com certeza mais elegante foi a criação de um Windows Service.
    Realmente Remoting é uma solução muito interessante.

    ResponderExcluir
  2. Olá Danilo,

    Boa solução. Prática, rápida e exigindo pouquíssima alteração.
    No caso de utilizar SOAP, seria o mesmo trabalho, não? Apenas alterar o cliente para referenciar a interface de serviço ao invés da classe.

    ResponderExcluir
  3. O problema maior da utilização de SOAP neste caso... seria ter que criar as interfaces, e não o trabalho para consumi-las

    ResponderExcluir