ServiceStack

Criando um Web Service com o ServiceStack – Parte 2

Dando continuidade ao estudo da biblioteca ServiceStack, neste artigo modificaremos o web service criado na primeira parte.

Os web services criados com essa biblioteca geralmente fazem uso dos outros recursos. Um deles é o ServiceStack.OrmLite, que é um micro framework ORM, que também já vimos anteriormente nesse artigo. Assim, vamos adicioná-lo na aplicação criada na primeira parte desse artigo.

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

Colocando a mão na massa

Inicialmente iremos definir a classe responsável pela resposta do web service. Desta forma, dentro do projeto, na pasta ServiceModel, será criado a classe chamada TodoRespose, contendo o código abaixo:

using ServiceStack;

namespace ServiceStackExample.ServiceModel {
    public class TodoResponse
    {
        public object Result { get; set; }

        public ResponseStatus ResponseStatus { get; set; }
    }
}

A criação desta classe não é algo obrigatório, como o artigo anterior pode ter implicitado, mas gosto de separar a classe DTO do seu response.

Ainda dentro da pasta ServiceModel adicione a classe DTO Todo, conforme o código abaixo:

using ServiceStack;

namespace ServiceStackExample.ServiceModel {

    [Route("/todo")]
    [Route("/todo/{Id}")]
    public class Todo
    {
        public int Id { get; set; }
        public string Text { get; set; }
        public bool Done { get; set; }
    }
}
C# (C Sharp) Básico
Curso de C# (C Sharp) Básico
CONHEÇA O CURSO

Definindo o ServiceStack.OrmLite

Antes de criarmos um service para a classe DTO Todo, vamos configurar o acesso ao banco de dados.

A primeira coisa que deve ser feita é adicionar a referência da biblioteca no projeto:

dotnet add package ServiceStack.OrmLite.Sqlite.Core

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

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

dotnet restore

A classe POCO utilizada será a DTO Todo, assim já iremos definir o repositório dela.

Inicialmente criaremos uma classe abstrata, onde o acesso pelo OrmLite será definido:

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

namespace ServiceStackExample.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();
    }
}

E a partir dele, será criado um repositório para a classe Todo:

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

namespace ServiceStackExample.Repositories
{
    public class TodoRepository: AbstractRepository<Todo>
    {
        public TodoRepository(IConfiguration configuration): base(configuration) { }

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

                return db.Select<Todo>();
            }
        }
    }
}

Agora podemos definir o service.

Criando a classe Service

Agora na pasta ServiceInterface adicione uma classe chamada TodoService, contendo o código abaixo:

using Microsoft.Extensions.Configuration;
using ServiceStack;
using ServiceStackExample.Repositories;
using ServiceStackExample.ServiceModel;

namespace ServiceStackExample.ServiceInterface {
    public class TodoService: Service
    {
        private readonly TodoRepository todoRepository;

        public TodoService(IConfiguration configuration){
            todoRepository = new TodoRepository(configuration);
        }

        public object Get(Todo todo)
        {

            if(todo.Id != default(int))
                return todoRepository.FindByID(todo.Id);

            return todoRepository.FindAll();
        }

        public Todo Post(Todo todo){
            todoRepository.Add(todo);

            return todo;
        }

        public Todo Put(Todo todo){
            todoRepository.Update(todo);

            return todo;
        }

        public Todo Delete(Todo todo){
            todoRepository.Remove(todo.Id);

            return todo;
        }
    }
}

Diferente do artigo anterior, agora estamos definindo métodos para tratar todos os principais métodos do HTTP: GET, POST, PUT e DELETE.

Com isso definido, agora só é necessário criar a interface.

Interface gráfica

Como faltei em todas as aulas de front-end :P, nem vou explicar aqui os códigos deste detalhe, você pode baixar o projeto clicando aqui.

Após executar a aplicação, ela será exibida da seguinte forma:

Interface da aplicação

Com o código disponibilizado, você pode testá-la, para ver como ficou.

Um abraço!

C# (C Sharp) - ASP.NET MVC
Curso de C# (C Sharp) - ASP.NET MVC
CONHEÇA O CURSO

Criando um Web Service com o ServiceStack – Parte 1

No artigo passado conhecemos o ServiceStack e vimos um exemplo do ServiceStack.OrmLite. Como comentado no artigo anterior, o ServiceStack possui uma gama de recursos, criados para substituir os frameworks WCF, Web API, ASP.NET MVC.

Assim, continuando o seu estudo, hoje veremos como criar uma aplicação REST simples em ASP.NET Core.

Para este artigo a aplicação terá apenas rota que irá retornar o que o usuário indicar. No próximo iremos integrá-la ao ServiceStack.OrmLite.

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

Entendo o projeto do ServiceStack

Para o Visual Studio, o ServiceStack possui um template (pode ser baixado aqui) que facilita a criação do web service.

