NOTíCIAS DO SETOR

Aspire 13.2: O que os desenvolvedores de serviços .NET precisam saber

Compartilhando algumas notas sobre o lançamento do Aspire 13.2 da equipe de engenharia aqui na Iron Software. Enviamos bibliotecas .NET (IronPDF, IronOCR, IronXL, IronWord, IronBarcode, e o resto), e praticamente todas as chamadas de clientes tocam em orquestração de aplicativos distribuídos. É por isso que prestamos atenção às versões Aspire. 13.2 é o primeiro onde o CLI parece que pode realmente substituir o painel para a maior parte do trabalho diário, e há algumas mudanças significativas que vão afetá-lo na atualização.

Este não é um regurgito de notas de lançamento. A página oficial de novidades tem a lista completa. Isto é o que é genuinamente útil em uma base de código de trabalho, além das armadilhas para observar.

Resumindo

  • A CLI agora é realmente scriptável: modo separado, aspire ps, aspire parar, modo isolado, saída JSON
  • O TypeScript AppHost está em pré-visualização se você quiser abandonar o .csproj para a camada de orquestração
  • Arquivos de configuração consolidaram-se em aspire.config.json (arquivos legados migram automaticamente)
  • Foundry substitui Azure AI Foundry. Este vai quebrar sua build
  • WithSecretBuildArg foi renomeado para WithBuildSecret
  • Env vars de descoberta de serviço agora usam esquema, não o nome do endpoint (risco de quebra silenciosa)
  • Comportamento padrão de credenciais Azure alterado em integrações de cliente

O CLI finalmente é um CLI

Este é o título. Antes do 13.2, aspire run bloqueava seu terminal e o dashboard era a única superfície realista para gerenciar um apphost em execução. Bom para desenvolvedor solo, estranho para CI, testes de integração ou qualquer fluxo de trabalho orientado por agentes.

13.2 corrige isso:

# Run in the background
aspire run --detach

# Or the new shortcut
aspire start

# See what's running
aspire ps

# Stop it
aspire parar
aspire parar --all
# Run in the background
aspire run --detach

# Or the new shortcut
aspire start

# See what's running
aspire ps

# Stop it
aspire parar
aspire parar --all
SHELL

Combinado com --format json (que vai para stdout enquanto mensagens de status vão para stderr, importante se você estiver redirecionando para qualquer lugar), você pode construir automação real em torno disso. aspire ps --resources --format json é uma base sólida para integrações de editores e scripts.

O modo isolado é o herói desconhecido

--isolated é o que estávamos esperando. Ele executa um apphost com portas randomizadas e segredos de usuário isolados, prevenindo conflitos de porta e colisões de configuração:

aspire run --isolated
aspire start --isolated
aspire run --isolated
aspire start --isolated
SHELL

Se você já tentou executar dois checkouts do mesmo apphost simultaneamente — digamos, principal contra um branch de recurso, testes de integração paralelos ou fluxos de trabalho dirigidos por agente — você já sentiu a dor. Portas aleatórias mais segredos separados significam que você pode finalmente apenas iniciar N cópias e não se preocupar.

Somente para worktrees do git, isso já vale o upgrade. Para suítes de testes de integração que trazem serviços reais com dependências nativas (renderização no Chrome para geração de PDF, Tesseract para OCR, os pesos pesados usuais), é a diferença entre instável e confiável.

aspire doctor e aspire describe

aspire doctor percorre seu ambiente: estado do certificado dev, versão do runtime do contêiner, .NET SDK, configuração WSL2, configuração do agente. É o tipo de coisa que todo framework deveria ter e a maioria não se incomoda. A saída é aplicável. Quando algo está errado, ele diz o que fazer.

aspire describe --follow oferece uma visualização contínua do estado de recursos a partir do terminal. Os mesmos dados que o painel exibe, mas canalizáveis. Coloque-o em um painel tmux e você obtém a maior parte do valor do painel em 80 colunas.

Os comandos de recurso ficaram mais organizados

Os comandos antigos resource-start / resource-parar / resource-restart estão obsoletos em favor da forma de subcomando mais limpa:

aspire resource api restart
aspire resource api rebuild
aspire resource api restart
aspire resource api rebuild
SHELL

