GIT Corrompido

Posted: February 4, 2021 in Uncategorized
Tags: ,

git push

Problema:

warning: ignoring broken ref refs/heads/master
error: cannot lock ref 'refs/heads/master': unable to resolve reference 'refs/heads/master': reference broken

Tentei clonar o repositório em outro lugar para contornar o problema:

git clone <caminho>

Mas o resultado foi este:

You appear to have cloned an empty repository.

De fato, a nova pasta ficou vazia.

Análise:

Entrei no local físico do repositório e vi que existe este caminho lá: refs/heads/master. Abri o arquivo master e verifiquei que estava só com caracteres em branco.

Olhei o mesmo arquivo em outro repositório e, a julgar pelo seu conteúdo, percebi que ele deveria ter um identificador de algum commit.

Executei o comando a seguir para ver os identificadores dos últimos 5 commits. Felizmente, ainda funcionou o comando:

git log origin/master -5

Editei o arquivo master no servidor manualmente e colei o identificador do commit mais recente lá.

Não é que deu certo?!

O SQL Server (2012 em diante) tem um comportamento default que acarreta em saltos de numeração nos campos identity, pulando de 1000 em 1000.

Para mitigar isto:

A solução é o flag 272 na configuração:
1) SQL Server Configuration Manager
2) SQL Server Services
3) Botão direito na instância, propriedades
4) Aba Startup Parameters
5) Add “-T272”
6) Add “-t272”

Mais em:

https://www.codeproject.com/Tips/668042/SQL-Server-2012-Auto-Identity-Column-Value-Jump-Is

https://stackoverflow.com/questions/14162648/sql-server-2012-column-identity-increment-jumping-from-6-to-1000-on-7th-entry

https://pt.linkedin.com/pulse/sql-server-2012-e-posteriores-trace-flag-272-anderson-alves-de-souza

Problema:

O banco de dados legado não pode ser alterado e os nomes das colunas não correspondem ao padrão de nomenclatura de chaves estrangeiras (foreign key) do EF Core. Consequentemente, ocorrem erros de mapeamento em tempo de execução.

No exemplo abaixo, o problema é que o EF Core vai procurar uma coluna chamada PessoaCredorId, mas o nome da coluna (legado) é PessoaCredor.

Se a coluna fosse de uma propriedade de tipo primitivo, bastaria usar o método HasColumnName. Mas isso está disponível apenas quando se usa o método Property e não para HasOne.

Solução:

Todos os tutoriais que já vi orientam a criar a propriedade PessoaCredorId na classe, mas, no meu entendimento, isso viola a separação de conceitos e não faço esta declaração. Por que a classe deveria ter duas propriedades: PessoaCredor e PessoaCredorId? Isso não faz sentido para uma classe de modelo.

Mas a solução passa secretamente por isso. Mesmo sem declarar esta propriedade extra, o EF Core usa magia negra e cria esta propriedade em tempo de execução. Ela está lá, você só não sabe disso. Ótimo, pois assim podemos usar HasColumnName nela e resolver o problema. Mas teremos que usar o nome hardcoded ao invés de usar as queridas expressões lambda.

Exemplo:

Tabelas:

CREATE TABLE [dbo].[Pessoa](
[AutoId] [int] IDENTITY(1,1) NOT NULL)

CREATE TABLE [dbo].[Credor](
[AutoId] [int] IDENTITY(1,1) NOT NULL,
[PessoaCredor] [int] NOT NULL)

Modelo

public class Pessoa
{
    public virtual int Id { get; set; }
}
public class Cliente
{
    public virtual int Id { get; set; }
    public virtual Pessoa PessoaCliente { get; set; }
}

Mapeamento

public class ClienteMapping : TelosEntityTypeConfiguration
{
    public override void Configure(EntityTypeBuilder builder)
    {
        builder.HasKey(entity => entity.Id);
        builder.HasOne(e => e.PessoaCliente).WithMany();
        builder.Property("PessoaClienteId").HasColumnName("PessoaCliente");
    }
}

Nota: Usei EF Core 3.1

Após 7 anos de uso, em plena pandemia, o XBOX360 resolveu dar pau. Vários.

