Working with Entity Framework, Multithreading and SQL Server in C#

Asked

Viewed 404 times

0

I’m using the Entity Framework and Multithreading on a project C# and I am going through problems of connection with the SQL Server.

Well, I wanted to improve the speed of some data searches for a grid because they are very time consuming selects. First I used multithreading in the queries and I saw that it didn’t really matter if I triggered the queries in different threads, because in the bank it ends up lining the queries as if I were in a normal loop. In order to solve this, I started instantiating several new scopes, repositories and connectionstring’s to each trhead I create, so that it selects as if they were different people accessing the database and so run all queries in parallel. It worked, in a way, but when I create many threads in some queries it of problems like The underlying provider failed on open and Referência de objeto não definida para uma instância de um objeto, however, it is not always in the same query, it is kind of random. Has anyone ever gone through such a problem or has a solution for it? I am also open to suggestions if there is a better way to do what I am doing. Below is my code:

public void BuscarDadosGridFamilia(string filialSelecionadaCodigo, string familiaSelecionada, string legendaSelecionada)
{
    _listaClasseGenericaFamiliaProduto.Clear();

    DateTime dataInicialPeriodoVendas = DateTime.Now.AddDays(-30);
    DateTime dataFinalPeriodoVendas = DateTime.Now;
    _totalVendas30Dias = 0;
    _totalEstoqueAtual = 0;
    _totalEstoquePrevisto = 0;
    _totalCapacidadeMin = 0;
    _totalCapacidadeMax = 0;
    _totalGiroObj = 0;
    decimal _totalGiroAtual = 0;
    decimal _totalGiroPrevisto = 0;
    decimal _totalGiroCapacidadeMax = 0;
    decimal _totalGiroObjetivo = 0;
    decimal _totalGiroIdeal = 0;
    int _quantidadeTasks = 0;
    int _totalLinhasGridFamilia = 0;
    string[] filialSplit = filialSelecionadaCodigo.Split('-');
    Filial filialSelecionada = _filialAplicacao.RepositorioFilial.ObterPorCodigo(_usuarioAtual.LocalEstoqueAtual.Filial.Empresa, filialSplit[0].Trim());

    var _familiaProduto = _familiaProdutoAplicacao.RepositorioFamiliaProduto.Todos()
                            .Where(d => d.FlagPercentualCapacidadeGondola == 1)
                            .Where(d => (d.Codigo.StartsWith(familiaSelecionada + ".") || d.Codigo == familiaSelecionada) && familiaSelecionada != "");

    if (legendaSelecionada != "Todos")
        _familiaProduto = _familiaProduto.Where(d => d.FamiliaMarcadaCapacidadeGondola.LegendaLinha.Legenda == legendaSelecionada.Split('-')[0].Trim());

    Task[] tasks = new Task[_familiaProduto.Count()];

    foreach (var familia in _familiaProduto)
    {
        ConexaoBanco.ConexaoMultiThread = "familia-" + familia.Codigo;
        string connectionString = ConexaoBanco.GetConexao();
        ConexaoBanco.ConexaoMultiThread = "familia-" + familia.Codigo;
        IRepositorioEscopo escopo = Havan.Infra.IoC.Container.Obter<IRepositorioEscopo>();

        ParametrosTask parametrosTask = new ParametrosTask();
        parametrosTask.familia = familia;
        parametrosTask.filialSelecionada = filialSelecionada;
        parametrosTask.legendaSelecionada = legendaSelecionada;
        parametrosTask.repositorioEscopo = escopo;

        Task _task = new Task(() => BuscarDadosGridFamiliaTask(parametrosTask), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
        _task.Start();
        tasks[_quantidadeTasks] = _task;

        _totalLinhasGridFamilia++;
        _quantidadeTasks++;
    }
    Task.WaitAll(tasks);

    try { _totalGiroAtual = Arredondar((_totalEstoqueAtual / _totalVendas30Dias) * 30); }
    catch (Exception) { Arredondar(_totalGiroAtual = 0); };
    try { _totalGiroPrevisto = Arredondar((_totalEstoquePrevisto / _totalVendas30Dias) * 30); }
    catch (Exception) { Arredondar(_totalGiroPrevisto = 0); };
    try { _totalGiroCapacidadeMax = Arredondar((_totalCapacidadeMax / _totalVendas30Dias) * 30); }
    catch (Exception) { Arredondar(_totalGiroCapacidadeMax = 0); };
    try { _totalGiroIdeal = Arredondar((_totalVendas30Dias / 30) * _totalGiroObjetivo); }
    catch (Exception) { Arredondar(_totalGiroIdeal = 0); };
    try { _totalGiroObjetivo = Arredondar(_totalGiroObj / _familiaProduto.Count()); }
    catch (Exception) { Arredondar(_totalGiroObjetivo = 0); };

    ClasseGenericaFamiliaProduto _classeGenericaFamiliaProdutoTotal = new ClasseGenericaFamiliaProduto();

    _classeGenericaFamiliaProdutoTotal.Filial = "TOTAL:";
    _classeGenericaFamiliaProdutoTotal.Vendas30Dias = _totalVendas30Dias;
    _classeGenericaFamiliaProdutoTotal.EstoqueAtual = _totalEstoqueAtual;
    _classeGenericaFamiliaProdutoTotal.EstoquePrevisto = _totalEstoquePrevisto;
    _classeGenericaFamiliaProdutoTotal.CapacidadeMin = _totalCapacidadeMin;
    _classeGenericaFamiliaProdutoTotal.CapacidadeMax = _totalCapacidadeMax;
    _classeGenericaFamiliaProdutoTotal.GiroAtual = _totalGiroAtual;
    _classeGenericaFamiliaProdutoTotal.GiroPrevisto = _totalGiroPrevisto;
    _classeGenericaFamiliaProdutoTotal.GiroCapacidadeMax = _totalGiroCapacidadeMax;
    _classeGenericaFamiliaProdutoTotal.GiroObjetivo = _totalGiroObjetivo;
    _classeGenericaFamiliaProdutoTotal.GiroIdeal = _totalGiroIdeal;

    if (_listaClasseGenericaFamiliaProduto.Count > 0)
        _listaClasseGenericaFamiliaProduto.Add(_classeGenericaFamiliaProdutoTotal);

    gridFamilia.DataSource = _listaClasseGenericaFamiliaProduto;
}

And now the method that is called by Tasks which contain consultations with the bank by LINQ

public void BuscarDadosGridFamiliaTask(ParametrosTask _parametrosTask)
{
    decimal _estoqueSeparadoFamiliaLoja = 0;
    decimal _estoqueTransitoFamiliaLoja = 0;
    decimal _estoqueEntrarFamiliaLoja = 0;
    decimal _estoqueLogicoFamiliaLoja = 0;
    decimal _estoquePrevistoFamiliaLoja = 0;
    DateTime dataInicialPeriodoVendas = DateTime.Now.AddDays(-30);
    DateTime dataFinalPeriodoVendas = DateTime.Now;

    _familiaProdutoAplicacao = new FamiliaProdutoAplicacao(_parametrosTask.repositorioEscopo);
    _filialAplicacao = new FilialAplicacao(_parametrosTask.repositorioEscopo);
    _dadosFamiliaFilialAplicacao = new DadosFamiliaFilialAplicacao(_parametrosTask.repositorioEscopo);
    _giroObjetivoFamiliaProdutoAplicacao = new GiroObjetivoFamiliaProdutoAplicacao(_parametrosTask.repositorioEscopo);
    _saldoEstoqueProdutoAplicacao = new SaldoEstoqueProdutoAplicacao(_parametrosTask.repositorioEscopo);
    _resumoVendasAplicacao = new ResumoVendasAplicacao(_parametrosTask.repositorioEscopo);
    _legendaLinhaAplicacao = new LegendaLinhaAplicacao(_parametrosTask.repositorioEscopo);
    _wrkGenerico01Aplicacao = new WrkGenerico01Aplicacao(_parametrosTask.repositorioEscopo);
    _parametroClasseABCFamiliaAplicacao = new ParametroClasseABCFamiliaAplicacao(_parametrosTask.repositorioEscopo);
    _usuarioAtualAplicacao = new UsuarioAplicacao(_parametrosTask.repositorioEscopo);

    Int64 _idPrimeiroNivelFamilia = _familiaProdutoAplicacao.RepositorioFamiliaProduto.ObterPorCodigo(_parametrosTask.familia.Codigo.Split('.')[0]).FirstOrDefault().Id;

    ParametroClasseABCFamilia _parametroClasseABCFamilia = _parametroClasseABCFamiliaAplicacao.ParametroClasseABCFamiliaPorFamiliaClasse(_parametrosTask.filialSelecionada.Id, _idPrimeiroNivelFamilia).FirstOrDefault();

    ClasseGenericaFamiliaProduto _classeGenericaFamiliaProduto = new ClasseGenericaFamiliaProduto();

    _saldoEstoqueProdutoAplicacao.ObterSaldoEstoqueProdutosFamilia(_usuarioAtual.TipoEstoque.Id, _parametrosTask.filialSelecionada, _parametrosTask.familia.Codigo, true, out _estoqueLogicoFamiliaLoja, false, out _estoqueSeparadoFamiliaLoja, false, out _estoqueTransitoFamiliaLoja, false, out _estoqueEntrarFamiliaLoja, true, out _estoquePrevistoFamiliaLoja);

    _classeGenericaFamiliaProduto.Filial = _parametrosTask.filialSelecionada.Codigo + " - " + _parametrosTask.filialSelecionada.Apelido;

    try { _classeGenericaFamiliaProduto.LinhaProduto = _parametrosTask.familia.FamiliaMarcadaCapacidadeGondola.LegendaLinha.Descricao; }
    catch (Exception) { _classeGenericaFamiliaProduto.LinhaProduto = ""; };

    _classeGenericaFamiliaProduto.Familia = _parametrosTask.familia.Codigo;

    _classeGenericaFamiliaProduto.DescricaoFamilia = _parametrosTask.familia.Nome;

    try { _classeGenericaFamiliaProduto.Vendas30Dias = Arredondar((decimal)_resumoVendasAplicacao.RepositorioResumoVendas.ObterQuantidadeVendaPorFamiliaFilial(_parametrosTask.filialSelecionada.Id, _parametrosTask.familia.Id, dataInicialPeriodoVendas, dataFinalPeriodoVendas)); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.Vendas30Dias = 0); };

    _totalVendas30Dias += Arredondar(_classeGenericaFamiliaProduto.Vendas30Dias);

    _classeGenericaFamiliaProduto.EstoqueAtual = Arredondar(_estoqueLogicoFamiliaLoja);

    _totalEstoqueAtual += Arredondar(_classeGenericaFamiliaProduto.EstoqueAtual);

    _classeGenericaFamiliaProduto.EstoquePrevisto = Arredondar(_estoquePrevistoFamiliaLoja);

    _totalEstoquePrevisto += Arredondar(_classeGenericaFamiliaProduto.EstoquePrevisto);

    _classeGenericaFamiliaProduto.CapacidadeMin = Arredondar(_dadosFamiliaFilialAplicacao.RepositorioDadosFamiliaFilial.ObterLimiteMaximoFamiliaEstoque(_familiaProdutoAplicacao.RepositorioFamiliaProduto, _usuarioAtual.LocalEstoqueAtual.Filial.Empresa.Id, _usuarioAtual.TipoEstoque.Id, _parametrosTask.familia.FamiliaMarcadaCapacidadeGondola.Codigo, _parametrosTask.familia.Codigo, _parametrosTask.filialSelecionada.Id).Select(d => d.Quantidade).FirstOrDefault());

    _totalCapacidadeMin += Arredondar(_classeGenericaFamiliaProduto.CapacidadeMin);

    try { _classeGenericaFamiliaProduto.CapacidadeMax = Arredondar(_familiaProdutoAplicacao.CalculoEstoqueMaximoFamilia(_classeGenericaFamiliaProduto.CapacidadeMin, _classeGenericaFamiliaProduto.Vendas30Dias, _parametrosTask.filialSelecionada.TempoReposicaoEmDias, _parametroClasseABCFamilia.FatorClasseFamilia, _parametroClasseABCFamilia.PercentualCabideFamilia)); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.CapacidadeMax = 0); };

    _totalCapacidadeMax += Arredondar(_classeGenericaFamiliaProduto.CapacidadeMax);

    try { _classeGenericaFamiliaProduto.GiroAtual = Arredondar((_classeGenericaFamiliaProduto.EstoqueAtual / _classeGenericaFamiliaProduto.Vendas30Dias) * 30); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.GiroAtual = 0); };

    try { _classeGenericaFamiliaProduto.GiroPrevisto = Arredondar((_classeGenericaFamiliaProduto.EstoquePrevisto / _classeGenericaFamiliaProduto.Vendas30Dias) * 30); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.GiroPrevisto = 0); };

    try { _classeGenericaFamiliaProduto.GiroCapacidadeMax = Arredondar((_classeGenericaFamiliaProduto.CapacidadeMax / _classeGenericaFamiliaProduto.Vendas30Dias) * 30); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.GiroCapacidadeMax = 0); };

    _classeGenericaFamiliaProduto.GiroObjetivo = Arredondar(_giroObjetivoFamiliaProdutoAplicacao.BuscaGiroObjetivoFamiliaMaisProxima(_parametrosTask.familia.Codigo, _parametrosTask.filialSelecionada.Id));

    _totalGiroObj += Arredondar(_classeGenericaFamiliaProduto.GiroObjetivo);

    try { _classeGenericaFamiliaProduto.GiroIdeal = Arredondar((_classeGenericaFamiliaProduto.Vendas30Dias / 30) * _classeGenericaFamiliaProduto.GiroObjetivo); }
    catch (Exception) { Arredondar(_classeGenericaFamiliaProduto.GiroIdeal = 0); };

    _classeGenericaFamiliaProduto.NecessidadeGiro = (Arredondar(_classeGenericaFamiliaProduto.GiroIdeal - _classeGenericaFamiliaProduto.EstoquePrevisto) < 0 ? 0 : Arredondar(_classeGenericaFamiliaProduto.GiroIdeal - _classeGenericaFamiliaProduto.EstoquePrevisto));

    _classeGenericaFamiliaProduto.NecessidadeCapacidade = (Arredondar(_classeGenericaFamiliaProduto.CapacidadeMax - _classeGenericaFamiliaProduto.EstoquePrevisto) < 0 ? 0 : Arredondar(_classeGenericaFamiliaProduto.CapacidadeMax - _classeGenericaFamiliaProduto.EstoquePrevisto));

    try { _classeGenericaFamiliaProduto.SGiro = Formatar((_classeGenericaFamiliaProduto.GiroPrevisto / _classeGenericaFamiliaProduto.GiroObjetivo) * 100); }
    catch (Exception) { Formatar(_classeGenericaFamiliaProduto.SGiro = 0); };

    _listaClasseGenericaFamiliaProduto.Add(_classeGenericaFamiliaProduto);
}
  • Have you been able to identify which appointments are lengthy? My bet: a change in just one or two lines can improve several times the response time of this part of your system. I’d start by analyzing this one: _familiaProdutoAplicacao.RepositorioFamiliaProduto.Todos().Where(... - see that you first return more records than you need and then make a selection in memory. Although its design does not seem ideal, most of the time the performance problem is localized. Identify which of the queries is taking 80% of the time and come back here for us to take a look.

  • Okay, thanks for the tip! makes sense! I will implement. However my doubt is another.

  • 1

    Hmm your question is with threads, right? I ended up focusing on what seemed to be the real problem and forgot about threads. Your design opens up a lot of holes for problems when working with threads. Since you’re open to suggestions, my suggestion is: don’t use threads. They are not designed to solve performance problems, let alone performance problems in queries to Dbms (they themselves open threads when necessary). It’s best to go back to the previous code without threads, identify and solve performance issues.

  • Well, with your tips and a lot of reading I ended up abandoning this idea of threads doing several parallel accesses to the comic book and chose to change the structure a little and improve my queries. Thank you.

1 answer

1

This here:

_familiaProdutoAplicacao.RepositorioFamiliaProduto.Todos()

It’s terrible in every way for performance, since you make a TABLE SCAN for each consultation. Here I explain why using repository with Entity Framework is not worth it.

The second thing that caught my attention is how many queries you make to synthesize the information. I don’t know how your code is under that, but it’s very likely that the same information will be brought back several times. The fuller the context (which in your case is shared), the slower it gets. Here in this answer I teach how to optimize the context for large portions of data, but I think the gain will be small by the way your application was built.

I suggest rethinking this entire architecture. I can help if you give more details of your LINQ queries.

  • Thanks gypsy! thanks for the tip and the documentation. I will implement. Now as I told @Caffé, My doubt is another. As long as my queries are and using repository is not worth it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.