rebuild é novo. Ele para, compila e reinicia um único recurso de projeto .NET sem desmontar toda a sessão do apphost. Se você já alterou um serviço em um gráfico de 12 recursos e resmungou ao reiniciar tudo, esta é a solução. Sentimos isso nós mesmos: quando você está iterando em um modelo de renderização de PDF ou ajustando o pré-processamento OCR, reiniciar todo o gráfico apenas para recarregar um projeto fica velho rapidamente.

Segredos e certificados sem sair da CLI

Dois novos grupos de comando dedicados:

aspire certs clean
aspire certs trust

aspire secret set ApiKey super-secret-value
aspire secret list --format json
aspire certs clean
aspire certs trust

aspire secret set ApiKey super-secret-value
aspire secret list --format json
SHELL

aspire secret é o maior ganho. Ele mapeia para o mesmo armazenamento de segredos de usuário que suporta AddParameter(..., secret: true) no modelo de aplicativo, mas você não precisa do .NET CLI instalado para gerenciá-los. Em um apphost poliglota onde nem todo desenvolvedor tem o SDK .NET, isso é importante.

aspire wait for CI

aspire wait api --status healthy --timeout 120
aspire wait api --status healthy --timeout 120
SHELL

Bloqueie no estado de um recurso. Combinado com aspire start e --format json, você finalmente pode escrever scripts de CI que esperam que as coisas estejam realmente prontas em vez de sleep 30 && hope.

Configuração: um arquivo para governar todos

Aspire está consolidando seus arquivos de configuração. A antiga divisão entre .aspire/settings.json e apphost.run.json foi eliminada, substituída por um único aspire.config.json na raiz do projeto:

{
  "appHost": {
    "path": "apphost.ts",
    "language": "typescript/nodejs"
  },
  "sdk": { "version": "13.2.0" },
  "channel": "stable",
  "profiles": {
    "default": {
      "applicationUrl": "https://localhost:17000;http://localhost:15000"
    }
  }
}

A migração é automática. Na primeira vez que você executar qualquer comando aspire em um projeto existente, os arquivos legados são mesclados no novo formato com os caminhos rebaseados para a raiz do projeto. Os arquivos legados são preservados para que você ainda possa usar versões antigas da CLI lado a lado. Configurações globais (globalsettings.json) também são migradas.

Se você tem automação que interage diretamente com .aspire/settings.json ou apphost.run.json, planeje movê-lo.

TypeScript AppHost (prévia)

Isso é interessante mesmo que você não vá usar no primeiro dia. Agora você pode escrever seu apphost em TypeScript em vez de C#:

import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const cache = await builder.addRedis("cache");

const api = await builder.addProject("api", "../api")
    .withReference(cache)
    .waitFor(cache);

await builder.build().run();

Nos bastidores, o apphost TS executa como um processo convidado conversando com o host de orquestração .NET do Aspire sobre JSON-RPC em um transporte local. Mesmo modelo de recurso, mesmo painel, mesmas integrações, apenas expresso em TypeScript.

A parte interessante é o codegen. Quando você executa aspire add, a CLI inspeciona o assembly .NET da integração e gera um SDK TypeScript em .modules/. aspire restore os regenera, útil após atualizar ou mudar de branches (também executa automaticamente em aspire run). O gerador 13.2 também adicionou alvos de teste Go, Java e Rust, o que sugere onde isso está indo.

Para equipes que priorizam .NET como a nossa, isso é mais "assista isso" do que "envie isso", mas o padrão de codegen significa que futuras linguagens de apphost poliglota seguirão o mesmo modelo. Veja a documentação de arquitetura multi-linguagem para saber como a ponte do host funciona.

Painel: exportação/importação de telemetria é o novo brinquedo

O painel ganhou um fluxo real de trabalho de exportação/importação. Em Configurações → Gerenciar, escolha recursos e tipos de telemetria e exporte-os como JSON em um zip. Reimporte no painel mais tarde, ou passe para outra pessoa (ou um LLM) para análise.

O comando CLI aspire export produz o mesmo pacote:

aspire export --output .\artifacts\aspire-export.zip
aspire export <resource>
aspire export --output .\artifacts\aspire-export.zip
aspire export <resource>
SHELL

