Aplicação Windows Service no Visual Studio 2013 Community com Debug!

VSLogo

Criar Aplicações do tipo serviço no windows não é difícil, no Visual Studio mesmo ja possui um template pronto. Mas existem dois detalhes:

1. O Templante exige algumas manipulação para ficar funcional

2. O Debug de um serviço pode-se dizer que é um verdadeiro inferno.

O grande problema é que o serviço a principio não pode ser executado diretamente do Visual Studio. Para depurar um projeto é preciso compilar o projeto instalar ele no Services do windows iniciar ele é depois anexar o seu processo no Visual Studio para então Debugar! Muita dor.

A Minha principal motivação para fazer este tutorial é que não achei em nenhum lugar uma explicação simples e direta. este post é uma compilação de tudo que pesquisei até conseguir instalar o serviço na aba de serviços e depurar ele pelo Visual Studio.

A versão do Visual Studio que eu utilizo é a versão gratuita distribuída pela microsoft no momento é a Visual Studio Community 2013

A linguagem de programação que vou utilizar será C# mas o procedimento é o mesmo para VB.NET, mudando apenas a sintaxe.

1. Criando a Aplicação tipo Serviço

Abra o Visual Studio e vá em FILE > New Project.

Service-01

  1. Em templates escolha Visual C# > Windows Desktop;
  2. Localize o template Windows Service;
  3. Insira um Nome ao Projeto
  4. Confime

2. Criando o Proccess Installer

O projeto esta criado, se o compilar e tentar usar o InstallUtil.exe (mais a frente) para inserir o executável criado na lista de serviços ele não irá funcionar, pois o serviço não possui um Installer.

Deve haver um motivo para isto não vir padrão com projeto. Eu não sei por que mas depois de pesquisar um pouco descobri que deve-se fazer o seguinte.

Service-02

  1. Na área de Design do seviço clique com o botão direito do mouse.
  2. Clique em Add Installer.

Feito isso verá que foram criados mais dois componentes. no Projeto um serviceProcessInstaller1 e o serviceInstaller1

Service-03

Agora ao tentarmos instalar o serviço compilado ele irá avançar mais alguns passos. Mas provavelmente enquanto estiver sendo instalado ira surgir uma tela pedindo credenciais para a instalação.  Para contornar isso vamos adcionar uma linha de código no Project Installer.Designer.cs

Localize o método InitializeComponent() Ele deve estar como a imagem abaixo.

Service-04

Acima daquelas duas linhas insira o a linha de comando mostrada abaixo. Assim o serviço irá parar de pedir a autenticação.

Service-05

Logo abaixo vemos o item ServiceName é ele que contém o nome do serviço que aparece na Aba dos Serviços do Windows.

Service-06

3. Habilitando o Debug pelo Visual Studio

Ok, foi criado o serviço ele compila sem erros e você ja consegue instalar ele sem problemas na aba de serviços do windows.

Mas agora precismos debugar o código verificar em tempo de execução como fazer isso. Inserimos os breakpoints pressionamos F5 e…

Service-05a

Nada acontece o Visual Studio não consegue executar o serviço. Por padrão esta não é uma tarefa simples, na própria página do Visual Studio se encontra um tutorial de como se utilizar o debug.  Existem diversas forma de se fazer o debug pelo Visual Studio, indo ao Stackoverflow encontra-se longas discussões sobre isso. A que achei mais simples e que me ajudava no que precisava fazer foi esta.

Primeiro Passo é tornar o Método OnStart do serviço executável. ele é um método do tipo protected para não alterar o seu nível de acesso vamos criar um novo método publico chamado Start(). Que a unica função é chamar o método OnStart. Eu coloquei envio null para ele porquê meu serviço não tem nenhum argumento de inicialização, caso o seu serviço tenha basta colocar no método a chamada para a variável.

Service-07

Agora vamos editar o  Program.cs .  Aqui iremos fazer algumas coisas.

Service-09

  • Adcionar o namespace System.Threading;
  • No metódo Main() vamos Inserir as diretivas #if DEBUG / #else / #endif;
  • Entre #else e #endif manter o código original de inicialização do Serviço
  • Abaixo de #if DEBUG inserimos o código destacado.

No código nós fazemos uma instancia da classe serviço e na sequencia executamos o método Start(). Logo em sguida colocamos a thread do mais para “dormir” com um comando Sleep com o tempo e infinito. Isto é feito para manter o verviço rodando depois de inicializado.