PARTE 1 – Red Light of Death

  1. XBOX 360 não ligava. O botão de power ficava vermelho ao invés de verde.
  2. Isso significa: “Red Light of Death
  3. Para descobrir o código de erro:
    1. Desligue o XBOX360, segure o botão sync e aperte o botão eject 4 vezes.
    2.  Isso fará o led do botão power piscar 4 vezes.
    3. O número de luzes representam os valores de 0 a 3 (4 luzes significa 0).
    4. No meu caso, código 0101.
  4. Não consegui resolver, tive que levar para consertar.
  5. O problema era a GPU. Segundo a assistência, ela “desalinhou”. Custou R$300 e resolveu.

Pode ser útil: https://en.wikipedia.org/wiki/Xbox_360_technical_problems

PARTE 2 – Memória Interna

O XBOX360 estava funcionando feliz um dia. No dia seguinte, a memória interna de 4GB amanheceu corrompida (não tem disco removível, já abri o console e não consegui descobrir onde fica).

  1. Formatei a unidade. Isso apagou jogos, perfis, atualizações do sistema.
  2. Ao tentar baixar meu perfil do xboxlive.com, pedia atualização do sistema.
  3. Ao tentar atualizar o sistema, dava o seguinte código de erro sem começar o download:
    • 4497-0000-3000-0902-8007-0070
    • Nota: Não tenho certeza, mas acho que a primeira tentativa de atualizar checou a começar o download e parou. Depois, nem começava mais a baixar.
  4. Tentei o seguinte, sem êxito: limpar cache, restaurar configurações gerais de fábrica, restaurar configurações de rede, baixar update para pen drive e atualizar com pen drive (não começava a extrair).
  5. O que realmente funcionou foi formatar a memória interna de novo.
    • Formatei (2a vez) e apareceram só 1.5GB livres.
    • Formatei de novo (3a vez) e apareceram 2.9GB livres. Esperava 4GB, mas talvez seja parte reservada para sistema e swap.
  6. Depois fui em “sistema”, “armazenamento”, posicionei o cursor na memória interna e fiz a sequência:
    • LB, RB, X, LB, RB, X
  7. Isso acionou a remoção de todos os updates do sistema de novo. Confirmei. O console reiniciou.
  8. Depois disso, consegui fazer a atualização e baixar meu perfil.

Pode ser útil: https://www.youtube.com/watch?v=7ihRDoW1FHU

PARTE 3 – Aquecimento

O XBOX360 está dando sinais de superaquecimento. Uma vez ele desligou sozinho, acendeu a luz vermelho e ligou uma “ventilação turbo” que eu nunca tinha visto. Ainda não fez de novo. Vou aguardar, aí vem briga. Talvez vou abri-lo para limpar por dentro, pode estar com cooler sujo.

Powershel, head txt

Posted: November 25, 2020 in Uncategorized
Tags: , , ,
powershell -command "& {Get-Content *filename* -TotalCount *n*}"

Para ler apenas as primeiras linhas de um arquivo no Windows PowerShell.

Usei para ver início de scripts gigantes de SQL de criação de bancos com dados.

  1. Botão direito no ícone de som na bandeja do sistema (perto do relógio).
  2. Escolher “Sons” (“Sounds”).
  3. Aba “Gravação” (“Recording”).
  4. Botão direito em “Mixagem Estéreo” (“Stereo Mixing”).
  5. “Habilitar” (“Enable”).
  6. Usar Audacity e mudar de “MME” para “Windows WSAPI”.
  7. Mudar o dispositivo de gravação no Audacity. Tente “Mixagem Estéreo”. Se não der, tente os outros. Na última vez, o que deu certo foi selecionar o disposivito de saída (“Fone de Ouvido”).

CT-e / MS / TLS 1.2

Posted: October 29, 2020 in Uncategorized
Tags: , ,

Tentando consumir webservices de CT-e de Mato Grosso do Sul (MS) no ambiente de produção:

EOF inesperado ou 0 bytes recebidos do fluxo de transporte.

O problema é que era exigido TLS 1.2, mas como estava usando o .NET Framework 2.0, parece que por default usava TLS 1.0.

O TLS 1.2 não aparece na enumeração do sistema:

System.Net.SecurityProtocolType

Mas isso não é um problema, basta fazer este truque antes de consumir o serviço:

System.Net.ServicePointManager.SecurityProtocol = (System.Net.SecurityProtocolType)3072;

É interessante que se você procurar pela Web verá que muita gente migrou a versão do framework porque não achou o valor necessário na enumeração.