Genuinamente útil para relatórios de bugs. Em vez de "aqui estão algumas capturas de tela e um arquivo de log", você pode anexar uma foto instantânea do estado real da telemetria.

Outras notas do lado do painel:

  • Agora você pode definir parâmetros de recurso diretamente da interface do painel, com uma opção para persistir nos segredos do usuário
  • Variáveis do Ambiente podem ser exportadas como arquivos .env a partir da visualização de detalhes do recurso
  • O layout do gráfico de recursos usa posicionamento adaptável baseado em forças. Gráficos complexos estão notavelmente menos desordenados
  • A API HTTP de telemetria em /api/telemetry retorna JSON OTLP; suporta ?follow=true para streaming NDJSON. Endpoints cobrem recursos, spans, logs e traços (incluindo /traces/{traceId} para busca completa de traços)

O painel independente agora define a API de telemetria como desativada por padrão. Se você está hospedando o dashboard por conta própria e dependendo da API, você precisa de DASHBOARD__API__ENABLED=true mais configuração de autenticação (DASHBOARD__API__AUTHMODE e DASHBOARD__API__PRIMARYAPIKEY). Cenários integrados ao AppHost ainda funcionam porque Aspire.Hosting conecta a API automaticamente para ferramentas.

Fragmentos do modelo de aplicativo que valem a pena notar

WithMcpServer

Você pode declarar no modelo de aplicativo que um recurso hospeda um endpoint MCP:

var api = builder.AddProject<Projects.MyApi>("api")
    .WithMcpServer("/mcp");
var api = builder.AddProject<Projects.MyApi>("api")
    .WithMcpServer("/mcp");
Dim api = builder.AddProject(Of Projects.MyApi)("api") _
    .WithMcpServer("/mcp")
$vbLabelText   $csharpLabel

Ferramentas do Aspire podem então descobrir e proxy esse endpoint. Se você está distribuindo qualquer coisa que exponha ferramentas para agentes de codificação, esta é a maneira mais limpa de integrá-lo. Caminho ou nome de endpoint personalizado suportado através de opções.

Resolução de endpoint contextual

Este é o tipo de coisa que você não percebe até precisar. Agora, os endpoints podem ser resolvidos na perspectiva de um chamador ou rede específico:

var endpoint = redis.GetEndpoint("tcp");

var url = await endpoint.GetValueAsync(new ValueProviderContext {
    Caller = containerApp.Resource,
});
var endpoint = redis.GetEndpoint("tcp");

var url = await endpoint.GetValueAsync(new ValueProviderContext {
    Caller = containerApp.Resource,
});
Dim endpoint = redis.GetEndpoint("tcp")

Dim url = Await endpoint.GetValueAsync(New ValueProviderContext With {
    .Caller = containerApp.Resource
})
$vbLabelText   $csharpLabel

O mesmo endpoint do Redis se resolverá para localhost:6379 de um processo host, host.docker.internal:6379 de um contêiner na rede do host, ou cache:6379 de um contêiner na rede de contêineres Aspire, dependendo do contexto. A classe KnownNetworkIdentifiers oferece LocalhostNetwork, DefaultAspireContainerNetwork, e PublicInternet se você preferir escolher uma rede do que um chamador.

As notas de lançamento chamam explicitamente a atenção para o fato de que essas APIs existiam no 13.1, mas não se comportavam corretamente. Portanto, se você escreveu algo contra elas no 13.1, reteste. Detalhes completos na documentação de hierarquias de recursos.

Segredos de compilação de container

WithSecretBuildArg foi renomeado para WithBuildSecret. O novo nome é mais claro. Estes fluem através do Docker/Podman como segredos de compilação adequados, não como argumentos de compilação (que vazam para o histórico da imagem).

builder.AddContainer("worker", "contoso/worker")
    .WithDockerfile("../worker")
    .WithBuildSecret("ACCESS_TOKEN", accessToken);
builder.AddContainer("worker", "contoso/worker")
    .WithDockerfile("../worker")
    .WithBuildSecret("ACCESS_TOKEN", accessToken);
$vbLabelText   $csharpLabel

