segunda-feira, julho 30, 2018

Função sem retorno não é função :)

Esses dias eu caçava um bug em uma funcionalidade do sistema, quando me deparei com uma tentativa de validação em JavaScript onde havia uma função sem retorno para um dos cenários que a função poderia encontrar. Algo assim:

01: function a() {
02:     if(1==1) 
03:     {
04:       alert("STOP!");
05:       return false;
06:     }
07:     //Missed true return!
08: }
09: 
10: function b() {
11:     if(a())
12:     {
13:       //This will never occurs! Change 1==1 to 1==2 in "a" function to test.
14:       alert("Ok, go ahead!");
15:       return true;
16:     }
17:     else 
18:     {
19:       return false;
20:     }
21: }
22:
23: document.write(b() == true ? "All set" : "Something is wrong");

Já deu para reparar que sem um return fora da condição ou em um else na função a nada aconteceria na função b.
Isso mesmo que acontecia no sistema.

Parta do princípio que funções devem ter retorno sempre e não seja o amiguinho a deixar esse bug para eu resolver rs.

#pás
|--|)

sexta-feira, março 11, 2016

Usando yield para encurtar o código

Esses dias estava no trabalho conversando com alguns colegas sobre um artigo que citava as perguntas mais frequentes em entrevistas para analista de sistemas/desenvolvedor/programador no Google, Microsoft, IBM, Yahoo, etc...  nas gigantes de tecnologia.
E uma das questões era como fazer a troca de valores entre duas variáveis numéricas sem o uso de uma variável auxiliar.

Hoje lendo meus e-mails passei pela chamada de um novo post em um grupo que participo no LinkedIn sobre desenvolvimento na plataforma .NET, e deste post me inspirei a fazer este (nada se cria...) e mais ainda, a colocar uma questão em nossa prova de seleção para analista de sistemas no estilo “não use uma variável/coleção auxiliar”.

Pense na classe abaixo:

1:  using System;  
2:  using System.Text;  
3:  namespace com.jeanjmichel.blogspot.testyield  
4:  {  
5:    public class Person  
6:    {  
7:      private int age;  
8:      private char gender;  
9:      private String name;  
10:      public int Age {set; get;}  
11:      public char Gender  
12:      {  
13:        get { return this.gender; }  
14:      }  
15:      public String Name { set; get; }  
16:      public Person(int age, char gender, String name)  
17:      {  
18:        this.age = age;  
19:        this.gender = gender;  
20:        this.name = name;  
21:      }  
22:      override public String ToString()  
23:      {  
24:        StringBuilder sb = new StringBuilder();  
25:        sb.Append(this.name);  
26:        sb.Append(" ");  
27:        sb.Append(this.age);  
28:        sb.Append(" years old");  
29:        sb.Append(this.gender == 'M' ? " man" : " woman");  
30:        return sb.ToString();  
31:      }  
32:    }  
33:  }  

Agora vamos usar a uma classe que utiliza ela:

1:  using System;  
2:  using System.Collections.Generic;  
3:  namespace com.jeanjmichel.blogspot.testyield  
4:  {  
5:    public class Program  
6:    {  
7:      private static List<Person> persons = new List<Person>();  
8:      static void PopulatePersonsList()  
9:      {  
10:        persons.Add(new Person(34, 'M', "Michel, Jean J."));  
11:        persons.Add(new Person(0, 'F', "Michel, Helena M."));  
12:        persons.Add(new Person(60, 'M', "Doe, John"));  
13:        persons.Add(new Person(47, 'F', "Doe, Jane"));  
14:        persons.Add(new Person(41, 'M', "Smith, John"));  
15:      }  
16:      static void Main(string[] args)  
17:      {  
18:        PopulatePersonsList();  
19:        foreach (Person p in persons)  
20:          Console.WriteLine(p);  
21:        Console.ReadKey();  
22:      }  
23:    }  
24:  }  

A saída da execução da classe é:

1:  Michel, Jean J. 34 years old man  
2:  Michel, Helena M. 0 years old woman  
3:  Doe, John 60 years old man  
4:  Doe, Jane 47 years old woman  
5:  Smith, John 41 years old man  

Se quisermos criar um método que retorne uma lista com apenas as pessoas do sexo masculino ou feminino para fazermos algo, como exibir a lista? Claro que não podemos percorrer a lista original excluindo as pessoas do sexo oposto ao pesquisado, então a solução seria criar uma lista auxiliar?

Podemos encurtar esse caminho executando exatamente o desejado, filtrar a lista pelo atributo gender da classe Person, porém sem declarar uma nova lista ou alterar a lista original, dando a impressão de que estamos fazendo o desejado “sem usar uma variável auxiliar”.

Veja isso:

1:      static IEnumerable<Person> FilterPersonsListByGender(char gender)  
2:      {  
3:        foreach (Person p in persons)  
4:          if (p.Gender == gender) yield return p;  
5:      }  

O que faz a palavra reservada yield é retornar quando a expressão for true. Da para usar com break também e parar um laço, por exemplo. Mais detalhes direto na fonte (MSDN): https://msdn.microsoft.com/pt-br/library/9k7k7cf0.aspx.

Assim ficaria a classe final:

1:  using System;  
2:  using System.Collections.Generic;  
3:  namespace com.jeanjmichel.blogspot.testyield  
4:  {  
5:    public class Program  
6:    {  
7:      private static List<Person> persons = new List<Person>();  
8:      static void PopulatePersonsList()  
9:      {  
10:        persons.Add(new Person(34, 'M', "Michel, Jean J."));  
11:        persons.Add(new Person(0, 'F', "Michel, Helena M."));  
12:        persons.Add(new Person(60, 'M', "Doe, John"));  
13:        persons.Add(new Person(47, 'F', "Doe, Jane"));  
14:        persons.Add(new Person(41, 'M', "Smith, John"));  
15:      }  
16:      static IEnumerable<Person> FilterPersonsListByGender(char gender)  
17:      {  
18:        foreach (Person p in persons)  
19:          if (p.Gender == gender) yield return p;  
20:      }  
21:      static void Main(string[] args)  
22:      {  
23:        PopulatePersonsList();  
24:        foreach(Person p in FilterPersonsListByGender('F'))  
25:          Console.WriteLine(p);  
26:        Console.ReadKey();  
27:      }  
28:    }  
29:  }  

E essa é a saída agora que temos:

1:  Michel, Helena M. 0 years old woman  
2:  Doe, Jane 47 years old woman  

Em nenhum momento você verá ser criada uma nova lista :)

Achei legal, resolvi postar, agora é elaborar uma questãozinha legal para a prova e ver se a galera vai conhecer esse o yield ;)



sexta-feira, dezembro 06, 2013

Tratamento de erros no SQL Server

