.NET Core C# Utilizando o Micro ORM ServiceStack.OrmLite em uma aplicação ASP.NET Core

Aprenda na prática como fazer uso do micro framework ORM ServiceStack.OrmLite em uma aplicação ASP.NET Core.

Wladimilson M. Nascimento 22 de novembro de 2017

O ServiceStack é um framework leve desenvolvido “sob” o ASP.NET que permite criar web services e aplicações web. Composto por vários serviços, podemos dizer que o ServiceStack é uma alternativa mais leve dos frameworks WCF, Web API, ASP.NET MVC.

Entre seus recursos podemos destacar:

  • Web Services Framework: REST, SOAP e Message Queuing;
  • JSON Serializer: Serialização automática de JSON, CSV e JSV;
  • ORMLite: um micro framework ORM;
  • Injeção de dependência;
  • Logging API;
  • Autenticação e autorização;
  • C# Redis Client.

Geralmente os seus recursos são utilizados em conjunto, mas neste artigo iremos abordar apenas o ORMLite, como uma forma de introdução aos recursos do ServiceStack.

C# (C Sharp) - Introdução ao ASP.NET Core
Curso de C# (C Sharp) - Introdução ao ASP.NET Core
CONHEÇA O CURSO

ServiceStack.OrmLite

O objetivo do OrmLite é fornecer um wrapper de configuração simples, DRY e agnóstico; que mantém uma alta afinidade com o SQL, expondo API intuitivas que geram expressões SQL para classes POCOs desconectadas.

Esta abordagem facilita o acesso aos dados, tornando óbvio qual é o SQL gerado, e quando é executado; enquanto mitiga qualquer comportamento inesperado, comuns em ORMs mais pesadas.

O OrmLite foi criado visando os seguintes objetivos:

  • Fornecer um conjunto de métodos de extensão leves para as interfaces System.Data.*;
  • Mapear classes POCO para tabelas do banco de dados, de forma clara, livre de convenções e necessidade de atributos;
  • Criação e exclusão de tabelas utilizando apenas definições de classes POCO;
  • Simplicidade: API de acesso simples;
  • Alto desempenho: com suporte a índices, text, blobs, etc;
  • Entre os mais rápidos micro ORM para .NET
  • Poder e flexibilidade: com acesso a interface IDbCommand e expressões SQL;
  • Multiplataforma: suporta vários bancos de dados (Atualmente: Sql Server, Sqlite, MySql, PostgreSQL, Firebird), tanto no .NET Standard quanto no .NET Core;

No OrmLite uma classe é igual a uma tabela. Não há nenhum comportamento escondido, a query criada pode até retornar resultados diferentes da classe POCO utilizada para criá-la, mas apenas se isso for a opção do desenvolvedor. Por exemplo, quando se quer listar apenas alguns campos da tabela.

Por padrão, tipos complexos (não escalares) são tratados como text ou blob. Mas a API também suporta relacionamentos, podemos persistir dados relacionados de forma simples.

Para exemplificar o seu uso, vamos ver um exemplo dele em uma aplicação ASP.NET Core.

Criando a aplicação

No terminal digite o código abaixo para criar uma aplicação chamada AspNetCoreOrmLite:

dotnet new mvc -n AspNetCoreDapper

Agora, adicione o pacote do OrmLite:

dotnet add package ServiceStack.OrmLite.Sqlite.Core

Acima estou usando o pacote do banco de dados que irei utilizar neste artigo. Você pode ver aqui as demais versões disponíveis.

Não se esqueça de aplicar o restore no projeto:

dotnet restore

Com isso já podemos começar a nossa configuração OrmLite, iniciando pela criação da entidade/classe POCO.

Criando a classe POCO

Para este exemplo será utilizado a classe abaixo:

using System;

namespace AspNetCoreOrmLite.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Quantity { get; set; }
        public double Price { get; set; }
    }
}

Configurando o acesso ao banco de dados

O OrmLite não define uma classe de configuração, o acesso ao banco de dados pode ser obtido com um objeto da classe OrmLiteConnectionFactory:

var dbFactory = new OrmLiteConnectionFactory(
    connectionString,  
    SqliteDialect.Provider);

Caso queria utilizar IOC, ela pode ser registrada como singleton:

container.Register<IDbConnectionFactory>(c => 
    new OrmLiteConnectionFactory(connectionString, SqliteDialect.Provider));

Para o nosso exemplo, irei configurar o acesso com base em repositórios:

using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using ServiceStack.OrmLite;

namespace AspNetCoreOrmLite.Repositories
{
    public abstract class AbstractRepository<T>
    {
        private string _connectionString;
        private OrmLiteConnectionFactory _dbFactory;
        protected OrmLiteConnectionFactory DbFactory => _dbFactory;
        public AbstractRepository(IConfiguration configuration){
            _connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");

            _dbFactory = new OrmLiteConnectionFactory(_connectionString, SqliteDialect.Provider);
        }
        public abstract void Add(T item);
        public abstract void Remove(int id);
        public abstract void Update(T item);
        public abstract T FindByID(int id);
        public abstract IEnumerable<T> FindAll();
    }
}