Segredos de construção agora também podem ser arquivos (por exemplo, .npmrc para autenticação de registro privado em builds de contêiner), que cobre a maioria dos casos de uso do mundo real.

Integrações: as que importam

A lista completa é longa. Estas são as que eu destacaria:

  • A publicação do Docker Compose agora é estável (era pré-lançamento). AddDockerComposeEnvironment gera um docker-compose.yaml do seu modelo de aplicativo no momento da publicação. Saída útil quando "implantar no Azure" não é a resposta. Vale a pena notar se você estiver distribuindo contêineres que incluem dependências nativas, já que IronPDF, IronOCR e IronXL suportam contêineres Linux e Docker de forma limpa, então o arquivo de composição gerado geralmente funciona sem cirurgia manual.
  • A integração Rede Virtual Azure (Aspire.Hosting.Azure.Network) permite declarar VNets, sub-redes, NSGs, gateways NAT e endpoints privados no apphost. AddPrivateEndpoint cria automaticamente zonas DNS privadas, links de rede virtual, e desabilita o acesso público ao alvo. Este é o tipo de coisa que anteriormente significava manter um arquivo Bicep separado.
  • O Azure Data Lake Storage obteve suporte tanto para hospedagem quanto para cliente: AddDataLake, AddDataLakeFileSystem, além de AddAzureDataLakeServiceClient / AddAzureDataLakeFileSystemClient no lado do cliente. Registro de DI, tentativas, verificações de saúde, telemetria, o habitual stack Aspire.
  • MongoDB EF Core tem uma nova integração de cliente (Aspire.MongoDB.EntityFrameworkCore). AddMongoDbContext<TContext> para o caso típico, ou EnrichMongoDbContext<TContext>() se você estiver registrando o DbContext por si mesmo.
  • Inferência Azure AI agora oferece suporte a embeddings, não apenas chat. Registrar AddAzureEmbeddingsClient("embeddings").AddEmbeddingGenerator() e injetar IEmbeddingGenerator<string, Embedding<float>>. Variante chaveada disponível também.
  • Registro de Contêiner Azure obteve WithPurgeTask("0 1 * * *", ago: TimeSpan.FromDays(7), keep: 5), que provisiona uma tarefa de purga de ACR em um agendamento cron.
  • Suporte Bun para recursos JavaScript via WithBun(). A confiabilidade do Yarn com AddViteApp também foi corrigida via WithYarn().
  • Microsoft Foundry substitui Azure AI Foundry. Aspire.Hosting.Foundry substitui Aspire.Hosting.Azure.AIFoundry. Mudança disruptiva; detalhes abaixo.

Juntando tudo: um serviço de documentos no Aspire 13.2

Este é o padrão que usamos internamente para testar cenários distribuídos com nossas bibliotecas. Vale a pena mostrar porque a maioria dos novos recursos 13.2 compensa neste tipo de configuração de multi-serviço, não em demos brinquedo.

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

// A worker service that uses IronPDF for HTML to PDF rendering
var renderer = builder.AddProject<Projects.PdfRenderer>("renderer")
    .WithReference(cache)
    .WaitFor(cache)
    .WithMcpServer("/mcp");

// An OCR worker that uses IronOCR for image and PDF text extraction
var ocr = builder.AddProject<Projects.OcrWorker>("ocr-worker")
    .WithReference(cache);

// API gateway that fans out to both
builder.AddProject<Projects.Api>("api")
    .WithReference(renderer)
    .WithReference(ocr)
    .WaitFor(renderer)
    .WaitFor(ocr);

builder.Build().Run();
var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

// A worker service that uses IronPDF for HTML to PDF rendering
var renderer = builder.AddProject<Projects.PdfRenderer>("renderer")
    .WithReference(cache)
    .WaitFor(cache)
    .WithMcpServer("/mcp");

// An OCR worker that uses IronOCR for image and PDF text extraction
var ocr = builder.AddProject<Projects.OcrWorker>("ocr-worker")
    .WithReference(cache);

// API gateway that fans out to both
builder.AddProject<Projects.Api>("api")
    .WithReference(renderer)
    .WithReference(ocr)
    .WaitFor(renderer)
    .WaitFor(ocr);

builder.Build().Run();
Imports DistributedApplication

