Arquivos CSV com C# no ASP .Net MVC 4

Existem várias formas de armazenar seus dados, uma delas é utilizar arquivos CSV com C#, que são arquivos de textos que usam algum separador para seus campos, vamos ver como consumir esses arquivos em nossa aplicação em ASP .Net MVC.

Veja uma demonstração da aplicação utilizado como exemplo em http://cid.apphb.com/

A motivação deste post veio da necessidade de importar os dados do CID (Cadastro internacional de Doenças) do DataSUS, eles fornecem os arquivos em CSV com o cadastro das Doenças catalogadas em Capítulos, Grupos, Categorias e Subcategorias, veja abaixo um pequeno trecho de um dos arquivos:

1
2
3
4
5
NUMCAP;CATINIC;CATFIM;DESCRICAO;DESCRABREV;
1;A00;B99;Capítulo I - Algumas doenças infecciosas e parasitárias;I.  Algumas doenças infecciosas e parasitárias;
2;C00;D48;Capítulo II - Neoplasias [tumores];II.  Neoplasias (tumores);
3;D50;D89;Capítulo III  - Doenças do sangue e dos órgãos hematopoéticos e alguns transtornos imunitários;III. Doenças sangue órgãos hemat e transt imunitár;
4;E00;E90;Capítulo IV - Doenças endócrinas, nutricionais e metabólicas;IV.  Doenças endócrinas nutricionais e metabólicas;

Reparem como funciona a estrutura dos arquivos CSV, na primeira linha temos o nome dos campos (colunas) e as demais linhas são os dados, para estes arquivos foi utilizado o “;” como separador.
Todos os demais arquivos seguem esta mesma estrutura, sabendo disto temos como fazer um parse neste arquivos e depois manipula-los em memória.

Lendo o arquivos CSV em C#

Você pode armazenar estes arquivos para a leitura em uma pasta, ou dentro do próprio projeto usando o resources, neste caso utilizei os resources.
Antes de continuar precisamos instalar um pacote do nuget chamado CsvHelper.

1
PM> Install-Package CsvHelper

Uma vez instalado foi criado uma classe genérica para a leitura destes arquivos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System.Collections.Generic;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;
 
namespace Repositorio
{
    public class LerCsv<T> where T : class
    {
        public IEnumerable<T> Ler(string csv)
        {
            if (csv == null) return null;
            var configuracaoCsv = new CsvConfiguration { Delimiter = ";" };
            var csvReader = new CsvReader(new StringReader(csv), configuracaoCsv);
            return csvReader.GetRecords<T>();
        }
    }
}

Dentro desta classe foi criado um método Ler que recebe como parâmetro o conteúdo do arquivo CSV e retorna uma Lista de objetos que representam as linhas deste arquivo.
Uma vez criado o método genérico que fara a leitura do conteúdo do arquivo CSV qualquer objeto que for passado para este método será utilizado como estrutura que representa o arquivo CSV, para exemplificar reparem o código da classe que representa o arquivo de capítulos.csv, chamada ViewModelCapitulo.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ViewModelCapitulo
    {
        //NUMCAP: número do capítulo (em numeração arábica); se igual a zero, indica o capítulo que contém os códigos não oficialmente pertinentes à CID-10;
        [CsvField(Name = "NUMCAP")]
        public string NumeroCapitulo { get; set; }
 
        //CATINIC: código da primeira categoria do capítulo;
        [CsvField(Name = "CATINIC")]
        public string CategoriaInicial { get; set; }
 
        [CsvField(Ignore = true)]
        public int CategoriaInicialCodigo
        {
            get { return Ferramentas.TrocaLetraPorNumero(CategoriaInicial); }
        }
 
        //CATFIM: código da última categoria do capítulo;
        [CsvField(Name = "CATFIM")]
        public string CategoriaFinal { get; set; }
 
        [CsvField(Ignore = true)]
        public int CategoriaFinalCodigo
        {
            get { return Ferramentas.TrocaLetraPorNumero(CategoriaFinal); }
        }
 
        //DESCRICAO: descrição (nome) do capítulo; e
        [CsvField(Name = "DESCRICAO")]
        public string Descricao { get; set; }
 
        //DESCRABREV: descrição (nome) abreviado do capítulo, com até 50 caracteres.
        [CsvField(Name = "DESCRABREV")]
        public string DescricaoAbreviada { get; set; }
    }

