NET 7 -  Alterações significativas para o desenvolvimento web


   Neste artigo vou apresentar as 3 alterações mais significativas para o desenvolvimento web que o .NET 7.0 vai trazer.

Ao escrever este artigo o .NET já foi lançado oficialmente a dois dias e temos agora o .NET SDK 7.0 eo Visual Studio 2022 versão 17.4.0 que é compatível com o .NET 7.0 disponíveis para download.

Hoje ao invés de estarmos preocupados com os novos recursos apresentados pela plataforma vamos focar nas alterações que podem causar problemas durante a migração de uma base de código existente para a nova versão do .NET.

Assim vou apresentar informações relacionadas a 3 importantes alterações que podem impactar o seu código em uma migração. Para obter informações detalhadas, você pode consultar a página de alterações importantes na MSDN.

1. As Actions dos Controllers das APIs agora tentam inferir parâmetros do contâiner DI

Antes do .NET 7, se uma action de uma API precisasse resolver um tipo registrado no contêiner de injeção de dependência, o parâmetro precisava ser decorado com [FromServices].

Portanto, usávamos o método Action com o atributo FromServices, o framework iria pesquisar o contêiner de serviços e iria injetar a implementação correspondente.  Sem esse atributo, o framework tentaria resolver o parâmetro dos dados do body do request.

A partir do .NET 7, o framework tenta implicitamente resolver os parâmetros dos tipos registrados na DI.

Desta forma agora, os tipos na DI são verificados na inicialização do aplicativo usando IServiceProviderIsService para determinar se um argumento de uma Action do controlador de API vem da DI ou de outras fontes.

As regras usadas são as seguintes:

- Se a origem da associação for especificada explicitamente, essa origem da associação será usada;
- Se o tipo complexo estiver registrado no contêiner DI, o tipo será resolvido implicitamente a partir do DI;
- Se o tipo não estiver registrado no contêiner DI, o tipo será resolvido a partir do corpo do request;
- Se algum parâmetro de rota corresponder ao nome do parâmetro, o valor do parâmetro será resolvido a partir do caminho;
- Se nenhuma das regras acima corresponder, o parâmetro será resolvido a partir da query string da URL;


A documentação sugere que não devem existir muitas alterações que precisem ser feitas ou que o código vai quebrar devido a essa mudança.

Caso o aplicativo falhe devido a isso, você pode desabilitar essa resolução implícita das Actions do controlador da API, especificando DisableImplicitFromServicesParameters como true nas opções de comportamento da API.

O exemplo de código para definir esse sinalizador é fornecido abaixo :

Services.Configure<ApiBehaviorOptions>(options =>
{
     options.DisableImplicitFromServicesParameters = true;
});

Se esta alteração quebrar o seu código mas você quiser vincular a DI para parâmetros de ação do controlador de API específicos, poderá desabilitar o recurso conforme mostrado acima e usar um atributo que implemente IFromServiceMetadata, como FromServicesAttribute:

Services.AddScoped<SomeCustomType>();
...

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    // vincular a partir do DI
    [HttpPost]
    public ActionResult Post([FromServices]SomeCustomType service) => Ok();
}

2. Atualização do Microsoft.Data.SqlClient para 4.0.1

O pacote Microsoft.Data.SqlClient pode ser usado por qualquer aplicativo da plataforma .NET para interagir com o SQL Server. Esta versão (4.0.1) do pacote NuGet já foi lançada nos últimos meses. O .NET 7 usa esta versão mais recente desde a preview 2.

O que há de tão especial no Microsoft.Data.SqlClient versão 4.0.1 ?

Para conectar-se a qualquer banco de dados, é necessária uma string de conexão. A string de conexão tem várias partes – cada parte é basicamente uma configuração que é usada para conectar ao banco de dados.

Algumas das configurações comuns são :

  1. Servidor de banco de dados (endereço IP/nome do servidor)
  2. Nome do banco de dados;
  3. ID de usuário e senha (ou quaisquer outras credenciais para se conectar ao servidor de banco de dados).

Além dessas configurações, a cadeia de conexão também possui uma configuração chamada – Encrypt. Essa configuração sugere se a conexão com o servidor de banco de dados é criptografada ou não. Na versão mais recente do pacote, essa configuração, Encrypt, foi definida como true, por padrão.

O que isto significa ?

Isso significa que, por padrão, quando este assembly estiver tentando iniciar a conexão, ele tentará abrir um canal criptografado e seguro. Conforme mencionado nas notas de versão deste pacote, essa alteração foi necessária porque o uso de banco de dados baseado em nuvem está crescendo e é recomendável usar conexões seguras com bancos de dados baseados em nuvem.

Portanto, se o banco de dados não suportar conexão segura, você receberá uma exceção quando o aplicativo estiver tentando se conectar ao banco de dados. É por isso que é muito importante saber sobre essa alteração.

Existem duas maneiras de corrigir este problema:

  1. A primeira opção é habilitar conexões seguras;
  2. A segunda opção é definir explicitamente a configuração de criptografia como false na string de conexão;

3. O Hub SignalR agora tenta resolver os parâmetros a partir do container DI

Esta é uma mudança importante pois muitos aplicativos usam o SignalR hoje em dia.

Os métodos do Hub SignalR agora dão suporte à injeção de serviços do seu contêiner de injeção de dependência (DI). Em casos raros, isso pode quebrar aplicativos que têm um tipo de DI que também é aceito em métodos de Hub de mensagens do cliente SignalR.

Entretanto, o hub SignalR pode ter métodos e os parâmetros para esses métodos podem ser resolvidos com base nos dados/mensagens enviados pelo cliente. Mesmo que o tipo seja registrado no container de injeção de dependência, a preferência foi dada à mensagem enviada pelo cliente.

De acordo com o novo comportamento, se um tipo for registrado no contêiner de injeção de dependência e o cliente também estiver enviando esse tipo como mensagem, o cliente receberá um erro.Isso ocorre porque o framework dará preferência para carregar o tipo a partir do container DI.

Pode haver muito menos chances de registrar o tipo na DI se o tipo corresponder à mensagem enviada pelo cliente. Portanto, é muito menos provável que essa alteração interrompa o aplicativo.

Caso o aplicativo quebre por este motivo, você pode definir o sinalizador DisableImplicitFromServicesParameters conforme mostrado no código abaixo:

Services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

Se sua aplicação foi afetada por esta alteração, mas quer usar o recurso sem quebrar os clientes, você pode desabilitar o recurso como mostrado acima e usar um atributo que implemente IFromServiceMetadata em novos argumentos/métodos de Hub:

Services.AddScoped<SomeCustomType>();
Services.AddScoped<SomeCustomType2>();

class MyHub : Hub
{
    // método antigo com novo recurso (sem quebra), apenas SomeCustomType2 é resolvido a partir de DI
    public Task MethodA(string arguments, SomeCustomType type, [FromServices] SomeCustomType2 type2);
    // novo método
    public Task MethodB(string arguments, [FromServices] SomeCustomType type);
}

E estamos conversados...

Extraído do documento : Breaking Changes in .NE 7.0

"como está escrito: Não há justo, nem um sequer, não há quem entenda, não há quem busque a Deus;
todos se extraviaram, à uma se fizeram inúteis; não há quem faça o bem, não há nem um sequer."
Romanos 3:10-12

Referências:


José Carlos Macoratti