Com este procedimento sempre que usando o Visual Studio e em DEBUG o Visual Studio Compilará a primeira parte em que ele não inicia a aplicação como um serviço mas como uma aplicação comum.

Quando o serviço estiver finalizado e pronto para ser distribuido. é precisso compilar ele como um RELEASE para isso faça o seguinte. No Projeto selecione properties.

Service-10

Em Configuration Properties > Configuration verá que quando selecionado como DEBUG o código ativo dentro do #if é a primeira parte. E é esta que sera compilada e executa a segunda é ignorada pelo compilador.

Service-11

Alterando para RELEASE o código que se torna ativo agora é o de baixo e a aplicação irá trabalhar como um serviço convencional.

Service-12

Sendo assim tenha sempe atenção com o configuração de compilação quando estiver desenvolvendo uma aplicação tipo serviço.

Servidor MySQL com Linux Ubuntu

Ubuntu-Logo mysqllogo

Montar um servidor MySQL Linux é extremamente simples porém como tudo tem alguns pequenos ajustes que são necessários de depois de tudo montado.

Durante o desenvolvimento do projeto em que precisa me conectar em um servidor MySQL eu achei melhor criar uma Maquina Virtual ao invéz de simplesmente instalar o MySQL em meu computodaro por alguns motivos simples.

  • A máquina virtual vai simular um segundo computador pois com sua placa de rede ligada em modo bridge eu acessarei ela como se fosse um outro computador.
  • Realizar a conexão via localhost é fácil e nunca será a realidade então conectado a um segundo computador terei como prever com antecedência algum detalhe que precisa se ajustado para que a conexão fucnione.
  • Me familizar com o uso de linux pois sou muito dependente do windows por sempre ter apenas trabalhado nele desssa forma me acostumo mais com o Sistema operacional.

Requisitos para o turtorial

Para realizar o tutorial o que é necessário:

  1. Computador com windows e uma boa quantidade de RAM ( pelo menos 4GB )
  2. Virtual Box instalado em seu computador
    1. Saber como Usar o Virtual box. não irei mostrar como se faz issi
  3. Uma ISO do Ubuntu. Eu utilizei a versão 12.04 LTS Desktop para teste e por ter interface gráfica. mas para a implementação definitiva o ideal é utilizar a versão server que é mais enxuta e consome menos recursos da máquina.
    1. Ubuntu instalado dentro do Virtual Box e rodando.
    2. Placa de Rede da Maquina Virtual em modo Bridge e configurada em uma rede que o Host windows “enxergue”.
    3. Conectado na Internet.
  4. Um cliente windows para Conexão ao Banco de dados MySQL. Eu utilizei o HeidiSQ

1. Instalando o MySQL

Para Instalar o MySQL execute os seguintes comandos no terminal do linux.


$ sudo su

# apt-get install mysql

# apt-get mysql_install_db

Siga a instruções como são passadas pelos wizzards depois disso iniciamos os ajustes.

2. Acessar o Servidor Externamente

Por padrão ao ser instalado o MySQL não permite que você acesse o banco de dados externamente então a primeira coisa a fazer é editar o arquivo my.cnf

para isso no terminal execute


# gedit /etc/mysql/my.cnf

la encontre a a seção [mysqld] nela deve conter um atributo chamado  bind-address. Por padrão ele possui o seguinte valor


bind-address = 127.0.0.1

este é o valor para o local host. Vamos alterar para o IP configurado no servidor. no meu caso será 192.168.1.200


bind-address = 192.168.1.200

com isto feito já permitimos que o banco de dados seja acessado através de outros computadores.

2 .Criar conta usuário

Agora vamos acessar o MySQL e criar a conta de usuário que fará o acesso externo.

Primeiro entramos no MySQL


# mysq - u root -p

dentro do MySQL iremos fazer 3 coisas

  1. Criar o usuário que faz o acesso externo
  2. Dar a este usuário os privilégios para acessar o servidor externamente
  3. Resetar o password do usuário.
  4. Aplicar as configurações

2.1 Nova conta

Vamos criar um usuário chamado “christian” com a senha “christian” executamos o seguinte comando dentro do MySQL.

O usuário root é para ser acessado apenas localmente. então deixe a preguiça de lado e excute essa meia dúzia de comandos.


mysql> create user 'christian'@'localhost' identified by 'christian';

2.2 Dar os privilégios ao usuário

Próximo dar permissão para que o usuário se conecte ao banco de dados remotamente.


mysql> grant all privileges on *.* to 'christian'@'%';

2.3 Resetar o Password

As vezes é pode ser necessário resetar o password se não estiver conseguindo conectar para isso execute.


