O objetivo principal desse artigo é aprimorar técnicas existentes de programação para que, no futuro, tenhamos uma eficiente e correta comunicação entre a DLL criada e um projeto de ambiente web

Para que seja desenvolvida a DLL é preciso, inicialmente, supor que ela assuma alguma funcionalidade e, nesse caso, ela será responsável por controlar as transações do aplicativo web para um banco de dados que, por padrão, pode ser o Microsoft SQL Server, desde que tenha instalado seu gerenciador, o chamado SQL Server Management Studio.

A proposta é criar um novo banco de dados com o nome de ServiceDesk (nome próprio que identificará facilmente a nova base de dados) e em sequência a tabela Funcionario, de acordo com o modelo de diagrama de classe apresentado na Figura 1.

Diagrama de Classe inicial para a criação da tabela Funcionario para o banco de dados
Figura 1. Diagrama de Classe inicial para a criação da tabela Funcionario para o banco de dados

Criando Tabela no SQL Server

Para criar a tabela Funcionario no SQL Server Management Studio, caso a versão instalada esteja em inglês, é só clicar em New Query (nova consulta) e depois colocar o código descrito na Listagem 1. Finalmente, clique em Execute (localizado no canto superior) ou pressione a tecla F5 - que é a tecla de atalho para execução de comandos SQL.