Desde que comecei a trabalhar também com o SQL Server, sempre senti saudades da forma como o Oracle trata exceções (muito parecido com o Java/C# onde se pode ir encadeando os tipos de exceção e tal - ok, isso pode ser feito no SQL Server, mas de forma fedorenta usando um CASE WHEN!.

Eu estou deixando aqui postado um exemplo de como tratar erros.

Ao executar esse script irá ser lançada uma exceção indicando que se está tentando inserir um registro com informações além da capacidade do campo (o campo Name foi criado de propósito para dar erro com apenas 20 caracteres de comprimento).

Quando a exceção for lançada, as inserções serão desfeitas (rollback) e será apresentada uma mensagem de erro formatada com o código e a mensagem do erro além do indicativo da linha onde ele ocorreu.

Se você alterar a declaração do campo Name para VARCHAR(100), por exemplo, e executar novamente o script a tabela será recriada, os registros todos inseridos e depois selecionados e exibidos.

--| Se a tabela Driver já existir na base, será deletada e recriada           |--
IF OBJECT_ID('dbo.DRIVER', 'U') IS NOT NULL
  DROP TABLE DRIVER
 
CREATE TABLE DRIVER (
  ID   INT         NOT NULL IDENTITY,
  NAME VARCHAR(20) NOT NULL
)
 
--| Inicia uma transação                                                      |--
BEGIN TRAN
 
--| Inicia um bloco try                                                       |--
BEGIN TRY
  
  --| Vai inserir na tabela uma lista de caras que "dirigem" muito            |--
  INSERT INTO Driver (Name) VALUES ('Jean J. Michel') --| Esse é o melhor! ;) |--
  INSERT INTO Driver (Name) VALUES ('Alain Prost')
  INSERT INTO Driver (Name) VALUES ('Alberto Ascari')
  INSERT INTO Driver (Name) VALUES ('Ayrton Senna da Silva')
  INSERT INTO Driver (Name) VALUES ('Damon Hill')
  INSERT INTO Driver (Name) VALUES ('Emerson Fittipaldi')
  INSERT INTO Driver (Name) VALUES ('Graham Hill')
  INSERT INTO Driver (Name) VALUES ('Nigel Mansell')
  INSERT INTO Driver (Name) VALUES ('Jack Brabham')
  INSERT INTO Driver (Name) VALUES ('Jackie Stewart')
  INSERT INTO Driver (Name) VALUES ('Juan Manuel Fangio')
  INSERT INTO Driver (Name) VALUES ('Nelson Piquet')
  INSERT INTO Driver (Name) VALUES ('Niki Lauda')
  
  --| Encerra a transação confirmando as inserções (inserts) feitas           |--
  COMMIT TRAN
  
  --| Seleciona todos os registros inseridos                                  |--
  SELECT Id, Name FROM Driver
  
--| Finaliza o bloco try. Se a execução chegou até aqui não passará pelo      |--
--| bloco catch, pois tudo ocorreu corretamente não lançando exceções         |--
END TRY
--| Se acontecer qualquer erro a execução passará da linha aonde o erro o-    |--
--| correu para a primeira linha deste bloco. Onde deverá ser tratado         |--
BEGIN CATCH
  --| Seleciona e exibe o que aconteceu durante a execução, qual a exceção    |--
  --| lançada e em que linha                                                  |--
  SELECT 'Erro: ' +
         CAST(ERROR_NUMBER() AS VARCHAR(MAX)) + '-'+
         ERROR_MESSAGE() + ' Na linha ' +
         CAST(ERROR_LINE() AS VARCHAR(MAX)) + '.'
  
  --| Encerra a transação desfazendo todas as inserções feitas no bloco try   |--
  ROLLBACK TRAN
  
--| Finaliza o bloco catch                                                    |--
END CATCH

Era isso, mais um postzinho básico, rápido e indolor para manter a atividade do blog ;)

sábado, novembro 09, 2013

Javascript recuperando e percorrendo elementos de uma listbox (select)

Sabe quando você está naquela fase de achar que "agora é focar no novo projeto" e esquecer todo o sofrimento que aquele velho produto/sistema lhe deu e então, de repente, surge aquela demanda que irá lhe parar para implementar algo naquele velho produto/sistema legado que você estava comemorando por nunca mais ter que olhar para ele?

Fui vítima disso recentemente. E o pior, aquele velho produto/sistema é web, e do tempo do Ronca. Ou seja, esquece se você pensou em ir lá, meter um jQueryzinho e correr para o abraço rs, você vai ter que implementar o solicitado (mesmo não fazendo muito sentido - quem manja de HTML sabe do que eu estou falando neste exemplo).

Enfim, é sempre uma chance a mais de fazer um código simples e postar por aqui ;) já que ando a algum tempo afastado das adoráveis linhas de código fazendo trabalhos de análise, documentação e prototipação.