Dim builder = DistributedApplication.CreateBuilder(args)

Dim cache = builder.AddRedis("cache")

' A worker service that uses IronPDF for HTML to PDF rendering
Dim renderer = builder.AddProject(Of Projects.PdfRenderer)("renderer") _
    .WithReference(cache) _
    .WaitFor(cache) _
    .WithMcpServer("/mcp")

' An OCR worker that uses IronOCR for image and PDF text extraction
Dim ocr = builder.AddProject(Of Projects.OcrWorker)("ocr-worker") _
    .WithReference(cache)

' API gateway that fans out to both
builder.AddProject(Of Projects.Api)("api") _
    .WithReference(renderer) _
    .WithReference(ocr) _
    .WaitFor(renderer) _
    .WaitFor(ocr)

builder.Build().Run()
$vbLabelText   $csharpLabel

O que você obtém especificamente do 13.2:

  • aspire start --isolated permite que você execute duas cópias deste gráfico lado a lado sem colisões de porta. Útil ao comparar branches ou executar testes de integração paralelos contra o renderizador
  • aspire resource renderer rebuild recarrega apenas o renderizador de PDF quando você muda um template Razor, ao invés de regenerar todo o gráfico.
  • aspire wait renderer --status healthy --timeout 120 permite que seu CI bloqueie até que a renderização do Chrome seja inicializada antes de executar testes de geração de PDF
  • A API HTTP de telemetria e aspire export fornece spans formatados em OTLP para cada chamada de renderização, que é como você realmente pegaria uma regra CSS lenta no tráfego de produção
  • WithMcpServer permite expor o renderizador como uma ferramenta MCP para fluxos de trabalho de agentes de código, útil se você está construindo algo que gera documentos programaticamente

Se você quiser construir um serviço como o renderizador acima, o tutorial HTML para PDF do IronPDF explica o lado do C#. Para o trabalhador OCR, o guia de introdução do IronOCR cobre o básico.

Mudanças disruptivas que realmente te morderão

Em ordem aproximada de quão provável você é de encontrá-los:

Nomenclatura de env var de descoberta de serviço

# Before (13.0/13.1)
services__myservice__myendpoint__0 = https://localhost:5001

# After (13.2)
services__myservice__https__0 = https://localhost:5001
# Before (13.0/13.1)
services__myservice__myendpoint__0 = https://localhost:5001

# After (13.2)
services__myservice__https__0 = https://localhost:5001
SHELL

O esquema do endpoint é usado em vez do nome do endpoint. Se você tem qualquer código ou configuração que corresponda a esses nomes de env var, atualize-os. Este é o erro silencioso mais provável: nada é lançado, as variáveis apenas têm diferentes chaves.

BeforeResourceStartedEvent

Anteriormente disparado mais amplamente; agora só dispara quando realmente inicia um recurso, não em cada mudança de estado. Se o seu manipulador contava com o comportamento anterior, ele silenciosamente deixará de ser executado.

AIFoundry para Foundry

Renomeação de pacote e API. Atualize a referência de pacote e as chamadas:

<PackageReference Include="Aspire.Hosting.Foundry" Version="13.2.0" />
<PackageReference Include="Aspire.Hosting.Foundry" Version="13.2.0" />
XML
// Before
var ai = builder.AddAzureAIFoundry("ai");

// After
var foundry = builder.AddFoundry("ai");
var project = foundry.AddProject("agents");
var chat = project.AddModelDeployment("chat", FoundryModel.OpenAI.Gpt5Mini);
// Before
var ai = builder.AddAzureAIFoundry("ai");

// After
var foundry = builder.AddFoundry("ai");
var project = foundry.AddProject("agents");
var chat = project.AddModelDeployment("chat", FoundryModel.OpenAI.Gpt5Mini);
' Before
Dim ai = builder.AddAzureAIFoundry("ai")

' After
Dim foundry = builder.AddFoundry("ai")
Dim project = foundry.AddProject("agents")
Dim chat = project.AddModelDeployment("chat", FoundryModel.OpenAI.Gpt5Mini)
$vbLabelText   $csharpLabel

RunAsFoundryLocal ainda funciona para desenvolvimento de modelo local, mas Foundry Projects não são suportados quando o recurso pai está configurado como Foundry Local.