mysql> update mysql.user ser Password=PASSWORD('christian') where user = 'christian';

2.4 Aplicar as configurações

Assim que estiver tudo feito execute um flush.


mysql> flush privileges;

3. Reiniciando o Serviço MySQL

Feitas as alterções para elas começarem a funcionar devemos reiniciar o serviço isto pode ser feito de duas formas:


# service mysql restart

ou


# service mysql stop



# service mysql start

para verificar o status do MySQl basta utilizar o óbvio depois do que vimos que é o comando


# service mysql status

4. Testando a Conexão

A principio nosso servidor já esta funcionando agora vamos verificar se conseguimos conectar nele remotamente.

Abrimos o HeidiSQL e configuramos com os dados para configuração.

heidiSQL 01

Ao clicar em Abrir se estiver tudo correto o Heidi irá listar as estruturas dentro do banco de dados.

heidiSQL 02

Mas se ele não conseguir o que acontecerá é que ele irá listar apresentar um erro. e então revise tudo que foi feito para verificar se não pulou algum ponto.

C# + NHibernate + MySQL – Parte 1

O objetivo do post é registrar como preparar um projeto em C# para utilziar um conexão com um Banco de Dados com o Nhibernate.

Porque usar NHibernate ? Por que é Sexy! Descolado! Mas principalmente para facilitar a vida no futuro. Depois de feita a configuração o acesso ao banco de dados é como se estivesse acessando um objeto não é necessário fazer querys o NHibernate faz elas para você!

Items Utilizados

  • MySQL Connector 6.6.5
  • Visual Studio 2013 Community
  • NHibernate
  • Fluent NHibernate
  • Banco de Dados MySQL ( No meu caso uma máquina Virtual Linux com ubuntu 12.04 )

Antes de tudo as duas primeiras coisas a serem feitas é ter o Visual Studio e o MySQL Connector Instalados no Computador

1. Criar uma solução vazia ( Blank Solution ) no Visual Studio

NHibernate 01

2. Criar o Projeto da Biblioteca de Conexão ( Class Libray )

Criar Nova class Libray
Criar Nova class Libray

Delete a Class1.cs criada pelo Visual Studio, mais adiante serão criadas as classes

Delete Class.cs

3.  Instalar os Pacotes Nugget  do NHibernate e Fluent NHibernate

Procure pelo pacote Nhibernate e clique em install.

NHibernate 05

Normalmente ja aparece na sequência da pesquisa o segundo pacote necessário que é o Fluent NHibernate. Instalar também.

NHibernate 06

Tudo dando certo os dois pacotes terão o simbolo de Check no canto superior Direito

NHibernate 07

E também estarão listados nas referencias do Projeto.

NHibernate 08

4. Criar as pastas para os mapeamentos e entitdades

NHibernate 09

NHibernate 10

A Partir deste Ponto o Projeto se altera de acordo com a estrutura de tabelas que ele ira se Conectar.

5. Criar as Entidades das Tabelas.

Para o tutorial criei uma tabela com apenas 3 campos para que não fique algo longo sem necessidade. Minha Tabela se Chama Pessoa e possui 3 campos

  • id
  • nome
  • idade

Primeiro cria-se dentro da pasta Entities a Classe que se referencia a Tabela.

NHibernate 11

NHibernate 12

Criado o arquivo CS o passo seguinte é criar uma classe exatamente com o Mesmo nome da Tabela no Banco de dados e criar Propiedades para cada uma das colunas

Estas propiedades devem ser do tipo Virtual.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL.Entities
{
    /// <summary>
    /// Tabela Pessoa
    /// </summary>
    public class Pessoa
    {
        public virtual int id { get; set; }
        public virtual string nome { get; set; }
        public virtual int idade { get; set; }
    }
}

6. Fazer o mapeamento

Crie um Classe dentro da Pasta Mapping

NHibernate 13

O mapeamento ficará como mostrado a abaixo.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FluentNHibernate.Mapping;
using NHMySQL.Entities;

namespace NHMySQL.Mapping
{
    /// <summary>
    /// Mapeamento da Tabela Pessoa
    /// </summary>
    public class PessoaMap :ClassMap<Pessoa>
    {
        public PessoaMap()
        {
            Id( p => p.id );
            Map( p => p.nome );
            Map( p => p.idade );
            Table( "Pessoa" );
        }
    }
}