https://github.com/coverlet-coverage/coverlet

https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-code-coverage

Primeiros passos com o framework de cobertura de código por testes unitários.

Com o VS 2019, ao criar um “projeto” de teste unitário com MSTest, o pacote coverlet.collector é incluído automaticamente.

Então, basta executar o seguinte comando no package manager console:

dotnet test --collect:"XPlat Code Coverage"

O resultado é a geração de arquivos XML com dados de cobertura em subdiretórios dos “projetos” de testes unitários.

Neste arquivo, o que devo ver? Esta ainda é a primeira vez que usei, então é uma resposta imatura, mas vamos lá.

A primeira tag:

<coverage line-rate=”0.1595″ branch-rate=”0.2727″ version=”1.9″ timestamp=”1594815785″ lines-covered=”154″ lines-valid=”965″ branches-covered=”24″ branches-valid=”88″>

Ou seja, está mostrando que:

(1) line-rate=”0.1595″ => apenas 15,95% das linhas foram executadas nos testes.

(2) lines-covered=”154″ lines-valid=”965″ => mesma informação, mas em termos absolutos, ou seja, 154 de 965 das linhas executáveis foram realmente executadas nos testes.

(3) branch-rate=”0.2727″ => apenas 27,27% das caminhos possíveis do código foram executados.

(4) branches-covered=”24″ branches-valid=”88″ => mesma informação, mas em termos absolutos, ou seja, 24 de 88 caminhos possíveis do código foram realmente executados nos testes.

A seguir, vêm os detalhes destas estatísticas discrimnadas por classe, mostrando todas as linhas e indicando se foram executadas ou não.

As informações são organizadas por classe, método e linha.

O mais interessante são as tags line com hits=”0″. Exemplo:

<line number=”96″ hits=”0″ branch=”False” />

Preste atenção nestas linhas, pois indicam o que ainda falta testar.

Um caso clássico é um if. Exemplo:

if(this.MyProperty == someValue)
DoSomething(); //hits > 0
else
DoAnotherThing(); //hits = 0

Ou seja, falta um teste em que this.MyProperty != someValue.

Finalmente, para visualizar o resultado de uma forma mais amigável (e muito interessante), é necessário um gerador de relatório. Então, instale o pacote, gere o relatório e abra-o no navegador:

dotnet tool install -g dotnet-reportgenerator-globaltool

reportgenerator
"-reports:Path\To\TestProject\TestResults\{guid}\coverage.cobertura.xml"
"-targetdir:coveragereport"
-reporttypes:Html

Mas o que seria realmente espetacular seria isto integrado visualmente dentro do VisualStudio. Talvez já exista isso, vamos ver.

Abordagem #3:

Alguém pode gostar de DataSet‘s e DataTable‘s.

public DataTable Report3(DateTime start, DateTime end)
{
    DataTable tab = new DataTable();
    for (int i = 0; i  n.DataRecebimento >= start)
        .Where(n => n.DataRecebimento  n.Fornecedor)
        .ThenInclude(f => f.PessoaCredor)
        .ThenInclude(p => p.EnderecoPessoa)
        .ThenInclude(e => e.Cidade)
        .ThenInclude(c => c.EstadoMunicipio)
        .ThenInclude(uf => uf.PaisEstado)
        .SelectMany(n => n.Produtos)
        .Include(item => item.MaterialItem)
        .Include(item => item.UnidadeCompra)
        .Select(item => tab.Rows.Add(
            item.NFEItem.Numero,
            item.NFEItem.Serie,
            item.NFEItem.DataRecebimento,
            item.NFEItem.Fornecedor.PessoaCredor.CNP,
            item.NFEItem.Fornecedor.PessoaCredor.Nome,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.Nome,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.Sigla,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.PaisEstado.Nome,
            item.Sequencial,
            item.MaterialItem.Abreviatura,
            item.MaterialItem.Codigo,
            item.UnidadeCompra.Codigo
        ))
        .ToList();

    return tab;
}

A “vantagem” de retornar um DataTable é não criar uma classe de modelo específico para o relatório. Quer dizer, se por acaso você fosse obrigado a gerar DataTable para a visualização do relatório, então você não ia querer primeiro criar e preencher uma classe de modelo para depois ter que copiar os dados para um DataTable.

Uma coisa que fica feita é que o ToList retorna uma coisa inútil. Usei-o para executar a query, mas talvez tenha outra forma, mas desconheço no momento.