A parada é, pegar o que foi informado no primeiro campo e pesquisar se há na listbox. Por exemplo, se eu informar 41 ou 041 no campo texto e sair do campo (pressionando TAB, por exemplo), a função deve selecionar na listbox o banco BANRISUL. Se eu informar 1 deve selecionar o Banco do Brasil, e se não encontrar o banco com o termo pesquisado deixo a lista no índice 0.

Cá está a implementação disto usando puramente JavaScript para pesquisar e selecionar um valor contido em uma listbox (ou combobox):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>Pesquisar em uma listbox</title>
    <script language="JavaScript" type="text/javascript">
      function fncPesquisarBanco(codBanco) {
        var listOptions;
        var i;
        
        listOptions = document.getElementById("lstBanco").options;
        i = 0;
        listOptions[0].selected = true;
        
        for(i; i < listOptions.length; i++)
        {
          if(parseInt((listOptions[i].text).substring(0, 3), 10) == parseInt(codBanco, 10))
          {
            listOptions[i].selected = true;
            break;
          }
        }
      }
      
      function fncLimparCampoPesquisa() {
        document.getElementById("txtCodBancoPesquisar").value = "";
      }
    </script>
  </head>
  <body>
    <form>
      <label accesskey="s" for="txtCodBancoPesquisar">Código banco:</label>
      
      <input type="text"
             id="txtCodBancoPesquisa"
             name="txtCodBancoPesquisa"
             onblur="fncPesquisarBanco(this.value)"
             size="4"
             maxlength="3" />
      
      <select id="lstBanco"
              name="cboBancos"
              onchange="fncLimparCampoPesquisa()">
        <option value="">Selecione uma opção...</option>
        <option value="1">001 - BANCO DO BRASIL S.A.</option>
        <option value="33">033 - BANESPA - ESTADO DE SAO PAULO</option>
        <option value="41">041 - BANRISUL - RIO GRANDE DO SUL</option>
        <option value="104">104 - CAIXA ECON. FEDERAL</option>
        <option value="237">237 - BANCO BRADESCO S.A.</option>
        <option value="341">341 - BANCO ITAU S.A.</option>
        <option value="422">422 - BANCO SAFRA S.A.</option>
        <option value="637">637 - BANCO SOFISA S.A.</option>
      </select>
    </form>
  </body>
</html>

Vale lembrar que eu poderia testar o comprimento do campo contra a quantidade de caracteres digitados para sair do campo disparando a função ao atingir a marca de 3 caracteres, mas a intenção aqui é deixar o código pequeno para blogar ;) Também deveria testar se o informado no campo é somente números ou a tacla Enter, e neste último caso invocar a função. Mas, de novo, a intenção aqui é blogar simplesmente como recuperar em JavaScript todos os elementos (options) contidos em um elemento SELECT do HTML.

quarta-feira, outubro 23, 2013

Não deixe o básico da orientação a objetos de lado, nunca!

Quando olho para trás lembro-me de quando eu era um humilde padawan que queria ser um monge Jedi em Java, e lembro claramente dos conselheiros Jedi do RSJUG falando "vá pelo caminho da orientação a objetos". Se passaram alguns muitos anos e eu ainda lembro e sou fiel a esse princípio, e quando vejo por aí classes ou entidades de banco de dados, que eu irei ter que transformar em classes¹, contendo 181 campos (não é piada!) minha gastrite quase me mata!

Eu não sei por que as pessoas teimam em ter campos como CPF, CNPJ, data de inicio e de fim, etc em certas classes. A quem vai ficar destinada a tarefa de validar um CNPJ/CPF? Ou se a data de início não é maior do que a de fim? Não tenha receio de criar classes que não serão persistidas. As pessoas tem a imagem de que classes de domínio/modelo só devem existir se forem ser persistidas, o resto é classe útil, DAO, service, etc. e não é bem por aí.