Reparem que a mesma estrutura (colunas) do arquivo csv foi representada na classe usando data annotations que vem com o CsvHelper (CsvField), no exemplo a propriedade da classe “NumeroCapitulo” representa a coluna “NUNCAP” do arquivo CSV, o que significa que o valor da coluna NUNCAP será carregada para a propriedade NumeroCapitulo.
As demais propriedades seguem o mesmo modelo, exceto é claro a propriedade “CategoriaInicialCodigo” que não representa nenhuma coluna do arquivo CSV de capítulos, uma vez que foi utilizado o data annotation
“[CsvField(Ignore = true)]”.

Se ficou alguma dúvida de como funciona os datas annotations assista a vídeo aula: ASP.Net MVC na Prática: Data Annotatios e Validação. Pode assistir eu espero! :)

Listando os dados de um arquivo CSV em C#

Uma vez criado as classes que serão utilizadas como base para os nossos arquivos CSV, precisamos utiliza-las através do nosso método “Ler” da classe “LerCsv.cs” como mostra o código da classe abaixo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections.Generic;
 
namespace Repositorio
{
    public class Repositorio
    {
        public IEnumerable<ViewModelCapitulo> ListarCapitulos()
        {
            var recurso = Properties.Resources.CID_10_CAPITULOS;
            return new LerCsv<ViewModelCapitulo>().Ler(recurso);
        }
 
        public IEnumerable<ViewModelGrupo> ListarGrupos()
        {
            var recurso = Properties.Resources.CID_10_GRUPOS;
            return new LerCsv<ViewModelGrupo>().Ler(recurso);
        }
 
        public IEnumerable<ViewModelCategoria> ListarCategorias()
        {
            var recurso = Properties.Resources.CID_10_CATEGORIAS;
            return new LerCsv<ViewModelCategoria>().Ler(recurso);
        }
 
        public IEnumerable<ViewModelSubCategoria> ListarSubCategorias()
        {
            var recurso = Properties.Resources.CID_10_SUBCATEGORIAS;
            return new LerCsv<ViewModelSubCategoria>().Ler(recurso);
        }
 
        public IEnumerable<ViewModelGrupoCid0> ListarGruposCid0()
        {
            var recurso = Properties.Resources.CID_O_GRUPOS;
            return new LerCsv<ViewModelGrupoCid0>().Ler(recurso);
        }
 
        public IEnumerable<ViewModelCategoriaCid0> ListarCateogiasCid0()
        {
            var recurso = Properties.Resources.CID_O_CATEGORIAS;
            return new LerCsv<ViewModelCategoriaCid0>().Ler(recurso);
        }
    }
}

Reparem o método “ListarCapitulos” ele nos retorna uma lista (IEnumerable) de objetos da classe ViewModelCapitulo.cs, para isso foi utilizado um arquivo CSV no resources chamado “CID_10_CAPITULOS”, e o valor deste arquivo (uma string) foi passada para a nossa classe genérica “LerCsv” através do método “Ler(recurso)” o mesmo se segue para os demais arquivos CSV e métodos.

Mostrando os dados do arquivo CSV no ASP .Net MVC

Agora que temos nossa estrutura toda montada, baste chamar os métodos de nossa classe repositório dentro das actions de nossos controller em ASP .Net MVC, como mostra o código do controller Home;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using System.Linq;
using System.Web.Mvc;
using Repositorio;
 