Note que foram adicionado o Namespace  FluentNHibernate.Mapping ele é que permite que nossa classe utilize a herde a propriedade de ClassMap< > e tb é adicionado o Namespace NHMySQL.Entities para que possamos acessar a classe de entidades de nossa tabela. Para chave primária iremos utilizar a função Id( ) através de uma função lambda fazemos o link com o a propiedade equiavalente na tabela pessoa. Os demais utilizamos a Função Map( ) para mapear os demais campos. E por fim passamos o nome da tabela que iremos conectar através do método Table( ).

7. Criar a Inteface

NHibernate 14

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL
{
    public interface IConnectionDAO<T>
    {
        void Insert(T entidade);
        void Update(T entidade);
        void Delete(T entidade);
        T ReturnById(int id);
        IList<T> Query();
    }
}

8. Criar Classe Repository.cs

Criar uma Nova classe ( ctrl + shift + A ) e fazer ela herdar a interface que foi criada.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL
{
    public class Repository<T> :IConnectionDAO<T> where T :class
    {
        public void Insert(T entidade)
        {
            throw new NotImplementedException();
        }

        public void Update(T entidade)
        {
            throw new NotImplementedException();
        }

        public void Delete(T entidade)
        {
            throw new NotImplementedException();
        }

        public T ReturnById(int id)
        {
            throw new NotImplementedException();
        }

        public IList<T> Query()
        {
            throw new NotImplementedException();
        }
    }
}

9. Criar Classe SessionFactory.cs

A Classe SessionFactory será a responsável por gerenciar a conexão ao banco de dados

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL
{
    public class MySQLSessionFactory
    {
        private static string ConnectionString = "Server=192.168.1.200;Port=3306;Database=mydb;Uid=christian;Pwd=christian;";
        private static ISessionFactory session;

        public static ISessionFactory CreateSession()
        {
            if (session != null)
            {
                return session;
            }

            IPersistenceConfigurer dbConfig = MySQLConfiguration.Standard.ConnectionString( ConnectionString );
            
            
            var mapConfig = Fluently.Configure().Database( dbConfig ).Mappings( c => c.FluentMappings.AddFromAssemblyOf<Mapping.PessoaMap>() );

            session = mapConfig.BuildSessionFactory();

            return session;
        }

        public static ISession StartSession()
        {
            return CreateSession().OpenSession();
        }
    }
}

10. Implementando Metódos em Repository.cs

Criada a SessionFactory volta-se para a Repository para implementar os métodos de interação com o Banco de dados.

using NHibernate;
using NHibernate.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL
{
    public class Repository<T> :IConnectionDAO<T> where T :class
    {

        public void Insert(T entidade)
        {
            using (ISession session = MySQLSessionFactory.StartSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Save( entidade );
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        if (!transaction.WasCommitted)
                        {
                            transaction.Rollback();
                        }
                        throw new Exception( "Erro ao inserir entidade: " + ex.Message );
                    }                    
                }
            }
        }

        public void Update(T entidade)
        {
            using (ISession session = MySQLSessionFactory.StartSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Update( entidade );
                    }
                    catch (Exception ex)
                    {
                        if (!transaction.WasCommitted)
                        {
                            transaction.Rollback();
                        }
                        throw new Exception( "Erro ao atualizar  entidade: " + ex.Message );
                    }
                }
            }
        }

        public void Delete(T entidade)
        {
            using (ISession session = MySQLSessionFactory.StartSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Delete( entidade );
                    }
                    catch (Exception ex)
                    {
                        if (!transaction.WasCommitted)
                        {
                            transaction.Rollback();
                        }
                        throw new Exception( "Erro ao excluir entidade: " + ex.Message );
                    }
                }
            }
        }

        public T ReturnById(int id)
        {
            using (ISession session = MySQLSessionFactory.StartSession())
            {
                return session.Get<T>( id );
            }
        }

        public IList<T> Query()
        {
           using (ISession session = MySQLSessionFactory.StartSession())
           {
               return ( from e in session.Query<T>() select e ).ToList();
           }
        }
    }
}

11. Criando o Repositório da Tabela

Cada tabela deve ter um uma Herdando Repository.cs recebendo a Classe da tabela. Vamos ao Exemplo da classe Pessoa.

using NHMySQL.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHMySQL
{
    public class PessoaRepository : Repository<Pessoa>;
    {

    }
}

É isto mesmo vaiza sem nada ! =D

Executando um Build no projeto com o que esta implementado até agora não é para retornar nenhum erro. Caso o Output do Visual Studio falhe ao compilar, revise o que foi feito.

Parte da implementação esta pronta próximos passo é criar uma Aplicação windows simples para conectarmos com o Banco de dados.