Ao instalar este template e criar uma aplicação ServiceStack ASP.NET Empty, será criada uma solução com 4 projetos:

  • Projeto: Projeto ASP.NET vazio. É o projeto “host”, onde deve ser adicionado os recursos web, como: views, arquivos js, css, imagens, fontes, etc;
  • Projeto.ServiceInterface: Projeto que contém os serviços e a camada de negócio da aplicação.
  • Projeto.ServiceModel: Projeto que contém as classes DTO da aplicação. Essas classes definem os “contratos” dos services.
  • Projeto.Test: Projeto que contém os testes de unidade da aplicação.

Como o projeto deste artigo será criado com o .NET Core em um ambiente Unix, ele não contém múltiplos projetos, apenas pastas para separar as camadas ServiceInterface, e ServiceModel.

Então vamos colocar a mão na massa!

Colocando a mão na massa

No o .NET Core instalado na máquina, no terminal crie uma aplicação ASP.NET Core vazia com o comando abaixo:

dotnet new web -n ServiceStackExample

Acesse a pasta do projeto criado e adicione nele a dependência do ServiceStack:

dotnet add package ServiceStack.Core

Pronto, o nosso projeto estará criado e com o ServiceStack referenciado.

Criando as classes DTO

Dentro do projeto, crie uma pasta chamada ServiceModel e dentro dela crie uma classe chamada CategoryRespose, contendo o código abaixo:

using ServiceStack;

namespace ServiceStackExample.ServiceModel {
    public class CategoryResponse
    {
        public string Result { get; set; }

        public ResponseStatus ResponseStatus { get; set; }
    }
}

Como o nome indica, esta classe será responsável pela resposta do web service. Ela só necessita de uma propriedade Result, mas para que exceções também sejam exibidas no navegador, é necessário adicionar propriedade ResponseStatus.

Agora dentro da mesma pasta adicione uma classe Category, contendo o código abaixo:

using ServiceStack;

namespace ServiceStackExample.ServiceModel {

    [Route("/categories")]
    [Route("/categories/{Name}")]
    public class Category
    {
        public string Name { get; set;}
    }
}

Esta classe irá tratar as solicitações do web service para as URLs definidas nela:

[Route("/categories")]
[Route("/categories/{Name}")]

O valor {Name} definido na última URL acima, será atribuído a propriedade Name da classe.

Criando a classe Service

Agora crie no projeto a pasta ServiceInterface, e a ela adicione uma classe chamada CategoryService, contendo o código abaixo:

using ServiceStack;
using ServiceStackExample.ServiceModel;

namespace ServiceStackExample.ServiceInterface {
    public class CategoryService: Service
    {
        public object Any(Category  request){
            return new CategoryResponse { Result = $"Categoria: {request.Name}" };
        }
    }
}

Como o nome indica, esta classe é a implementação do “serviço” das classes que definimos na pasta ServiceModel. Nela pode ser definidos métodos para tratar os métodos do HTTP: GET, POST, PUT, DELETE, etc; Esses métodos devem ter o mesmo nome dos métodos do HTTP.

Ou seja, para criar um método que será chamado quando houver uma solicitação GET, o método deve ser nomeado como Get. Caso queira tratar uma solicitação POST, o método deve ser nomeado como Post; e assim por diante.

Também pode ser definido o método Any que é para qualquer solicitação, que é o que foi feito na classe acima.

Iniciando o serviço

Para que o serviço seja iniciado e as solicitações que definimos com o ServiceStack sejam tratadas pela aplicação, temos que criar uma classe AppHost, contendo o conteúdo abaixo:

using Funq;
using ServiceStack;

namespace ServiceStackExample
{
   public class AppHost: AppHostBase {
       public AppHost(): base("Treinaweb web Services", typeof(ServiceInterface.CategoryService).Assembly){}

        public override void Configure(Container container)
        {    
        }
    }
}

A classe acima está apenas registrando o nosso serviço, mas no seu método configure pode ser configurado outros recursos, como o ServiceStatck.OrmLite, Cache, Redis, Autenticação, etc;

Por fim, esta classe precisa ser chamada no método Configure da classe Startup:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseServiceStack(new AppHost());
}

Pronto, o nosso serviço já está criado e configurado e podemos testá-lo.

Executando o serviço

No terminal execute a aplicação:

dotnet run

Aí no navegador acesse o serviço: http://localhost:5000/categories/Mouse:

Print do exemplo do Service Stack Funcionando.

Tudo certo. No próximo artigo trabalharemos mais a aplicação.

Até a próxima!

C# (C Sharp) - ASP.NET MVC
Curso de C# (C Sharp) - ASP.NET MVC
CONHEÇA O CURSO

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

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) Avançado
Curso de C# (C Sharp) Avançado
CONHEÇA O CURSO