Abordagem #2:

public List Report2(DateTime start, DateTime end)
{
    return this.Entities
    .AsNoTracking()
    .Where(n => n.DataRecebimento >= start)
    .Where(n => n.DataRecebimento  n.Fornecedor)
    .ThenInclude(f => f.PessoaCredor)
    .ThenInclude(p => p.EnderecoPessoa)
    .ThenInclude(e => e.Cidade)
    .ThenInclude(c => c.EstadoMunicipio)
    .ThenInclude(uf => uf.PaisEstado)
    .SelectMany(n => n.Produtos)
    .Include(item => item.MaterialItem)
    .Include(item => item.UnidadeCompra)
    .Select(item => new RefactorMe()
    {
        Numero = item.NFEItem.Numero,
        Serie = item.NFEItem.Serie,
        DataRecebimento = item.NFEItem.DataRecebimento,
        CNP = item.NFEItem.Fornecedor.PessoaCredor.CNP,
        NomeFornecedor = item.NFEItem.Fornecedor.PessoaCredor.Nome,
        Cidade = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.Nome,
        UF = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.Sigla,
        Pais = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.PaisEstado.Nome,
        SequenciaItem = item.Sequencial,
        Material = item.MaterialItem.Abreviatura,
        CodigoMaterial = item.MaterialItem.Codigo,
        Unidade = item.UnidadeCompra.Codigo
    })
    .ToList();
}

Note que para esse caso foi criada uma classe especificamente com o modelo final específico para o relatório. Isso cria uma complicação para desenhar as camadas da aplicação.

public class RefactorMe 
{
    public int Numero;
    public string Serie;
    public DateTime DataRecebimento;
    public string CNP;
    public string NomeFornecedor;
    public string Cidade;
    public string UF;
    public string Pais;
    public int SequenciaItem;
    public string Material;
    public string CodigoMaterial;
    public string Unidade;
}

Em compensação, a query tem um join bizarro de NotaFiscalEntrada para ItemNFEProduto e depois outro de ItemNFEProduto para NotaFiscalEntrada. Veja query abaixo.

Isso poderia ser evitado, declarando um DbSet para ItemNFEProduto e partindo dele ao invés de partir da NotaFiscalEntrada. Deve ter outro jeito também, mas não é o propósito deste post.

Query gerada:

SELECT [n0].[Numero], [n0].[Serie], [n0].[DataRecebimento], [p].[CNP], [p].[Nome] AS [NomeFornecedor],
[m].[Nome] AS [Cidade], [e0].[Sigla] AS [UF], [p0].[Nome] AS [Pais], [i].[Sequencial] AS [SequenciaItem], [m0].[Abreviatura] AS [Material],
[m0].[Codigo] AS [CodigoMaterial], [u].[Codigo] AS [Unidade]
FROM[NotaFiscalEntrada] AS[n]
INNER JOIN[ItemNFEProduto] AS[i] ON[n].[AutoId] = [i].[NFEItem]
LEFT JOIN[NotaFiscalEntrada] AS[n0] ON[i].[NFEItem] = [n0].[AutoId]
LEFT JOIN[Credor] AS[c] ON[n0].[Fornecedor] = [c].[AutoId]
LEFT JOIN[Pessoa] AS[p] ON[c].[PessoaCredor] = [p].[AutoId]
LEFT JOIN[Endereco] AS[e] ON[p].[EnderecoPessoa] = [e].[AutoId]
LEFT JOIN[Municipio] AS[m] ON[e].[Cidade] = [m].[AutoId]
LEFT JOIN[Estado] AS[e0] ON[m].[EstadoMunicipio] = [e0].[AutoId]
LEFT JOIN[Pais] AS[p0] ON[e0].[PaisEstado] = [p0].[AutoId]
LEFT JOIN[Material] AS[m0] ON[i].[MaterialItem] = [m0].[AutoId]
LEFT JOIN[Unidade] AS[u] ON[i].[UnidadeCompra] = [u].[AutoId]
WHERE([n].[DataRecebimento] >= @__start_0) AND([n].[DataRecebimento] <= @__end_1)',N'@__start_0 datetime2(7),@__end_1 datetime2(7)',@__start_0='2019 – 01 – 01 00:00:00',@__end_1='2019 – 01 – 31 00:00:00'