.NET - LINQ To IEnumerable
Um dos aspectos elegantes do LINQ é que as suas consultas podem ser executadas contra qualquer fonte de dados que implemente a interface IEnumerabe.
Suponha que você precise procurar por todos os arquivos modificados na última hora na pasta Meus Documentos do usuário atual. Vejamos como podemos realizar esta e outras tarefas usando os recursos do LINQ...
Pesquisando o sistema de arquivos
Vamos usar o Visual Basic 2008 Express Edition , sim isso mesmo , o VB 2008 Express também suporta o LINQ.
Crie um novo projeto do tipo Windows Application usando a linguagem VB .NET com o nome linqIEnumerable;
No formulário padrão form1.vb inclua um DataGridView (dtgdv_arq)e um botão de comando(btnExibir) e no evento Click do botão de comando inclua o seguinte código:
Imports System.IO Public Class Form1 Private Sub btnExibir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExibir.Click Dim arquivos As String() Dim meusdocumentos As String meusdocumentos = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) arquivos = Directory.GetFiles(meusdocumentos, "*.*", SearchOption.TopDirectoryOnly) Dim consulta = From arquivos In diretorio _ Let ultimahora = DateTime.Now.Subtract(New TimeSpan(0, 1, 0, 0)) _ Where File.GetLastAccessTime(arquivos) >= ultimahora AndAlso (File.GetAttributes(arquivos) And FileAttributes.System) = 0 _ Select New With {.nomeArquivos = System.IO.Path.GetFileName(arquivos)} dtgdv_arq.DataSource = consulta.ToList End Sub End Class |
Vamos entender o que foi feito:
- Declararmos o imports System.IO pois vamos acessar o sistema de arquivos do sistema;
- Definimos a string meusdocumentos
para obter o diretório onde iremos efetuar a seleção. No
exemplo irei selecionar na pasta Meus Documentos;
- Definimos a variável arquivos como um array de String() onde
iremos armazenar os arquivos do diretório Meus Documentos.
Observe que usei a propriedade TopDirectoryOnly. Para buscar em
todas as sub-pastas temos a opção AllDirectories.
- Em seguida temos a consulta LINQ:
Dim
consulta = From arquivos In diretorio _
Let ultimahora = DateTime.Now.Subtract(New TimeSpan(0, 1, 0, 0))
_
Where
File.GetLastAccessTime(arquivos) >= ultimahora AndAlso
(File.GetAttributes(arquivos) And FileAttributes.System) = 0 _
Select
New With {.nomeArquivos = System.IO.Path.GetFileName(arquivos)}
Vamos debulhar a consulta:
- consulta é um tipo anônimo inferido como sendo do tipo System.Collection.Generic.IEnumerable(Of <tipo anônimo>)
- Let ultimahora = DateTime.Now.Subtract(New TimeSpan(0, 1, 0, 0))
Aqui temos a presença da instrução Let a qual permite a definição de valores locais em uma consulta; ela é usada para dar uma melhor legebilidade ao código pois estamos atribuindo a variável ultimahora a data na qual vamos efetuar o filtro da consulta;
- Where File.GetLastAccessTime(arquivos) >= ultimahora AndAlso (File.GetAttributes(arquivos) And FileAttributes.System) = 0
Definimos o critério para seleção para arquivos acessados a partir da última hora e somente arquivos que não sejam do sistema.
- Select New With {.nomeArquivos = System.IO.Path.GetFileName(arquivos)}
Para poder obter o nome dos arquivos que atendem ao critério definimos um tipo anônimo. Aqui vemos o poder do LINQ sem este recurso teríamos que criar uma classe para definir o tipo de dados que desejamos retornar.
Executando o projeto iremos obter a relação de arquivos da pasta Meus Documentos que sofreram qualquer alteração na última hora:
Poderíamos ainda criar uma rotina para procurar por um arquivo específico em uma pasta específica.
Inclua no formulário mais um botão de comando(btnProcurar) com o texto Procurar e também dois TextBox: txtDiretorio e txtNomeArquivo;
Veja abaixo o código de uma rotina usando LINQ que faz exatamente isso:
Private Sub procuraArquivosPorNome(ByVal DirectorioPath As String, ByVal NomeArquivo As String) Dim consulta = From file In New DirectoryInfo(DirectorioPath).GetFiles() _ Where file.Name = NomeArquivo _ Select file dtgdv_arq.DataSource = consulta.ToList End Sub |
A chamada pode ser feita no evento Click do botão de comando conforme o código abaixo:
Private Sub btnProcurar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProcurar.Click Dim caminho As String = txtDiretorio.Text Dim nome As String = txtNomeArquivo.text procuraArquivosPorNome(caminho, nome) End Sub |
O resultado da execução deste código onde informamos o diretório c:\ e o arquivo Macoratti.txt é visto a seguir:
Dica: Para selecionar um arquivo pela sua extensão substitua a consulta acima pela do código abaixo:
Dim consulta = From file In New DirectoryInfo(DirectoryPath).GetFiles() _ Where file.Extension = Extention _ Select file |
Pesquisando o interior de um arquivo
Vamos agora realizar outra tarefa com LINQ: pesquisar no interior de um arquivo por um determinado caractere/palavra.
Inclua um novo formulário Windows form2.vb no projeto a partir do menu Project -> Add New Windows Forms
Inclua no formulário a partir da ToolBox os seguintes controles:
A seguir vamos definir a rotina procuraArquivo usando uma consulta LINQ:
Private Sub procuraNoArquivo(ByVal arquivo As String, ByVal criterio As String) Dim linhas = From linha In File.ReadAllLines(arquivo) _ Where linha.Contains(criterio) _ Select New With {.nome = linha.ToString()} gdvResultado.DataSource = linhas.ToList End Sub |
No evento Click do botão de comando chamamos a rotina passando o caminho e nome do arquivo e o que desejamos procurar.
No código estamos lendo todas as linhas do arquivo (ReadAllLines) para um array e então procurar no array para selecionar as linhas que contém o caractere "file" usado no exemplo.
O resultado é mostrado abaixo exibe em um DataGridView as linhas que possui a palavra file:
Acessando o log de eventos
Precisa acessar o log de eventos. Com LINQ é barbada...
Inclua um novo formulário form3.vb no projeto e inclua no formulário um DataGridView e um botão de comando. A seguir inclua o código abaixo no evento Click do botão de comando:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim applog As New SD.EventLog("Application", ".") Dim aplicativo As String = "explorer.exe" Dim usuario As String = "Macoratti" Dim entradas = From entrada In applog.Entries.Cast(Of SD.EventLogEntry)() _ Where entrada.Source = aplicativo AndAlso entrada.UserName = usuario _ Order By entrada.TimeWritten Descending _ Select entrada DataGridView1.DataSource = entradas.ToList End Sub |
Neste código estamos acessando o log de eventos e procurando pelas entradas de um determinado usuário em uma determinada aplicação.
Acessando os contatos do Outlook
Podemos usar o LINQ no Visual Studio Tools for Office (VSTO) e como exemplo vamos procurar pelos contatos de usuário no Outlook.
Neste caso podemos usar o código abaixo:
Dim folder As Outlook.MAPIFolder = Me.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts) Dim contacts = From contact In folder.Items.OfType(Of Outlook.ContactItem)() _ Where contact.Email1Address IsNot Nothing _ Select contact |
Neste código estamos usando o
recurso do operador de consulta OfType do LINQ o
qual encapsula coleções anteriores a versão 2.0 da plataforma
.NET e que filtra a coleção para retornar somente os objetos do
tipo desejado.
Vejamos uma variação da consulta LINQ acima onde desejamos
coletar todas os endereços distintos de emails a partir dos
contatos de usuário no Outlook :
Dim folder As Outlook.MAPIFolder = Me.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts) Dim emails = (From contact In folder.Items.OfType(Of Outlook.ContactItem)() _ Where contact.Email1Address IsNot Nothing _ Select contact.Email1Address).Distinct() |
E poderíamos continuar com uma lista de tarefas quase sem fim usando as consultas LINQ mas em outro artigo eu prometo continuar mostrando outras características importantes do LINQ.
Pegue o projeto completo aqui: LinqEnumerable.zip
Eu sei é apenas LINQ , mas eu gosto...
Referências:
José Carlos Macoratti