Compartilhar via


DbContextPool no Asp.Net Core 2.0

http://blog.ralms.net/wp-content/uploads/2017/09/netcore.png
**
*DbContextPool *** esse novo recurso foi disponibilizado na versão do Asp.Net Core 2.0.

Esta API oferece suporte à infraestrutura Entity Framework Core.

A ideia principal é permitir a reutilização de instâncias do DbContext em um pool, que para muitos casos, iremos ter um aumento significativo de desempenho ao criar uma nova instância. Sem ter a necessidade de criar novamente uma nova instancia do objeto em memória. 

Vamos criar um pequeno projeto pra testar essa nova API e entender melhor.

https://blog.ralms.net/imagens/techwiki/01.PNG
https://blog.ralms.net/imagens/techwiki/02.PNG

Vamos adicionar o pacote do ***Microsoft.EntityFrameworkCore.SqlServer 2.0.1  
***para ser usado em nosso exemplo.

https://blog.ralms.net/imagens/techwiki/03.PNG

https://blog.ralms.net/imagens/techwiki/04.PNG

Substitua o conteúdo do seu Program.cs pelo conteúdo abaixo:

using System;
using System.Linq;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
 
namespace ExemploDbContextPooling
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext(
                c => c.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=TechWiki;Integrated Security=True;"));
        }
    }
 
    public class Program
    {
        private const int Threads = 50;
        private const int Segundos = 8;
 
        private static long _requisicoesProcessadas;
 
        private static void Main()
        {
            var serviceCollection = new ServiceCollection();
            new Startup().ConfigureServices(serviceCollection);
            var serviceProvider = serviceCollection.BuildServiceProvider();
 
            InicializarBanco(serviceProvider);
 
            var stopWatch = new Stopwatch();
 
            Resultados(TimeSpan.FromSeconds(Segundos), stopWatch);
 
            Task.WhenAll(
                Enumerable
                    .Range(0, Threads)
                    .Select(_ => SimularRequisicaosAsync(serviceProvider, stopWatch)));
 
            Console.ReadKey();
        }
 
        private static void InicializarBanco(IServiceProvider serviceProvider)
        {
            using (var serviceScope = serviceProvider.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetService();
 
                if (context.Database.EnsureCreated())
                {
                    context.Blogs.Add(new Blog { Author = "Rafael Almeida", Url = "https://blog.ralms.net/" });
                    context.Blogs.Add(new Blog { Author = "Microsoft", Url = "https://blogs.msdn.microsoft.com/" });
                    context.Blogs.Add(new Blog { Author = "Asp Net Core", Url = "https://microsoft.com/net" });
                    context.SaveChanges();
                }
            }
        }
 
        private static async Task SimularRequisicaosAsync(IServiceProvider serviceProvider, Stopwatch stopSatch)
        {
            while (stopSatch.IsRunning)
            {
                using (var serviceScope = serviceProvider.CreateScope())
                {
                    await new BlogController(serviceScope.ServiceProvider.GetService()).ActionAsync();
                }
 
                Interlocked.Increment(ref _requisicoesProcessadas);
            }
        }
 
        private static async void Resultados(TimeSpan duracao, Stopwatch stopWatch)
        {
            var ultimaInstancia = 0L;
            var ultimaRequisicao = 0L;
            var ultimoTempo = TimeSpan.Zero;
 
            stopWatch.Start();
 
            while (stopWatch.Elapsed < duracao)
            {
                await Task.Delay(TimeSpan.FromSeconds(1));
 
                var contadorInstancia = BlogContext.ContadorInstancia;
                var contadorRequisicao = _requisicoesProcessadas;
                var tempoCorrido = stopWatch.Elapsed;
                var tempoAtual = tempoCorrido - ultimoTempo;
                var requisicoes = contadorRequisicao - ultimaRequisicao;
 
                Console.WriteLine(
                    $"{DateTime.Now:HH:mm:ss.fff} - "
                    + $"Criação do DbContext/Segundo: {contadorInstancia - ultimaInstancia} | "
                    + $"Requisição/Segundo: {Math.Round(requisicoes / tempoAtual.TotalSeconds)}");
 
                ultimaInstancia = contadorInstancia;
                ultimaRequisicao = contadorRequisicao;
                ultimoTempo = tempoCorrido;
            }
 
            Console.WriteLine();
            Console.WriteLine($"Total de Instancias Criadas......: {BlogContext.ContadorInstancia}");
            Console.WriteLine(
                $"Média de Requisições por Segundos:     {Math.Round(_requisicoesProcessadas / stopWatch.Elapsed.TotalSeconds)}");
 
            stopWatch.Stop();
        }
 
    }
 
    public class Blog
    {
        public int Id { get; set; }
        public string Author { get; set; }
        public string Url { get; set; }
    }
 
    public class BlogContext : DbContext
    {
        public static long ContadorInstancia;
 
        public BlogContext(DbContextOptions options)
            : base(options)
        {
            Interlocked.Increment(ref ContadorInstancia);
        }
 
        public DbSet Blogs { get; set; }
    }
 
    public class BlogController
    {
        private readonly BlogContext _contextBlog;
 
        public BlogController(BlogContext context)
        {
            _contextBlog = context;
        }
 
        public async Task ActionAsync()
        {
            await _contextBlog.Blogs.FirstAsync();
        }
    }
}

Ao executar nosso projeto teremos algo parecido com este, é óbvio que a cada execução poderemos ter valores diferentes, mais o foco aqui será mostrar a diferença entre usar o AddDbContext e o AddDbContextPool.

Em meu teste inicial eu obtive o seguinte resultado.

https://blog.ralms.net/imagens/techwiki/05.PNG

Se observarmos direito, ele criou milhares de instancias, isso pode afetar a performance de nossa aplicação.
agora iremos trocar apenas a API AddDbContext Para AddDbContextPool. E vamos rodar novamente a aplicação.

Observe agora o número significativo de instancias que foram reduzidas para apenas 50.

https://blog.ralms.net/imagens/techwiki/06.PNG

Pessoal por hoje é só, nosso intuito aqui foi mostrar como um todo .Net Core está evoluindo.
Cada dia que passa a ferramenta fica fantástica.