.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