WPF - Login com Linq To SQL


Vamos criar uma aplicação WPF com uma página de Login usando Linq To SQL para mostrar primeiro que o LINQ to SQL ainda existe e pode ser usado no Visual Basic 2010 Express Edition e segundo que é muito simples usar o Linq to SQL.

Vou iniciar mostrando o funcionamento da aplicação:

1- Ao iniciar a aplicação o formulário de Login é apresentado:

2- Se o login do usuário e a senha forem válidos então será aberta uma página onde executamos um vídeo;

3- Se o login e/ou senha forem inválidos então será apresentada uma mensagem ao usuário:

4- No evento Click do botão Sair temos a exibição de uma caixa de mensagem ao usuário solicitando a confirmação para fechar ou não a aplicação:

É uma aplicação bem simples mas que envolve alguns conceitos básicos para iniciantes em aplicações WPF e que usa o LINQ to SQL para simplificar o código de acesso as informações de um banco de dados SQL Server chamado Cadastro.mdf, onde existe a tabela Usuarios que armazena o login e a senha do usuário.

A estrutura da tabela Usuarios é mostrada abaixo:

Para simplificar o acesso as informações vamos usar o Linq to SQL para mapear a tabela Usuarios como uma classe e assim usar uma consulta Linq to SQL para acessar as informações na tabela.

LINQ to SQL é uma implementação específica do LINQ para o SQL Server que converte consultas escritas em C# ou Visual Basic em SQL dinâmico , provendo uma interface que permite mapear os objetos do banco de dados gerando as classes para realizar as operações usando a sintaxe LINQ; também permite realizar alterações nos objetos e atualizar o banco de dados.

1 - Criando o projeto WPF no Visual Basic 2010 Express Edition

Abra o VB 2010 Express Edition e crie um novo projeto no menu File -> New Project selecionando o template WPF Application, informando o nome WPF_Login_Linq e clicando em OK;

Agora vamos incluir a partir do menu Project->Add New Item , a partir da janela Templates, o item LINQ to SQL Classes alterando o nome para Login.dbml e clicando no botão Add;

Neste momento será exibida a janela do descritor Objeto Relacional. Expanda os objetos do banco de dados Cadastro.mdf e selecione a tabela Usuarios arrastando e soltando-a na janela Object Relational Designer;

Você pode criar o banco de dados Cadastro.mdf e a tabela Usuarios usando o SQL Server Management Studio Express Edition.

Você pode fazer isso usando diretamente a ferramenta conforme eu mostrei neste artigo:

Usando o SQL Server Management Studio - Macoratti.net

Pode também criar o banco de dados e a tabela executando os scripts sql descritos a seguir:

1- Script para criar o banco de dados Cadastro.mdf (cria na pasta c:\dados)

USE [master]
GO