É do domínio da minha aplicação um CNPJ/CPF! E eu tenho que ter um objeto para isso, com a responsabilidade de validar se ele mesmo é válido. Não vou jogar essa responsabilidade para uma classe megazord no pacote útil responsável por validar isso e, talvez, mais coisas em um único lugar (olha o acoplamento aíííííííí gente).

Bem, para exemplificar isso, vou mostrar um código/abordagem que gosto muito, onde eu separo o campo nome da classe usuário em um objeto a parte.

Se você parar para pensar, o nome deve conter sempre 3 campos: primeiro nome, inicial do meio e último(s) nome(s). Mas e o usuário SISTEMA? Ele não tem sobrenome. E o John Doe que não tem nome do meio?

Então eu tenho a classe PersonName:

using System;
using System.Text;

namespace com.blogspot.jeanjmichel.model
{
    public class PersonName
    {
        private String firstName;
        private String middleInitial;
        private String lastName;
        
        public String FirstName
        {
            set { this.firstName = value; }
            get { return this.firstName; }
        }
        
        public String MiddleInitial
        {
            set { this.middleInitial = value; }
            get { return this.middleInitial; }
        }
        
        public String LastName
        {
            set { this.lastName = value; }
            get { return this.lastName; }
        }
        
        public PersonName()
        {
        }
        
        public PersonName(String firstName)
        {
            this.FirstName = firstName;
        }
        
        public PersonName(String firstName, String middleInitial,
                          String lastName)
        {
            this.FirstName = firstName;
            this.MiddleInitial = middleInitial;
            this.LastName = lastName;
        }
        
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            
            sb.Append(this.LastName);
            sb.Append(", ");
            sb.Append(this.FirstName);
            sb.Append(" ");
            sb.Append(this.MiddleInitial);
            
            return sb.ToString();
        }
    }
}

E agora a classe User:

using System;
using System.Text;

namespace com.blogspot.jeanjmichel.model
{
    public class User
    {
        #region attributes
        
        Int64 id;
        String username;
        PersonName personName;
        char gender;
        String password;
        char active;
        
        #endregion
        
        #region properties
        
        public Int64 Id
        {
            set { this.id = value; }
            get { return this.id; }
        }
        
        public String Username
        {
            set { this.username = value; }
            get { return this.username; }
        }
        
        public PersonName PersonName
        {
            set { this.personName = value; }
            get { return this.personName; }
        }
        
        public char Gender
        {
            set { this.gender = value; }
            get { return this.gender; }
        }
        
        public String Password
        {
            set { this.password = value; }
            get { return this.password; }
        }
        
        public char Active
        {
            set { this.active = value; }
            get { return this.active; }
        }
        
        #endregion
        
        /// <summary>
        /// Returns a string with this instance's data
        /// The format of the string is:
        /// namespace + class name + [ + the fields most representative
        /// of the class + ].
        /// </summary>
        /// <returns>Returns a string with this instance's data</returns>
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            
            sb.Append("com.blogspot.jeanjmichel.model.User [");
            sb.Append(this.PersonName);
            sb.Append(" as '");
            sb.Append(this.Username);
            sb.Append("'");
            sb.Append(" Active: ");
            sb.Append(this.Active);
            sb.Append("]");
            
            return sb.ToString();
        }
        
        public User()
        {
        }
        
        public User(String username, PersonName personName, String password,
                    char active)
        {
            this.Username = username;
            this.PersonName = personName;
            this.Password = password;
            this.Active = active;
        }
        
        public User(Int64 id, String username, PersonName personName,
                    char gender, String password, char active)
        {
            this.Id = id;
            this.Username = username;
            this.PersonName = personName;
            this.Gender = gender;
            this.Password = password;
            this.Active = active;
        }
    }
}

Agora criando um usuário:

using System;
using com.blogspot.jeanjmichel.model;