namespace UI.Web.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var repositorio = new Repositorio.Repositorio();
            var capitulos = repositorio.ListarCapitulos().ToList();
            return View(capitulos);
        }
 
        public ActionResult Capitulo(string id)
        {
            var repositorio = new Repositorio.Repositorio();
            var capitulo = repositorio.ListarCapitulos().FirstOrDefault(x => x.NumeroCapitulo == id);
            ViewBag.capitulo = capitulo;
            var grupos = repositorio.ListarGrupos().Where(x =>
                x.CategoriaInicialCodigo >= capitulo.CategoriaInicialCodigo &&
                x.CategoriaFinalCodigo <= capitulo.CategoriaFinalCodigo
                ).ToList();
            return View(grupos);
        }
 
        public ActionResult Grupo(string categoriaInicial, string categoriaFinal)
        {
            var repositorio = new Repositorio.Repositorio();
            var grupo = repositorio.ListarGrupos().FirstOrDefault(x => x.CategoriaInicial == categoriaInicial && x.CategoriaFinal == categoriaFinal);
            ViewBag.grupo = grupo;
            var capitulo = repositorio.ListarCapitulos().FirstOrDefault(x =>
                    grupo.CategoriaInicialCodigo >= x.CategoriaInicialCodigo &&
                    grupo.CategoriaFinalCodigo <= x.CategoriaFinalCodigo
                    );
 
            ViewBag.capitulo = capitulo;
 
            var categorias = repositorio.ListarCategorias().Where(x =>
                x.CodigoDaCategoriaCodigo >= grupo.CategoriaInicialCodigo &&
                x.CodigoDaCategoriaCodigo <= grupo.CategoriaFinalCodigo
                ).ToList();
 
            return View(categorias);
        }
 
        public ActionResult Categoria(string codigoDaCategoria)
        {
            var repositorio = new Repositorio.Repositorio();
 
            var categoria = repositorio.ListarCategorias().FirstOrDefault(x => x.CodigoDaCategoria == codigoDaCategoria);
 
            var subCategorias = repositorio.ListarSubCategorias().Where(x =>
               x.CodigoDaCategoria == codigoDaCategoria).ToList();
 
            categoria.ListaDeSubCategorias = subCategorias;
 
            var grupo = repositorio.ListarGrupos().FirstOrDefault(x => categoria.CodigoDaCategoriaCodigo >= x.CategoriaInicialCodigo && categoria.CodigoDaCategoriaCodigo <= x.CategoriaFinalCodigo);
            ViewBag.grupo = grupo;
 
            var capitulo = repositorio.ListarCapitulos().FirstOrDefault(x =>
                    grupo.CategoriaInicialCodigo >= x.CategoriaInicialCodigo &&
                    grupo.CategoriaFinalCodigo <= x.CategoriaFinalCodigo
                    );
 
            ViewBag.capitulo = capitulo;
 
            return View(categoria);
        }
 
        public ActionResult Detalhe(string codigo)
        {
            var repositorio = new Repositorio.Repositorio();
            var subcategoria = repositorio.ListarSubCategorias().FirstOrDefault(x => x.CodigoDaSubCategoria == codigo);
 
            return View(subcategoria);
        }
 
        public ActionResult Busca (string busca)
        {
 
            busca = Ferramentas.SubstituiAcentos(busca);
            var repositorio = new Repositorio.Repositorio();
            var subCategorias = repositorio.ListarSubCategorias().Where(x => x.DescricaoSemAcentos.ToLower().Contains(busca.ToLower())).ToList();
            ViewBag.busca = busca;
            return View(subCategorias);
        }
    }
}

Reparem a action Index e veja como foi simples listar todos os dados do capítulos de doenças utilizando a estrutura montada, bastou chamar o método “ListarCapitulos” da classe repositório e passar os valores para a View “Index.cshtml”, veja seu código.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@model List<Repositorio.ViewModelCapitulo>
@{
    ViewBag.Title = "W7SAUDE";
}
<h2>Busca CID</h2>
<div class="span12" style="margin-left: 0">
    @Html.Partial("_formBusca")
    <div class="span9">
        @foreach (var capitulo in Model)
        {
            <div class="span6" style="margin-left: 0; height: 110px; padding: 5px;">
                @{
            var titulo = capitulo.Descricao.Split('-');
            titulo[0] = titulo[0].Trim();
            titulo[1] = titulo[1].Trim();
                }
                <h2><a href="~/Home/Capitulo/@capitulo.NumeroCapitulo">@titulo[0]</a></h2>
                <p>@titulo[1]</p>
            </div>
        }
    </div>
</div>

Veja como manipular arquivos CSV se torna fácil utilizando o CsvHelper e uma classe genérica para fazer o trabalho de mapeamento para objetos.

Aproveite e veja o exemplo deste código rodando em http://cid.apphb.com/, ou baixe este código completo no meu GitHub no repositório CidDataSus.

Aproveite para saber mais acessando esta lista de artigos sobre ASP.Net MVC. Acessa! Vai te ajudar muito e aproveita que é de graça!

[Post originalmente publicado em http://cleytonferrari.com/arquivos-csv-com-c-no-asp-net-mvc-4/ em 19/02/2013]

Comentarios

comentarios