CREATE DATABASE [Cadastro] ON PRIMARY
( NAME = N'Cadastro', FILENAME = N'c:\dados\Cadastro.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'Cadastro_log', FILENAME = N'c:\dados\Macoratti_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [Cadastro].[dbo].[sp_fulltext_database] @action = 'enable'

end

2- Script para criar a tabela Usuarios

USE [Cadastro]

GO


SET
ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[Usuarios](

   [id] [int] IDENTITY(1,1) NOT NULL,

   [login] [nvarchar](50) NULL,

   [nome] [nvarchar](50) NULL,

   [senha] [nvarchar](50) NULL,

   [email] [nvarchar](80) NULL,

CONSTRAINT [PK_Usuarios] PRIMARY KEY CLUSTERED

(

[id] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

 

Para executar os scripts basta abrir o SQL Server Management Studio Express e clicar em New Query , copiar e colar os scripts (na ordem e um de cada vez) e clicar no botão Execute;

As tabelas do banco de dados serão mapeadas como classes (campos como propriedades, procedures e funções como métodos)  e você terá no Descritor o conjunto de classes que representam o banco de dados. Para o nosso exemplo a tabela Usuarios será mapeada para a classe Usuario conforme mostra a imagem acima;

O arquivo Login.dbml contém o arquivo XML com informações sobre o leiaute das tabelas que foram mapeadas e também o descrito contendo as classes geradas pelo mapeamento. Após encerrar o mapeamento você já terá acesso aos recursos do LINQ To SQL com direito a intellisense completo das informações referente as tabelas mesmo sem conhecer nada sobre elas.

Vamos agora criar a interface na página MainWindow.xaml usando os seguintes controles:

  1. 2 Labels
  2. 1 TextBox - txtLogin
  3. 1 PasswordBox - txtSenha
  4. 2 Button - btnLogin e btnSair

Conforme o leiaute abaixo:

O código XAML gerado para o formulário acima é dado a seguir:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Login com LINQ" Height="468" Width="575">
    <Grid Background="#0E62FF00" Height="259" Width="458">
        <Label Content="Nome do Usuário " Height="28" HorizontalAlignment="Left" Margin="69,58,0,0" Name="Label1" VerticalAlignment="Top" Width="123" Foreground="White" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="198,60,0,0" Name="txtLogin" VerticalAlignment="Top" Width="137" Background="#9C9BE9EB" />
        <Label Content="Senha" Height="28" HorizontalAlignment="Left" Margin="71,95,0,0" Name="Label2" VerticalAlignment="Top" Width="106" Foreground="White" />
        <Button Content="Login" Height="31" HorizontalAlignment="Left" Margin="189,156,0,0" Name="btnLogin" VerticalAlignment="Top" Width="75" />
        <Button Content="Sair" Height="31" HorizontalAlignment="Left" Margin="277,156,0,0" Name="btnSair" VerticalAlignment="Top" Width="75" />
        <Image Height="66" HorizontalAlignment="Left" Margin="79,129,0,0" Name="Image1" Stretch="Fill" VerticalAlignment="Top" Width="69" Source="/WPF_Login_Linq;component/Images/login.jpg" />
        <PasswordBox Height="23" Margin="200,96,0,0" Name="txtSenha" VerticalAlignment="Top" Password="" Background="#9C9BE9EB" HorizontalAlignment="Left" Width="134" />
    </Grid>
    <Window.Background>
        <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
            <GradientStop Color="Black" Offset="0" />
            <GradientStop Color="White" Offset="1" />
        </LinearGradientBrush>
    </Window.Background>
</Window>

Além da página MainWindow.xaml vamos incluir uma nova página no projeto. No menu Project -> Add New Item, selecione o template Page(WPF) e aceite o nome padrão Page1.xaml;

Agora nesta página vamos incluir apenas uma declaração XAML que usa o controle MediaElement para exibir um vídeo na página;

<Page x:Class="Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="440" d:DesignWidth="581"
      Title="Page1">
    <Grid Height="411" Width="588">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48*" />
            <ColumnDefinition Width="540*" />
        </Grid.ColumnDefinitions>
        <MediaElement Source="C:\Users\Public\Videos\Sample Videos\lake.wmv" Margin="12,12,12,22" Grid.ColumnSpan="2" />
    </Grid>
</Page>

Eu estou usando o vídeo de amostra do Windows Lake.wmv.

A classe MediaElement representa um controle que contém áudio e/ou vídeo e pode ser usado em dois modos distintos, dependendo de como o controle esta direcionado: 

Vejamos agora a implementação do código na página WPF.

Primeiro declaramos os namespaces usados no projeto:

Imports System.Linq
Imports System.Data
Imports System.ComponentModel
Imports System.Collections.Generic

A seguir temos o código do evento Click do botão Login:

 Private Sub btnLogin_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLogin.Click

        If (verificaUsuario(txtLogin.Text.Trim(), txtSenha.Password.Trim()) = True) Then
            Dim page1 As New Page1()
            Me.Content = page1
        Else
            Dim result As MessageBoxResult = MessageBox.Show("Login/Senha Inválidos", "Login Inválido")
        End If

Neste código usamos a rotina verificaUsuario() passando o login e a senha para verificar se as credenciais são válidas. Em caso positivo abrimos a página Page1() que irá exibir o vídeo.

A rotina verificaUsuario() usa os recursos do Linq To SQL para consultar a tabela Usuarios e verificar se o nome e senha do usuário existem:

Public Function verificaUsuario(ByVal userName As String, ByVal senha As String) As Boolean
        Dim db As New LoginDataContext()

        Dim nomes = (From lg In db.Usuarios Where lg.login = userName AndAlso lg.senha = senha).SingleOrDefault()
        If nomes IsNot Nothing Then
            Return True
        Else
            Return False
        End If
    End Function

No código estamos criando uma instância do nosso DataContext : Dim db As New LoginDataContext()

Aqui o banco de dados é mapeado em um DataContext permitindo acesso a tabelas de forma transparente sem nos preocuparmos com conexão. O DataContext utiliza a interface IDbConnection do ADO.NET para acessar o armazenamento e pode ser inicializado tanto com um objeto de conexão ADO.NET estabelecido ou com uma string de conexão que possa ser utilizada para criar a conexão.

O DataContext é o responsável por gerar as instruções SQL da sua linguagem de consulta e então mapear as linhas de dados retornadas a partir do seu banco de dados para objetos.

Em seguida executamos a consulta Linq To SQL: Dim nomes = (From lg In db.Usuarios Where lg.login = userName AndAlso lg.senha = senha).SingleOrDefault()

A consulta LINQ To SQL inicia com a cláusula From e em seguida o operador de condição Where e no final a cláusual SingeOrDefault.

Obs: O LINQ possui o método de extensão Single/SingleOrDefault que pode ser usado para retornar somente um elemento em uma sequência que satisfaça uma condição específica.

Um dos motivos desta inversão de ordens é  o uso recurso IntelliSense, pois quando você indica primeiro a origem dos dados ele pode mostrar as listas de membros de tipos nos objetos em sua coleção. Outro motivo , segundo Anders Hejlsberg , seria que esta ordem esta mais próxima da nossa lógica de pensamento. Quando você digita uma instrução SQL iniciando com Select na verdade você já esta pensando na origem dos dados , condições , agrupamentos. etc.

A cláusula From é a mais importante do LINQ To SQL pois é usada em todas as consultas. Uma consulta deve sempre começar com From. (O Select pode estar implícito o From não.)

Este código define a variável nomes do tipo Usuario, note que o compilador esta inferindo o tipo da variável pois ela não foi declarada no código.

Este é um dos recursos da plataforma .NET, a inferência de tipos, que ocorre quando não há declaração explícita do tipo do objeto e o compilador descobre e atribui o tipo correto ao objeto declarado; é o que acontece neste caso.

Se o nome e a senha existirem retornamos True caso contrário retornamos False.

Para encerrar temos a seguir o código dos botões Sair que encerra a aplicação e do evento Load da janela que coloca o foco no campo txtLogin:

Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        txtLogin.Focus()
End Sub

Private Sub btnSair_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnSair.Click
        If MessageBox.Show("Deseja realmente sair?", "Confirmação", MessageBoxButton.YesNo) = MessageBoxResult.Yes Then
            Me.Close()
        End If
End Sub

Dessa forma , usando LINQ to SQL é muito simples tratar com o acesso a dados em sua aplicação quer seja ela Windows Forms, WPF ou ASP .NET.

Aguarde em breve mais artigos sobre WPF.

Pegue o projeto completo aqui: WPF_Login_Linq.zip

Eu sei é apenas WPF , mas eu gosto...

Referências:

José Carlos Macoratti