namespace com.blogspot.jeanjmichel
{
    class Program
    {
        static void Main(string[] args)
        {
            PersonName pn = new PersonName("Jean", "J", "Michel");
            User u = new User("jmichel", pn, "Senha", 'Y');
            
            Console.WriteLine(u);
            Console.ReadKey();
        }
    }
}

A saída é: com.blogspot.jeanjmichel.model.User [Michel, Jean J as 'jmichel' Active: Y].

Quase tudo funcionou.
Mas no teste unitário quando eu digo que criando o usuário System assim:

using System;
using com.blogspot.jeanjmichel.model;

namespace com.blogspot.jeanjmichel
{
    class Program
    {
        static void Main(string[] args)
        {
            PersonName pn = new PersonName("System");
            User u = new User("sysadmin", pn, "Senha", 'Y');
            
            Console.WriteLine(u);
            Console.ReadKey();
        }
    }
}

Eu espero o resultado: com.blogspot.jeanjmichel.model.User [System as 'sysadmin' Active: Y] o que acontece?

Failed TestUserToStringMethod TestProject Assert.AreEqual failed.
Expected:<com.blogspot.jeanjmichel.model.User [System as 'sysadmin' Active: Y]>.
Actual..:<com.blogspot.jeanjmichel.model.User [, System as 'sysadmin' Active: Y]>.


Holly crap! Então, a resposabilidade da minha classe PersonName é validar esse tipo de coisa. Refatorando-a teremos:

using System;
using System.Text;

namespace com.blogspot.jeanjmichel.model
{
    public class PersonName
    {
        private String firstName;
        private String middleInitial;
        private String lastName;
        
        public String FirstName
        {
            set { this.firstName = value; }
            get { return this.firstName; }
        }
        
        public String MiddleInitial
        {
            set { this.middleInitial = value; }
            get { return this.middleInitial; }
        }
        
        public String LastName
        {
            set { this.lastName = value; }
            get { return this.lastName; }
        }
        
        public PersonName()
        {
        }
        
        public PersonName(String firstName)
        {
            this.FirstName = firstName;
        }
        
        public PersonName(String firstName, String middleInitial,
                         String lastName)
        {
            this.FirstName = firstName;
            this.MiddleInitial = middleInitial;
            this.LastName = lastName;
        }
        
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            
            if (String.IsNullOrEmpty(this.LastName))
            {
                sb.Append(this.FirstName);
            }
            else
            {
                sb.Append(this.LastName);
                sb.Append(", ");
                sb.Append(this.FirstName);
                sb.Append(String.IsNullOrEmpty(this.MiddleInitial) ? "" :
                " " + this.MiddleInitial);
            }
            
            return sb.ToString();

        }
    }
}

Eu faço o mesmo com CNPJ/CPF, datas (eu crio uma classe chamada período) e outras.

¹ Eu não criei uma classe com 181 campos, mas também não pude refatorar a classe, a tabela, etc (software de terceiros). então criei uma classe com o set mínimo de informações da tabela, que deu mais de 30 campos, e faço o enxerto do resto com os valores padrão para cada campo. Uma pena eu não poder quebrar essa maldita tabela em meia dúzia de 8 tabelas mais produtivas e reutilizáveis. Enfim, coisas da TI que o McGyver aprovaria :)

terça-feira, outubro 08, 2013

Utilizando métodos seguros no WCF