Credencial padrão do Azure

Integrações de clientes Aspire Azure não utilizam mais o construtor DefaultAzureCredential sem parâmetros. Se você estava contando com credenciais diferentes de ManagedIdentityCredential funcionando em um serviço Azure, mudanças de comportamento. Leia o documento de credencial padrão do Azure antes de atualizar a produção.

Renomeação de comando de recurso

resource-start / resource-parar / resource-restart agora são aspire resource <name> start|parar|reiniciar. Atualize qualquer script. A opção --apphost também é agora preferida em relação ao legado --project (que ainda é aceito).

Sufixo de propriedade de conexão

Um sufixo de propriedade de conexão foi adicionado. Se você acessar propriedades de conexão diretamente (em vez de via WithReference), verifique se seu código ainda as resolve.

WithSecretBuildArg para WithBuildSecret

Mencionado acima. Renomeação direta.

IAzureContainerRegistry obsoleto

Use a propriedade ContainerRegistry em ambientes computacionais em vez disso.

API de telemetria de painel agora com opção para entrar (independente)

Já abordado acima, mas vale a pena repetir: implantações de painel independentes agora precisam habilitar a API explicitamente.

Você deve atualizar?

Para uma loja .NET funcional rodando apps de multi-serviços localmente, sim, assumindo que você inventariou as mudanças críticas acima. As melhorias no CLI por si só valem a pena. Os modos destacado e isolado, em particular, resolvem problemas reais de fluxo de trabalho.

Para usuários do Foundry: a renomeação é uma migração forçada se você quiser qualquer novidade desta versão, portanto, planeje de acordo.

Para os curiosos sobre TypeScript: 13.2 é a primeira versão em que o apphost TS é real o suficiente para ser avaliado. Ainda em pré-visualização, mas vale uma tarde de sexta-feira.

A atualização em si é uma linha se você já estiver no 13.x:

aspire update --self
aspire update
aspire update --self
aspire update
SHELL

Se você estiver no 12.x ou anterior, acesse primeiro o guia de atualização. Há um passo no 13.0 que você não pode pular.

Notas de patch: 13.2.1

13.2.1 foi enviada desde a versão original com correções de confiabilidade. Há uma pequena renomeação no SDK TypeScript que vale a pena mencionar, só importa se você já estiver na pré-visualização do apphost TS:

Anterior Novo
runAsExistingFromParameters(name, resourceGroup) runAsExisting(name, { resourceGroup })
publishAsExistingFromParameters(name, resourceGroup) publishAsExisting(name, { resourceGroup })
withConnectionPropertyValue(name, value) withConnectionProperty(name, value)
withParameterBuildArg(name, parameter) withBuildArg(name, parameter)

withConnectionPropertyValue é mantido como um alias de compatibilidade nos SDKs gerados, portanto, não é uma ruptura em tempo de execução.

Construindo apps .NET distribuídos com cargas de trabalho de documentos?

Se os seus serviços realizam geração de PDF, OCR, processamento de Excel, códigos de barras ou quaisquer outros formatos que cobrimos, nossas bibliotecas são projetadas exatamente para o tipo de configuração amigável a multi-serviços que o Aspire orquestra. Tudo suporta .NET 10, 9, 8, 7, 6, Framework e Core, e roda em containers Linux, Azure, AWS e no local.

Alguns lugares para começar:

  • IronPDF para HTML para PDF, edição de PDF, assinatura e formulários. O hub de tutoriais é o caminho mais rápido para um serviço de renderização funcional.
  • IronOCR para extração de texto de imagens e PDFs em mais de 125 idiomas.
  • IronXL para leitura/escrita de Excel sem Interop do Office.
  • IronWord para geração e edição de DOCX.
  • IronBarcode e IronQR para geração e escaneamento de códigos de barras e QR.
  • Iron Suite se você precisar de mais de um dos acima.

Você pode obter uma chave de teste de 30 dias e ter um serviço de PDF ou OCR em execução dentro de um apphost Aspire em bem menos de uma hora. Se encontrar algo estranho, nosso time de suporte são engenheiros de verdade, não uma fila de triagem de tickets.

É isso. Nos vemos na próxima versão.