Note que no método construtor um objeto de DbFactory foi criado:

_dbFactory = new OrmLiteConnectionFactory(_connectionString, SqliteDialect.Provider);

Por mais que seja aceito outro, o provider definido nesta classe deve ser do banco do pacote adicionado na aplicação. Caso seja referenciado outro banco, um erro será apresentado.

Agora para finalizar a configuração, vamos definir o repositório abaixo:

namespace AspNetCoreOrmLite.Repositories
{
    public class ProductRepository: AbstractRepository
    {
        public ProductRepository(IConfiguration configuration): base(configuration) { }

        public override void Add(Product item)
        {
            using (var db = DbFactory.Open())
            {
                if (db.CreateTableIfNotExists())
                {
                    db.Insert(item);
                }
            }
        }
        public override void Remove(int id)
        {
            using (var db = DbFactory.Open())
            {
                db.Delete(p => p.Id == id);
            }
        }
        public override void Update(Product item)
        {
            using (var db = DbFactory.Open())
            {
                db.Update(item);
            }
        }
        public override Product FindByID(int id)
        { 
            using (var db = DbFactory.Open())
            {
                return db.SingleById(id);
            }
        }
        public override IEnumerable FindAll()
        { 
                if (db.CreateTableIfNotExists())
                {
                    return db.Select();
                }

                return db.Select();
        }
    }
}

Note que o OrmLite gera as queries SQL com base na classe POCO informadas nos seus métodos genéricos:

using (var db = DbFactory.Open())
{
    db.Delete(p => p.Id == id);
}

Ou de acordo com o parâmetro do método:

using (var db = DbFactory.Open())
{
    db.Insert(item);
}

Quando há retorno de dados:

using (var db = DbFactory.Open())
{
    return db.SingleById(id);
}

Eles podem ser atribuídos a um objeto da classe POCO como acima, ou a outro objeto caso seja filtrado:

var q = db.From()
          .Where(x => x.Quantity  new { x.Id, x.Name });

Dictionary results = db.Dictionary(q);

Acima também é possível reparar que o filtro pode ser criado com LINQ e a query resultante passada para o OrmLite.

Ou isso pode ser definido como uma query SQL:

var tracks = db.Select<Track>("SELECT * FROM track WHERE Artist = @artist AND Album = @album", new { artist = "Nirvana", album = "Heart Shaped Box" });

Há muitas outras opções de métodos e criação de queries, você pode ver no repositório do framework.

Usando o OrmLite

Para exemplificar o uso do OrmLite, crie o controller abaixo:

using System.Linq;
using AspNetCoreOrmLite.Models;
using AspNetCoreOrmLite.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace AspNetCoreOrmLite.Controllers
{
    public class ProductController : Controller
    {
        private readonly ProductRepository productRepository;

        public ProductController(IConfiguration configuration){
            productRepository = new ProductRepository(configuration);
        }

        // GET: Products
        public ActionResult Index()
        {
            return View(productRepository.FindAll().ToList());
        }

        // GET: Products/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return StatusCode(StatusCodes.Status404NotFound);
            }
            Product product = productRepository.FindByID(id.Value);
            if (product == null)
            {
                return StatusCode(StatusCodes.Status404NotFound);
            }
            return View(product);
        }

        // GET: Products/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Products/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind("Id,Name,Quantity,Price")] Product product)
        {
            if (ModelState.IsValid)
            {
                productRepository.Add(product);
                return RedirectToAction("Index");
            }

            return View(product);
        }

        // GET: Products/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return StatusCode(StatusCodes.Status400BadRequest);
            }
            Product product = productRepository.FindByID(id.Value);
            if (product == null)
            {
                return StatusCode(StatusCodes.Status404NotFound);
            }
            return View(product);
        }

        // POST: Products/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind("Id,Name,Quantity,Price")] Product product)
        {
            if (ModelState.IsValid)
            {
                productRepository.Update(product);
                return RedirectToAction("Index");
            }
            return View(product);
        }

        // GET: Products/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return StatusCode(StatusCodes.Status400BadRequest);
            }
            Product product = productRepository.FindByID(id.Value);
            if (product == null)
            {
                return StatusCode(StatusCodes.Status404NotFound);
            }
            return View(product);
        }

        // POST: Products/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            productRepository.Remove(id);
            return RedirectToAction("Index");
        }
    }
}

Também crie as views para as actions acima e então podemos ver o sistema funcionando:

Aplicação AspNet Core OrmLite

Você pode baixar o código desta aplicação clicando aqui.

C# (C Sharp) Intermediário
Curso de C# (C Sharp) Intermediário
CONHEÇA O CURSO
Deixe seu comentário

Conheça o autor desse artigo

  • Foto Autor Wladimilson M. Nascimento
    Wladimilson M. Nascimento

    Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.

    Posts desse Autor

Artigos relacionados