Recentemente estive trabalhando com o WCF (Windows Communication Foundation) da Microsoft. E depois de fazer algumas experiências básicas lá fui eu me certificar da forma de transmissão segura e criptografadas.
Não vou me alongar na explicação porque isso tem no MSDN e em n blogs melhores escritor do que o meu :-), mas, como de praxe vou registrar um exemplo aqui (cabe ressaltar que daria para refatorar um pouco esse meu exemplo, que eu tive que fazer as pressas, principalmente a classe Greetting.cs que ficou bem porca mesmo. Admito, sem orgulho :-| #ficaAdica).

Para quem não sabe o que é o WCF, aqui vai a definição da Microsoft:

Windows Communication Foundation (WCF) is Microsoft's unified programming model for building service-oriented applications. It enables developers to build secure, reliable, transacted solutions that integrate across platforms and interoperate with existing investments.

Sacou?

Então vamos ao que interessa: código!
Em meu exemplo eu criei uma classe chamada Credential.cs que irá armazenar um token (que no meu caso é uma senha) e a informação de que tipo de device está consumindo meu serviço.
Além disso, há uma classe onde a lógica do meu programa está implementada e outra classe que é o serviço em sí (a lógica do tratamento das chamadas ao serviço).
Simplão, o funcionamento é: conferir se a senha passada está correta e se estiver chamar a classe que vai gerar a saudação e retorná-la ao client, caso contrário é lançar uma exceção.

Para começar essa é a classe da credencial (Credential.cs):

using System;
using System.ServiceModel;
using System.Net.Security;

namespace com.blogspot.jeanjmichel.services
{
    /// <summary>
    /// This class represents the credential.
    /// </summary>
    [MessageContract]
    public class Credential
    {
        /// <summary>
        /// The token is a password that have be valid to authorize the 
        /// client to consume the service.
        /// </summary>
        [MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public String Token;
        
        /// <summary>
        /// The DeviceCategory is an inrelevant information that is not 
        /// encrypted and is just used to show the kind of device is 
        /// consuming the service (Whindows Phone, iOS, Android, web page, 
        /// etc).
        /// </summary>
        [MessageBodyMember(Order = 1, ProtectionLevel = ProtectionLevel.None)]
        public String DeviceCategory;

        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Credential()
        {
        }
    }
}


Notem que a informação do token irá trafegar criptografada, no header da mensagem. Já a informação do device do client não irá trafegar criptografada.
Greeting.cs, este é o core, ou kernel, do programa, dependendo do quanto nojento você queira parecer falando isso :-) (roubei essa do Elemar Jr):

using System;
using System.ServiceModel;
using System.Net.Security;

namespace com.blogspot.jeanjmichel.model
{
    /// <summary>
    /// This class represents a greeting.
    /// </summary>
    [MessageContract]
    public class Greeting
    {
        private String userGreeting;

        /// <summary>
        /// This method will set an appropriate greeting based on the server's 
        /// local time.
        /// </summary>
        private void SetGreeting()
        {
            DateTime now = DateTime.Now;

            if (now.Hour >= 7 && now.Hour <= 11)
            {
                this.userGreeting = "Good morning";
            }
            else if (now.Hour >= 12 && now.Hour <= 17)
            {
                if (now.Hour == 12 || now.Hour == 13)
                {
                    this.userGreeting = "Good afternoon, it's lunch time!";
                }
                else
                {
                    this.userGreeting = "Good afternoon";
                }
            }
            else if (now.Hour >= 18 && now.Hour <= 20)
            {
                this.userGreeting = "Good evening";
            }
            else
            {
                this.userGreeting = "Good night";
            }
        }

        /// <summary>
        /// This property returns the greeting in the message body,
        /// and this will move across the network ever encrypted.
        /// </summary>
        [MessageBodyMember(Order = 1, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public String UserGreeting
        {
            get { return this.userGreeting; }
        }


        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Greeting()
        {
            this.SetGreeting(); //Call the method on construction time.
        }
    }
}


Fica a dica para quem quiser refatorar essa classe: usar internacionalização para retornar a saudação em n idiomas.

E por fim, mas não menos importante :-), a interface (contrato) do serviço e a implementação dele:

using System.ServiceModel;
using com.blogspot.jeanjmichel.model;

namespace com.blogspot.jeanjmichel.services.contract
{
    /// <summary>
    /// This interface defines the service methods.
    /// </summary>
    [ServiceContract(Namespace = "http://jeanjmichel.blogspot.com/services/v0.0.1")]
    public interface IGetGreeting
    {
        /// <summary>
        /// This method will returns a greeting based based on the server's 
        /// local time.
        /// </summary>
        /// <param name="credential">An access credential that will be validated
        ///                          in order to authorize the access to the 
        ///                          method.</param>
        /// <returns>A Greeting object.</returns>
        [OperationContract]
        Greeting GetGreeting(Credential credential);
    }
}


using System;
using System.ServiceModel;
using com.blogspot.jeanjmichel.services.contract;
using com.blogspot.jeanjmichel.model;

namespace com.blogspot.jeanjmichel.services
{
    /// <summary>
    /// This class is the service. Is where the logic behind the services' 
    /// calls occurs (validade credencial, invoke the business layer, etc).
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,
                     Namespace = "http://jeanjmichel.blogspot.com/services/v0.0.1")]
    public class GetGreetingService: IGetGreeting
    {
        /// <summary>
        /// This method will returns a greeting based based on the server's 
        /// local time.
        /// </summary>
        /// <param name="credential">An access credential that will be validated
        ///                          in order to authorize the access to the 
        ///                          method.</param>
        /// <returns>A Greeting object.</returns>
        public Greeting GetGreeting(Credential credential)
        {
            if (String.IsNullOrEmpty(credential.Token))
            {
                throw new FaultException("Inform the security phrase," +
                                         " and try again.");
            }
            else
            {
                if (credential.Token.Equals("mySeCuriTyP@ss"))
                {
                    Greeting g = new Greeting();
                    return g;
                }
                else
                {
                    throw new FaultException("Wrong password.");
                }
            }
        }
    }
}