CREATE TABLE [dbo].[Funcionario](
      [fun_codigo] [int] IDENTITY(1,1) NOT NULL,
      [fun_nome] [varchar](70) NOT NULL,
      [fun_dtcontratacao] [datetime] NOT NULL,
      [fun_dtdemissao] [datetime] NOT NULL,
      [fun_ativo] [char](1) NOT NULL,
      [fun_senha] [varchar](15) NOT NULL,
      [fun_tipo] [char](1) NOT NULL,
 CONSTRAINT [PK_Funcionario] PRIMARY KEY CLUSTERED 
(
      [fun_codigo] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Listagem 1. Comandos para a criação da tabela e da chave primária
Nota: Comandos descritos na Listagem 1 foram executados no SQL Management Studio 2005.

Após isso, é necessário que sejam inseridas algumas informações no interior do banco de dados, pois posteriormente a aplicação web pode ser responsável por trazer para o usuário ou enviar essas informações para a tabela e, com nenhuma informação, a aplicação retornará nula ou com erro.

Criando um Novo Projeto

Agora, depois de ter o Microsoft Visual Studio instalado, é preciso criar um novo projeto para a criação da DLL. Neste exemplo será utilizado a versão 10 deste ambiente de desenvolvimento. No menu superior (se a versão estiver em inglês) vá em FileNewProject (ArquivoNovoProjeto). Após isso, aparecerá uma janela para escolha da forma de um novo projeto: selecione a aba Windows para abrir o menu Windows Forms Control Library. Dê um Name (nome), Location (localização) e um Solution name (nome para solução), deixando selecionado a opção “Create directory for solution” (dizendo que é para criar um diretório para solução) e clique em OK, como mostra a Figura 2.

Janela de configurações para a criação de um novo projeto Windows Forms Control Library (Visual C#)
Figura 2. Janela de configurações para a criação de um novo projeto Windows Forms Control Library (Visual C#)

Depois, aparecerá um arquivo chamado UserControl1.cs que é padrão na hora da criação de um novo projeto Windows Forms Control Library. Esse arquivo criado e presente no Solution Explorer pode ser excluído do projeto.

Feito isso, é preciso criar três pastas (Folders): uma com o nome de Controller (que será a responsável pelo Controlador), a segunda com o nome de DAO (que será a responsável pelo acesso, envio e manipulação de dados) e a terceira com o nome de Model (que será a responsável pelo Modelo), como mostra a Figura 3.

Hierarquia do projeto no Solution Explorer, com as pastas criadas
Figura 3. Hierarquia do projeto no Solution Explorer, com as pastas criadas

Criação se Classes

A proposta agora é criar as classes dentro de cada pasta, na qual cada uma é responsável por fazer sua funcionalidade.

Na pasta DAO serão criadas as classes: Banco.cs e FuncionarioBD.cs , onde a classe FuncionarioBD.cs será responsável pela manipulação de dados e comandos de SQL (insert, update, delete), e a classe Banco.cs será responsável por conectar com o banco de dados.

Para a classe Banco.cs podemos implementar os códigos referentes a Listagem 2.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Data;
 
namespace ServiceDesk.DAO
{
    internal class Banco
    {
        private string _strConexao;
        private SqlCommand _comandoSQL;
        protected SqlCommand ComandoSQL
        {
            get { return _comandoSQL; }
            set { _comandoSQL = value; }
        }
        private SqlConnection _conn;
        private SqlTransaction _transacao;
        protected Banco()
        {
            _strConexao = @"Data Source=localhost\sqlexpress;Initial Catalog=ServiceDesk;
            Persist Security Info=True;User ID=sa;Password=a12345z";
            _conn = new SqlConnection(_strConexao);
            _comandoSQL = new SqlCommand();
            _comandoSQL.Connection = _conn;
        }
        protected Banco(string stringConexao)
        {
            _strConexao = stringConexao;
            _conn = new SqlConnection(_strConexao);
            _comandoSQL = new SqlCommand();
            _comandoSQL.Connection = _conn;
        }
        protected bool AbreConexao(bool transacao)
        {
            try
            {
                _conn.Open();
                if (transacao)
                {
                    _transacao = _conn.BeginTransaction();
                    _comandoSQL.Transaction = _transacao;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        protected bool FechaConexao()
        {
            try
            {
                if (_conn.State == ConnectionState.Open)
                    _conn.Close();
                return true;
            }
            catch
            {
                return false;
            }
        }
        protected void FinalizaTransacao(bool commit)
        {
            if (commit)
                _transacao.Commit();
            else
                _transacao.Rollback();
            FechaConexao();
        }
        ~Banco()
        {
            FechaConexao();
        }
        protected int ExecutaComando(bool transacao)
        {
            if (_comandoSQL.CommandText.Trim() == string.Empty)
                throw new Exception("Não há instrução SQL a ser executada.");
 
            int retorno;
            AbreConexao(transacao);
            try
            {
                retorno = _comandoSQL.ExecuteNonQuery();
            }
            catch(Exception ex)
            {
                retorno = -1;
                throw new Exception("Erro ao executar o comando SQL:", ex);
            }
            finally
            {
                if (!transacao)
                    FechaConexao();
            }
            return retorno;
        }
        protected int ExecutaComando(bool transacao, out int ultimoCodigo)
        {
            if (_comandoSQL.CommandText.Trim() == string.Empty)
                throw new Exception("Não há instrução SQL a ser executada.");
 
            int retorno;
            ultimoCodigo = 0;
            AbreConexao(transacao);
            try
            {
                ultimoCodigo = Convert.ToInt32(_comandoSQL.ExecuteScalar());
                retorno = 1;
            }
            catch(Exception ex)
            {
                retorno = -1;
                throw new Exception("Erro ao executar o comando SQL: ", ex);
            }
            finally
            {
                if (!transacao)
                    FechaConexao();
            }
            return retorno;
        }
        protected DataTable ExecutaSelect()
        {
            if (_comandoSQL.CommandText.Trim() == string.Empty)
                throw new Exception("Não há instrução SQL a ser executada.");
 
            AbreConexao(false);
            DataTable dt = new DataTable();
            try
            {
                dt.Load(_comandoSQL.ExecuteReader());
            }
            catch(Exception ex)
            {
                dt = null;
                throw new Exception("Erro ao executar o comando SQL: ", ex);
            }
            finally
            {
                FechaConexao();
            }
            return dt;
        }
        protected double ExecutaScalar()
        {
            if (_comandoSQL.CommandText.Trim() == string.Empty)
                throw new Exception("Não há instrução SQL a ser executada.");
 
            AbreConexao(false);
            double retorno;
            try
            {
                retorno = Convert.ToDouble(_comandoSQL.ExecuteScalar());
            }
            catch(Exception ex)
            {
                retorno = -1;
                throw new Exception("Erro ao executar o comando SQL: ", ex);
            }
            finally
            {
                FechaConexao();
            }
            return retorno;
        }
    }
}
Listagem 2. Classe responsável pela conexão com o banco de dados e execução de comandos de SQL, enviar, retornar e manipular conexão com tratamentos de erros

Vamos entender a lógica por traz da Listagem 2:

  • O campo _strConexao é responsável pela definição da string de conexão;
  • O campo _comandoSQL é responsável pelo comando de SQL a ser executado;
  • A propriedade ComandoSQL é que expõe o campo para definição do comando de SQL a ser executado;
  • O campo _conn define o objeto de conexão;
  • O campo _transacao define o objeto de transação;
  • O construtor Banco() define uma string de conexão fixa e cria os objetos de conexão e para cada configuração do banco de dados há um ID e Password (senha);
  • O construtor Banco(string stringConexao) recebe por parâmetro a string de conexão a ser utilizada e cria os objetos de comando e conexão;
  • O método AbreConexao(bool transacao) abre a conexão com o banco de dados;
  • O método FechaConexao() fecha a conexão com o banco de dados;
  • O método FinalizaTransacao(bool commit) finaliza uma transação;
  • O destrutor ~Banco() fecha a conexão com o banco de dados;
  • O método ExecutaComando(bool transacao) é responsável pela execução dos comandos de Insert, Update e Delete e Retorna um número inteiro que indica a quantidade de linhas afetadas;
  • O método ExecutaComando(bool transacao, out int ultimoCodigo) é responsável pela execução dos comandos de Insert com retorno do último código cadastrado e Retorna um número inteiro que indica a quantidade de linhas afetadas;
  • O método ExecutaSelect() é responsável pela execução dos comandos de Select e Retorna um DataTable com o resultado da operação;
  • O método ExecutaScalar() é que executa comandos de Select para retornos escalares, ou seja, retorna a primeira linha e primeira coluna do resultado do comando de Select. Para esse exemplo, esse valor é convertido para Double. Ao final, retorna a primeira linha e primeira coluna do resultado comando de Select;

Para a classe FuncionarioBD.cs será implementado os códigos referentes a Listagem 3.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServiceDesk.Model;
using System.Data;
 
namespace ServiceDesk.DAO
{
    internal class FuncionarioBD : Banco
    {
        internal FuncionarioBD() { }
 
        internal Funcionario Autenticar(int codigo, string senha)
        {
            ComandoSQL.Parameters.Clear();
            ComandoSQL.CommandText = @"select * from FUNCIONARIO
                    where fun_codigo = @codigo and fun_senha = @senha";
            ComandoSQL.Parameters.AddWithValue("@codigo", codigo);
            ComandoSQL.Parameters.AddWithValue("@senha", senha);
            DataTable dt = ExecutaSelect();
            if (dt != null && dt.Rows.Count > 0)
            {
                Funcionario f = new Funcionario();
                f.Codigo = Convert.ToInt32(dt.Rows[0]["fun_codigo"]);
                f.Nome = dt.Rows[0]["fun_nome"].ToString();
                f.DataContratacao = Convert.ToDateTime(dt.Rows[0]["fun_dtcontratacao"]);
                if (dt.Rows[0]["fun_dtdemissao"] == DBNull.Value)
                    f.DataDemissao = null;
                else
                    f.DataDemissao = Convert.ToDateTime(dt.Rows[0]["fun_dtdemissao"]);
                f.Ativo = Convert.ToChar(dt.Rows[0]["fun_ativo"]);
                f.Senha = dt.Rows[0]["fun_senha"].ToString();
                f.Tipo = Convert.ToChar(dt.Rows[0]["fun_tipo"]);
 
                return f;
            }
            else
                return null;
        }
 
        internal int Gravar(Funcionario func)
        {
            ComandoSQL.Parameters.Clear();
            if (func.Codigo == 0) //Novo registro
            ComandoSQL.CommandText = @"insert into Funcionario 
                    (fun_nome, fun_dtcontratacao, fun_ativo, fun_senha, fun_tipo)
                    values (@nome, @datacontratacao, @ativo, @senha, @tipo)";
            else //Alteração
            {
                ComandoSQL.CommandText = @"update FUNCIONARIO
                    set fun_nome = @nome, fun_dtcontratacao = @datacontratacao, fun_dtdemissao = 
                    @datademissao, 
                        fun_ativo = @ativo, fun_senha = @senha, fun_tipo = @tipo
                        where fun_codigo = @codigo";
                ComandoSQL.Parameters.AddWithValue("@codigo", func.Codigo);
                if (func.DataDemissao == null)
                    ComandoSQL.Parameters.AddWithValue("@datademissao", DBNull.Value);
                else
                    ComandoSQL.Parameters.AddWithValue("@datademissao", func.DataDemissao);
            }
            ComandoSQL.Parameters.AddWithValue("@nome", func.Nome);
            ComandoSQL.Parameters.AddWithValue("@datacontratacao", func.DataContratacao);
            ComandoSQL.Parameters.AddWithValue("@ativo", func.Ativo);
            ComandoSQL.Parameters.AddWithValue("@senha", func.Senha);
            ComandoSQL.Parameters.AddWithValue("@tipo", func.Tipo);
 
            return ExecutaComando(false);
        }
 
        internal List<Funcionario> Obter(string palavraChave, char ativo)
        {
            ComandoSQL.Parameters.Clear();
            ComandoSQL.CommandText = @"select fun_codigo, fun_nome, fun_dtcontratacao, 
            fun_dtdemissao, fun_ativo, fun_senha, fun_ativo, fun_tipo
                        from Funcionario
                        where fun_ativo = @ativo and fun_nome like @palavra order by fun_nome";
            ComandoSQL.Parameters.AddWithValue("@ativo", ativo);
            ComandoSQL.Parameters.AddWithValue("@palavra", "%"+palavraChave+"%");
 
            DataTable dt = ExecutaSelect();
            List<Funcionario> dados = null;
            if (dt != null && dt.Rows.Count > 0)
            {
                dados = new List<Funcionario>();
                foreach(DataRow linha in dt.Rows)
                {
                    Funcionario f = new Funcionario();
                    f.Codigo = Convert.ToInt32(linha["fun_codigo"]);
                    f.Nome = linha["fun_nome"].ToString();
                    f.DataContratacao = Convert.ToDateTime(linha["fun_dtcontratacao"]);
                    if (linha["fun_dtdemissao"] == DBNull.Value)
                        f.DataDemissao = null;
                    else
                        f.DataDemissao = Convert.ToDateTime(linha["fun_dtdemissao"]);
                    f.Ativo = Convert.ToChar(linha["fun_ativo"]);
                    f.Senha = linha["fun_senha"].ToString();
                    f.Tipo = Convert.ToChar(linha["fun_tipo"]);
 
                    dados.Add(f);
                }
            }
            return dados;
        }
 
        internal int Demitir(int codigoFuncionario)
        {
            ComandoSQL.Parameters.Clear();
            ComandoSQL.CommandText = @"update Funcionario set fun_ativo = 'N',
                    fun_demissao = @data where fun_codigo = @codigo";
            ComandoSQL.Parameters.AddWithValue("@data", DateTime.Now);
            ComandoSQL.Parameters.AddWithValue("@codigo", codigoFuncionario);
 
            return ExecutaComando(false);
        }
 
        internal Funcionario Obter(int codigoFuncionario)
        {
            ComandoSQL.Parameters.Clear();
            ComandoSQL.CommandText = @"select fun_codigo, fun_nome, fun_dtcontratacao, 
            fun_dtdemissao, fun_ativo, fun_senha, fun_ativo, fun_tipo
                        from Funcionario
                        where fun_codigo = @codigo";
            ComandoSQL.Parameters.AddWithValue("@codigo", codigoFuncionario);
 
            DataTable dt = ExecutaSelect();
            if (dt != null && dt.Rows.Count > 0)
            {
                Funcionario f = new Funcionario();
                f.Codigo = Convert.ToInt32(dt.Rows[0]["fun_codigo"]);
                f.Nome = dt.Rows[0]["fun_nome"].ToString();
                f.DataContratacao = Convert.ToDateTime(dt.Rows[0]["fun_dtcontratacao"]);
                if (dt.Rows[0]["fun_dtdemissao"] == DBNull.Value)
                    f.DataDemissao = null;
                else
                    f.DataDemissao = Convert.ToDateTime(dt.Rows[0]["fun_dtdemissao"]);
                f.Ativo = Convert.ToChar(dt.Rows[0]["fun_ativo"]);
                f.Senha = dt.Rows[0]["fun_senha"].ToString();
                f.Tipo = Convert.ToChar(dt.Rows[0]["fun_tipo"]);
 
                return f;
            }
            else
                return null;
        }
    }
}
Listagem 3. Classe FunionarioBD que contém métodos para autenticação de funcionário, gravação, listagem, consulta (obtenção) de acordo com suas atividades

Agora, na pasta Controller, será criada a classe: FuncionarioController.cs, responsável por controlar a sua tabela, definindo as ações e regras de acesso, como se fosse uma validação interna entre a saída e entrada de dados. A classe FuncionarioController.cs será implementada conforme o código da Listagem 4.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServiceDesk.Model;
using ServiceDesk.DAO;
 
namespace ServiceDesk.Controller
{
    public class FuncionarioController
    {
        FuncionarioBD bd = new FuncionarioBD();
 
        public Funcionario Autenticar(int codigo, string senha)
        {
            if (codigo > 0 && senha.Trim().Length > 0)
                return new FuncionarioBD().Autenticar(codigo, senha);
            else
                return null;
        }
        public FuncionarioController() { }
        public int Gravar(Funcionario func)
        {
            if (func.Nome.Trim().Length >= 3 &&
                func.Ativo == 'S')
            {
                return bd.Gravar(func);
            }
            else
                return -1;
        }
        public List<Funcionario> Obter(char ativo = 'S')
        {
            if (ativo == 'S' || ativo == 'N')
                return bd.Obter("",ativo);
            else
                return null;
        }
 
        public Funcionario Obter(int codigoFuncionario)
        {
            return codigoFuncionario > 0 ? bd.Obter(codigoFuncionario) : null;
        }
 
        public int Demitir(int codigoFuncionario)
        {
            return bd.Demitir(codigoFuncionario);
        }
    }
}
Listagem 4. Classe controladora responsável por fazer validações, operações e métodos como na autenticação, pesquisa, gravação e alteração antes que os dados sejam manipulados

Já na pasta Model será criada a classe Funcionario.cs, onde modelará as regras de negócio como se fosse um intermediário entre o lado Cliente e o lado Servidor e cada propriedade é responsável por receber e/ou enviar as informações. A classe Funcionario.cs será implementada de acordo com a Listagem 5.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ServiceDesk.Model
{
    public class Funcionario
    {
        public Funcionario() { _codigo = 0; }
 
        #region Propriedades do Funcionario
        private int _codigo;
        public int Codigo
        {
            get
            {
                return _codigo;
            }
            set
            {
                _codigo = value;
            }
        }
        private string _nome;
 
        public string Nome
        {
            get 
            {
                return _nome; 
            }
            set { _nome = value; }
        }
        private DateTime _dataContratacao;
 
        public DateTime DataContratacao
        {
            get { return _dataContratacao; }
            set { _dataContratacao = value; }
        }
        private DateTime? _dataDemissao;
 
        public DateTime? DataDemissao
        {
            get { return _dataDemissao; }
            set { _dataDemissao = value; }
        }
        private char _ativo;
 
        public char Ativo
        {
            get { return _ativo; }
            set { _ativo = value; }
        }
        private string _senha;
 
        public string Senha
        {
            get { return _senha; }
            set { _senha = value; }
        }
        private char _tipo;
 
        public char Tipo
        {
            get { return _tipo; }
            set { _tipo = value; }
        }
        #endregion
    }
}
Listagem 5. Classe responsável por obter ou enviar informações do Funcionário via Get e Set

Por fim, após ter criado todas as classes relativas a tabela Funcionário, o layout dos arquivos ficará como mostra a Figura 4.

Hierarquia do projeto, com as pastas e suas respectivas classes
Figura 4. Hierarquia do projeto, com as pastas e suas respectivas classes

Criando a DLL ServiceDesk

Finalmente, para que o projeto se transforme em uma DLL, basta dar um Build e salvar. Com isso, ela pode ser utilizada em qualquer projeto web ASP .NET.

Para anexar essa DLL criada a um projeto web ASP .NET, basta clicar com o botão direito em References do novo projeto e clicar em Add Reference..., como ilustra a Figura 5.

Caminho para referenciar a DLL criada para um novo projeto web
Figura 5. Caminho para referenciar a DLL criada para um novo projeto web

Feito isso, aparecerá uma janela, conforme mostra na Figura 6.

Janela de opções e confirmação de anexo de Referencias (que no caso é uma referencia a DLL)
Figura 6. Janela de opções e confirmação de anexo de Referencias (que no caso é uma referencia a DLL)

Após ter selecionado o Project Name, basta dar OK e a DLL criada estará na futura aplicação web ASP . NET.

Nota: Sempre que houver quaisquer alterações na DLL durante a implementação do projeto web ASP .NET, é obrigatório dar um Build na DLL para que a mesma não apresente erros.