Só falta configurar a aplicação no app.config, na tag <endpoint> o atributo binding de basicHttpBinding por wsHttpBinding:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="GetGreetingService">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8081/soa" />
          </baseAddresses>
        </host>
        <endpoint address ="" 
                  binding="wsHttpBinding"
                  contract="com.blogspot.jeanjmichel.services.contract.IGetGreeting"
                  bindingNamespace="http://jeanjmichel.blogspot.com/services/v0.0.1" >
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <useRequestHeadersForMetadataAddress>
            <defaultPorts>
              <add scheme="http" port="8010" />
            </defaultPorts>
          </useRequestHeadersForMetadataAddress>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>


Agora o client para consumir o serviço:

using ConsoleClient.GetGreetingService; using System; using System.ServiceModel;   namespace ConsoleClient {     public class Program     {         static void Main(string[] args)         {             Console.WriteLine("Connecting to the endpoint...");             ChannelFactory<IGetGreeting> factory =                  new ChannelFactory<IGetGreeting>("WSHttpBinding_IGetGreeting",                      new EndpointAddress("http://192.168.0.173:8081/soa/GetGreetingService.svc"));               Console.WriteLine("Creating the proxy...");             IGetGreeting proxy = factory.CreateChannel();               Console.WriteLine("Creating the credential...");             Credential credential = new Credential();             credential.Token = "mySeCuriTyP@ss";             credential.DeviceCategory = "Windows Phone 7.8";               try             {                 Console.WriteLine("Call the service...");                 Greeting greeting = proxy.GetGreeting(credential);                   Console.WriteLine("Greeting: " + greeting.UserGreeting);             }             catch (Exception e)             {                 Console.WriteLine("An error has occurred. Message: " + e.Message);             }                          Console.ReadKey();         }     } }

Rodando esse client assim como está aqui a saída seria:

Connecting to the endpoint... Creating the proxy... Creating the credential... Call the service... Greeting: Good evening

Se você errar a senha:

Connecting to the endpoint... Creating the proxy... Creating the credential... Call the service... An error has occurred. Message: Wrong password.

E se você anular a senha:

Connecting to the endpoint... Creating the proxy... Creating the credential... Call the service... An error has occurred. Message: Inform the security phrase, and try again.

Vale ressaltar que eu estou hospedando a aplicação em um IIS local para testes.

Era isso. Qualquer dúvida é só comentar.