<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Topics tagged with howto]]></title><description><![CDATA[A list of topics that have been tagged with howto]]></description><link>https://forum.androidiani.net/tags/howto</link><generator>RSS for Node</generator><lastBuildDate>Fri, 01 May 2026 00:36:53 GMT</lastBuildDate><atom:link href="https://forum.androidiani.net/tags/howto.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 29 Apr 2026 15:53:47 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Come strutturare un’applicazione ASP.NET Core in crescita: dal monolite a strati ai vertical slice]]></title><description><![CDATA[Quando un’applicazione ASP.NET Core è piccola, quasi qualsiasi struttura di cartelle funziona. Controller in una cartella, servizi in un’altra, repository da qualche altra parte. Per un po’ va bene. Poi l’applicazione cresce, le funzionalità si moltiplicano e le regole di business si diffondono per tutta la codebase. Ogni modifica tocca cinque o sei file in posti diversi. I nuovi sviluppatori hanno bisogno di una guida turistica solo per capire dove si trova qualcosa.Quel momento è il punto in cui la struttura smette di essere una scelta cosmetica e diventa un problema di manutenibilità. In questo articolo esaminiamo le opzioni più comuni — Feature Folders, architettura a strati, Clean Architecture, Vertical Slices e Modular Monolith — con un’ottica pratica su quando e perché usarle.L’obiettivo reale non è l'”architettura perfetta”Prima di confrontare i pattern, è utile chiarire l’obiettivo. Non si tratta di rendere il progetto architetturalmente impressionante. Si tratta di rendere più facile:capire dove appartiene il codicemodificare una funzionalità senza rompere funzionalità non correlateinserire nuovi sviluppatori più velocementetestare il comportamento importante con meno attritofar evolvere la struttura man mano che il sistema cresceLa risposta giusta è solitamente quella che crea meno confusione per i prossimi 12-24 mesi di sviluppo, non quella che vince i dibattiti architetturali.La testabilità è una questione di architetturaUno dei controlli pratici più importanti è questo: possiamo verificare il comportamento di business importante con unit test veloci, senza avviare l’intera applicazione o un database reale? Se la risposta è no, l’attrito architetturale si manifesta come feedback lento, modifiche fragili e paura di fare refactoring.Una buona struttura migliora la testabilità rendendo esplicite le dipendenze e mantenendo le regole di business lontane dai dettagli del framework e dell’infrastruttura — cose come accesso al database, gestione HTTP, file system e chiamate a servizi esterni. Una regola pratica:Unit test per le decisioni di business e gli invarianti del dominioIntegration test per database, messaging e wiring HTTPEnd-to-end test per i percorsi utente criticiFeature FoldersI Feature Folders organizzano il codice per capacità di business invece che per tipo tecnico. Invece della struttura classica orizzontale:Controllers/ Services/ Repositories/ Models/ si passa a una struttura verticale per funzionalità:Features/   Orders/     Create/       CreateOrderEndpoint.cs       CreateOrderRequest.cs       CreateOrderHandler.cs     GetById/       GetOrderByIdEndpoint.cs       GetOrderByIdHandler.cs   Products/     List/       ListProductsEndpoint.cs       ListProductsHandler.cs Il principio guida è semplice: se devi modificare la funzionalità “Orders”, la maggior parte del codice che ti serve dovrebbe trovarsi da qualche parte sotto la cartella Orders. Questo riduce drasticamente il tempo di ricerca e la probabilità di modifiche accidentali a funzionalità non correlate.Adatto quando: l’applicazione sta crescendo oltre il CRUD basilare, il team vuole una chiara ownership per funzionalità, gli sviluppatori sono stanchi di saltare tra strati orizzontali per fare una piccola modifica.Attenzione: se applicati in modo disordinato, i Feature Folders possono diventare inconsistenti e trasformarsi in “un’altra convenzione di cartelle”.Architettura a strati (Layered Architecture)L’architettura a strati è la classica separazione in UI, logica di business e accesso ai dati:Web/ Application/ Domain/ Infrastructure/ Esiste da decenni proprio perché è facile da spiegare, facile da insegnare e fornisce una separazione delle responsabilità immediata. Per i team che vengono da tutorial e applicazioni di esempio, è spesso il punto di partenza più familiare.Un dettaglio pratico per .NET moderno: non è sempre necessario un layer repository separato, soprattutto se Entity Framework Core fornisce già l’astrazione necessaria per l’accesso ai dati semplice. Creare repository per “rispettare la struttura” piuttosto che per risolvere un problema reale è una delle trappole comuni.Adatto quando: il team è relativamente piccolo, l’applicazione non è ancora molto complessa, gli sviluppatori traggono vantaggio da una struttura familiare, la codebase è principalmente transazionale e CRUD-oriented.Attenzione: una modifica a una funzionalità richiede spesso modifiche su più strati. La logica di business può frammentarsi. Gli sviluppatori iniziano a creare astrazioni perché la struttura le richiede, non perché il problema ne ha bisogno.Clean ArchitectureClean Architecture pone forte enfasi sui confini tra logica di dominio e dettagli dell’infrastruttura. Il principio centrale è valido: le regole di business non dovrebbero essere strettamente accoppiate a database, web framework, message broker o SDK esterni.In pratica, però, alcuni team spingono Clean Architecture così lontano che ogni caso d’uso viene sepolto sotto strati di interfacce, wrapper, handler, repository e adattatori che il sistema non ha realmente bisogno. Il takeaway più importante non è il template completo, ma il principio: tieni le regole di business lontane dall’infrastruttura tecnica.// Esempio: un handler di dominio che NON dipende da EF Core direttamente public class CreateOrderHandler {     private readonly IOrderRepository _repository;  // astrazione, non EF diretto     private readonly IEventPublisher _events;      public CreateOrderHandler(IOrderRepository repository, IEventPublisher events)     {         _repository = repository;         _events = events;     }      public async Task&lt;OrderId&gt; Handle(CreateOrderCommand command, CancellationToken ct)     {         var order = Order.Create(command.CustomerId, command.Items);         await _repository.SaveAsync(order, ct);         await _events.PublishAsync(new OrderCreated(order.Id), ct);         return order.Id;     } } Adatto quando: il dominio ha una complessità significativa, l’applicazione ha una lunga vita prevista, più infrastrutture devono rimanere scambiabili o isolate, il team ha la disciplina per usare i confini intenzionalmente.Attenzione: è facile over-engineerare. Troppa cerimonia rallenta il lavoro su funzionalità semplici. I team inesperti spesso copiano diagrammi invece di risolvere il vero problema di manutenibilità.Vertical Slice ArchitectureL’architettura a vertical slice organizza il codice attorno a singoli casi d’uso o richieste. Invece di pensare per layer tecnici, ogni “slice” è un percorso verticale completo dalla richiesta alla risposta:Features/   PlaceOrder/     PlaceOrderCommand.cs     PlaceOrderHandler.cs     PlaceOrderValidator.cs     PlaceOrderEndpoint.cs   CancelOrder/     CancelOrderCommand.cs     CancelOrderHandler.cs     CancelOrderEndpoint.cs Ogni slice è autonoma e contiene tutto il necessario per gestire quella specifica operazione. Questo riduce l’accoppiamento tra funzionalità diverse: modificare “PlaceOrder” non richiede di toccare il codice di “CancelOrder”.MediatR è comunemente usato con questo pattern in .NET, ma non è obbligatorio — il pattern funziona anche con endpoint minimali diretti.Adatto quando: le funzionalità sono relativamente indipendenti tra loro, il team preferisce massimizzare la coesione per caso d’uso, si vuole limitare al minimo l’accoppiamento laterale.Attenzione: la duplicazione del codice tra slice simili può crescere se non si definisce chiaramente cosa è condiviso e cosa non lo è.Modular MonolithIl modular monolith è uno step successivo rispetto ai pattern precedenti: invece di organizzare il codice per funzionalità singole, si definiscono moduli di business più ampi con confini chiari tra loro, pur rimanendo un’unica applicazione deployabile.Modules/   Ordering/     Api/     Application/     Domain/     Infrastructure/   Catalog/     Api/     Application/     Domain/     Infrastructure/   Payments/     ... Ogni modulo espone un’interfaccia pubblica e nasconde i propri dettagli interni. La comunicazione tra moduli avviene attraverso quella interfaccia — mai direttamente tra le implementazioni interne. Questo crea i presupposti per un eventuale passaggio a microservizi, se e quando il sistema lo richiederà, senza dover fare un refactoring massiccio.Adatto quando: il sistema è abbastanza grande da giustificare confini chiari tra aree di business, non si vuole la complessità operativa dei microservizi, si vuole prepararsi a una futura decomposizione senza impegnarsi subito.Quale scegliere?Non esiste una risposta universale, ma questo schema può orientare la scelta:App nuova, team piccolo, CRUD dominante → Layered o Feature FoldersApp in crescita, molte funzionalità indipendenti → Feature Folders o Vertical SlicesDominio complesso, lunga vita prevista, team disciplinato → Clean ArchitectureSistema grande, confini di business chiari, no microservizi ancora → Modular MonolithInizia dalla struttura più semplice che risolve il tuo problema attuale. Evolvi quando la complessità del sistema lo giustifica, non prima. Il momento migliore per passare a un’architettura più sofisticata è quando il dolore del non averla è reale e misurabile — non anticipatorio.Fonte: ASP.NET: How to Structure a Growing Application So It Stays Maintainable — Chris Pietschmann, pietschsoft.com.]]></description><link>https://forum.androidiani.net/topic/26decad8-f135-4f5c-9328-7bbdf366198c/come-strutturare-un-applicazione-asp.net-core-in-crescita-dal-monolite-a-strati-ai-vertical-slice</link><guid isPermaLink="true">https://forum.androidiani.net/topic/26decad8-f135-4f5c-9328-7bbdf366198c/come-strutturare-un-applicazione-asp.net-core-in-crescita-dal-monolite-a-strati-ai-vertical-slice</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Wed, 29 Apr 2026 15:53:47 GMT</pubDate></item><item><title><![CDATA[LangChain.js per sviluppatori JavaScript: corso gratuito per costruire agenti AI]]></title><description><![CDATA[Volete costruire agenti AI con JavaScript che vadano oltre le semplici chat? Agenti che ragionano, chiamano strumenti esterni e interrogano knowledge base in modo autonomo? Microsoft ha pubblicato un corso gratuito e open source per fare esattamente questo: LangChain.js for Beginners, 8 capitoli con oltre 70 esempi TypeScript eseguibili.Se già conoscete Node.js, npm, TypeScript e async/await, non avete bisogno di passare a Python per sviluppare applicazioni AI. LangChain.js vi mette a disposizione i componenti necessari — chat model, tool, agenti, retrieval e molto altro — senza dover cablare tutto da zero.Perché LangChain.js?LangChain.js è come avere un negozio di ferramenta completamente fornito a portata di mano. Invece di fabbricare ogni strumento dal metallo grezzo, prendete quello che serve dallo scaffale e iniziate a costruire. La libreria astrae l’integrazione con vari LLM (OpenAI, Azure OpenAI, Anthropic e altri), standardizza l’interfaccia per tool e agenti, e fornisce primitive per la costruzione di pipeline RAG.Il vantaggio rispetto a Python? Zero friction per chi già lavora nell’ecosistema JavaScript/TypeScript. Stessi strumenti di build, stesso toolchain CI/CD, stessa base di codice.Struttura del corso: un approccio agent-firstLa maggior parte dei tutorial su LangChain inizia con document loader ed embedding. Questo corso arriva agli agenti e ai tool presto, perché è lì che si trovano i sistemi AI in produzione. Gli agenti decidono cosa fare, quando usare strumenti, e se hanno bisogno di cercare dati.Capitoli 1-3: fondamentaLa prima chiamata a un LLM, chat model, streaming, prompt template e output strutturati con schemi Zod. Contenuto classico, ma necessario prima che le cose si facciano interessanti.Capitolo 4: Function Calling e ToolQui l’AI smette di parlare e inizia a fare. Si insegna al modello a chiamare funzioni personalizzate, e lui ragiona su quando usarle. Esempio pratico:import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const weatherTool = tool(
  async ({ city }) =&gt; {
    // chiamata a una API meteo reale
    return `Il meteo a ${city} è soleggiato, 22°C`;
  },
  {
    name: "get_weather",
    description: "Ottieni le condizioni meteo correnti per una città",
    schema: z.object({
      city: z.string().describe("Il nome della città"),
    }),
  }
);

const model = new ChatOpenAI({ model: "gpt-4o" });
const modelWithTools = model.bindTools([weatherTool]);

const result = await modelWithTools.invoke(
  "Che tempo fa a Milano?"
);
console.log(result.tool_calls);Capitolo 5: Agenti con pattern ReActUn LLM risponde a domande. Un agente ragiona attraverso i problemi, sceglie i tool giusti ed esegue piani multi-step. Il capitolo 5 mostra come costruire agenti con il pattern ReAct (Reason + Act): il modello alterna tra pensiero esplicito e azioni concrete fino a raggiungere la risposta finale.import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";

const agent = createReactAgent({
  llm: new ChatOpenAI({ model: "gpt-4o" }),
  tools: [weatherTool, searchTool, calculatorTool],
});

const response = await agent.invoke({
  messages: [{ role: "user", content: "Pianifica un itinerario a Roma per domani" }],
});
console.log(response.messages.at(-1)?.content);Capitolo 6: MCP — Model Context ProtocolIl Model Context Protocol sta diventando lo standard per connettere l’AI a servizi esterni. Il capitolo guida alla costruzione di server MCP e al collegamento degli agenti tramite trasporti HTTP e stdio. È un’abilità sempre più richiesta man mano che l’ecosistema AI aziendale matura.Capitoli 7 e 8: RAG agentivoI capitoli finali portano documenti, embedding e ricerca semantica, poi combinano tutto in Agentic RAG. L’agente decide quando cercare nella knowledge base e quando rispondere direttamente da ciò che già conosce.È una distinzione importante. Il RAG tradizionale è come lo studente che sfoglia il libro di testo per ogni domanda, anche “Quanto fa 2+2?”. Il RAG agentivo è lo studente intelligente che risponde alle domande semplici a memoria e apre il libro solo quando ne ha davvero bisogno. Il risultato: risposte più veloci, costi inferiori (meno ricerche di embedding non necessarie) e un’esperienza complessivamente migliore.Come iniziareIl corso è open source su GitHub. Per iniziare bastano tre comandi:# Clona il repository
git clone https://github.com/microsoft/generative-ai-with-javascript

# Installa le dipendenze
cd generative-ai-with-javascript/lessons/08-langchain/
npm install

# Configura la chiave API
cp .env.example .env
# Aggiungi AZURE_OPENAI_API_KEY o OPENAI_API_KEY nel file .env

# Esegui il primo esempio
npx ts-node chapter1/01-first-llm-call.tsOgni capitolo include spiegazioni concettuali con analogie concrete, esempi di codice eseguibili immediatamente, sfide pratiche per testare la comprensione e punti chiave per consolidare l’apprendimento.A chi è rivoltoIl corso si rivolge a sviluppatori JavaScript/TypeScript che conoscono npm install e async/await. Non è richiesta esperienza precedente in AI o machine learning. Ogni capitolo inizia con un’analogia del mondo reale per ancorare il concetto prima di qualsiasi codice.Per chi già lavora su applicazioni .NET o backend e vuole esplorare il lato AI senza cambiare linguaggio, questo corso rappresenta il punto di ingresso ideale: nessun boilerplate Python, nessun ambiente virtuale da gestire, solo TypeScript e strumenti già familiari.Considerazioni finaliLangChain.js ha raggiunto una maturità che lo rende adatto a progetti di produzione. Il corso di Microsoft colma un gap reale: la maggior parte della documentazione e dei tutorial sull’AI generativa è orientata a Python. Avere un percorso strutturato, gratuito e orientato agli agenti per l’ecosistema JavaScript è un vantaggio concreto per chi già lavora in questo stack.Se state valutando come integrare capacità AI nelle vostre applicazioni Node.js o Deno, o se volete costruire un copilota interno per il vostro team, questo è il punto di partenza più pragmatico disponibile oggi.Fonte originale: LangChain.js for Beginners: A Free Course to Build Agentic AI Apps with JavaScript — Microsoft for Developers]]></description><link>https://forum.androidiani.net/topic/d6b3b4f8-f64b-4308-8c95-6ce062a6f12c/langchain.js-per-sviluppatori-javascript-corso-gratuito-per-costruire-agenti-ai</link><guid isPermaLink="true">https://forum.androidiani.net/topic/d6b3b4f8-f64b-4308-8c95-6ce062a6f12c/langchain.js-per-sviluppatori-javascript-corso-gratuito-per-costruire-agenti-ai</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Mon, 27 Apr 2026 11:00:43 GMT</pubDate></item><item><title><![CDATA[State Pattern in C#: guida decisionale con esempi pratici]]></title><description><![CDATA[Gli oggetti cambiano comportamento in base al loro stato interno continuamente. Un documento passa da bozza a revisione a pubblicato. Un personaggio di un gioco alterna tra inattivo, in corsa e in attacco. Un pagamento transita da pending ad autorizzato a catturato. La domanda quando usare lo State Pattern in C# emerge nel momento in cui la logica condizionale inizia a ramificarsi sullo stesso campo stato in metodo dopo metodo — e ogni nuovo stato obbliga a toccare più file.In questo articolo troverete una guida decisionale pratica per il pattern State in C#: quando merita davvero il suo posto e quando soluzioni più semplici — enum o flag booleani — bastano e avanzano.Cos’è lo State Pattern e cosa fa davveroLo State Pattern consente a un oggetto di cambiare il proprio comportamento quando il suo stato interno cambia. Dall’esterno sembra che l’oggetto abbia cambiato classe. Invece di sparpagliare istruzioni if e switch in ogni metodo che dipende dallo stato corrente, si incapsula il comportamento di ciascuno stato in una propria classe. L’oggetto delega al state object attivo in quel momento.La struttura coinvolge tre ruoli:Context — mantiene un riferimento allo state object corrente e vi delega il comportamentoState interface — dichiara i metodi che ogni stato deve implementareConcrete state classes — implementano l’interfaccia con il comportamento specifico del loro statoQuando avviene una transizione, il context sostituisce il riferimento al proprio state object. Questo approccio elimina i blocchi condizionali che crescono ogni volta che si aggiunge uno stato nuovo.Segnali che indicano che avete bisogno dello State PatternNon ogni oggetto con un campo status ha bisogno dello State Pattern. Tuttavia certi code smell sono segnali forti che il pattern ripulirà il design.La logica condizionale si ramifica sullo stesso stato ovunqueQuesto è il trigger principale. Quando vedete lo stesso switch o if-else che controlla _status in tre, quattro o dieci metodi diversi, il vostro oggetto sta gestendo le transizioni di stato nel modo più difficile. Ogni nuovo stato significa toccare ciascuno di quei metodi.Avete regole di transizione complesseSe le transizioni valide dipendono dallo stato attuale — e alcune azioni devono lanciare eccezioni o essere semplicemente ignorate a seconda di dove ci si trova — il pattern State rende queste regole esplicite invece di affogarle in condizionali.Ogni stato ha un comportamento distintoQuando lo stato influenza come si comporta un metodo, non soltanto se eseguirlo, il pattern vale l’investimento. Ciascuna classe stato diventa un luogo coeso che racchiude tutto il comportamento per quella condizione.Scenario 1: gestione degli ordini (comportamento condizionale complesso)Ecco un esempio di elaborazione ordini con lo State Pattern:public interface IOrderState {     void Submit(OrderContext context);     void Cancel(OrderContext context);     void Ship(OrderContext context);     void Deliver(OrderContext context); }  public sealed class OrderContext {     public IOrderState CurrentState { get; private set; }     public string OrderId { get; }      public OrderContext(string orderId)     {         OrderId = orderId;         CurrentState = new PendingState();     }      public void TransitionTo(IOrderState state)     {         Console.WriteLine(             $"Order {OrderId}: " +             $"{CurrentState.GetType().Name} -&gt; " +             $"{state.GetType().Name}");         CurrentState = state;     }      public void Submit() =&gt; CurrentState.Submit(this);     public void Cancel() =&gt; CurrentState.Cancel(this);     public void Ship()   =&gt; CurrentState.Ship(this);     public void Deliver() =&gt; CurrentState.Deliver(this); }  public sealed class PendingState : IOrderState {     public void Submit(OrderContext context)     {         Console.WriteLine("Ordine inviato per elaborazione.");         context.TransitionTo(new ProcessingState());     }      public void Cancel(OrderContext context)     {         Console.WriteLine("Ordine annullato prima dell'invio.");         context.TransitionTo(new CancelledState());     }      public void Ship(OrderContext context) =&gt;         throw new InvalidOperationException("Impossibile spedire un ordine in attesa.");      public void Deliver(OrderContext context) =&gt;         throw new InvalidOperationException("Impossibile consegnare un ordine in attesa."); }Notate come PendingState sappia esattamente cosa fare (o non fare) per ogni azione. Non c’è nessuno switch nello stato: il polimorfismo gestisce tutto.Scenario 2: workflow e gestione dei processiUn caso d’uso classico è la gestione di una domanda con audit trail integrato:public interface IApplicationState {     string StatusName { get; }     void Review(ApplicationContext context);     void Approve(ApplicationContext context);     void Reject(ApplicationContext context);     void RequestInfo(ApplicationContext context); }  public sealed class ApplicationContext {     public IApplicationState CurrentState { get; private set; }     public string ApplicantName { get; }     public List&lt;string&gt; AuditLog { get; } = new();      public ApplicationContext(string applicantName)     {         ApplicantName = applicantName;         CurrentState = new SubmittedState();         Log("Domanda inviata");     }      public void TransitionTo(IApplicationState state)     {         Log($"Transizione a {state.StatusName}");         CurrentState = state;     }      public void Log(string message) =&gt;         AuditLog.Add($"[{DateTime.UtcNow:u}] {message}"); }L’audit trail viene aggiornato automaticamente a ogni transizione, senza duplicazione di codice nei metodi chiamanti.Scenario 3: gestione dello stato di un personaggio in un giocoI giochi sono un esempio naturale: un personaggio che alterna tra IdleState, RunningState, AttackingState e DyingState beneficia enormemente di questo pattern, poiché ciascuno stato ha logica di input e di update completamente diversa.Quando NON usare lo State PatternIl pattern non è sempre la risposta giusta. Evitate di applicarlo nei seguenti casi:Stati booleani semplici: se l’oggetto ha solo attivo e inattivo, un campo booleano è più chiaro e diretto.Pochi stati senza transizioni significative: se avete due o tre stati con poco comportamento differenziato, gli enum bastano.La logica di transizione è esterna all’oggetto: se le decisioni di cambio stato appartengono a un orchestratore esterno, il pattern State aggiunge complessità senza benefici.State Pattern vs alternative: quando scegliere cosaUn enum con un switch è la scelta giusta quando gli stati sono pochi, stabili e il comportamento differisce solo su una o due dimensioni. Appena gli stati crescono, il comportamento diverge significativamente per metodo, o le transizioni diventano complesse, è il momento di passare al pattern State.Il pattern State non è la stessa cosa del pattern Strategy: Strategy cambia l’algoritmo usato per una singola operazione, mentre State cambia il comportamento complessivo dell’oggetto al variare della condizione interna. Possono tuttavia lavorare insieme: una transizione di stato può emettere eventi che degli Observer gestiscono.Integrazione con Dependency InjectionUna domanda comune è se il pattern State si integri bene con la DI di ASP.NET Core. La risposta è sì, con qualche accorgimento: le classi stato concrete possono essere registrate nel contenitore DI, ma è consigliabile usare factory o ActivatorUtilities.CreateInstance per creare le istanze in modo da evitare cicli nel contenitore.ConclusioneLo State Pattern in C# risolve un problema preciso: oggetti il cui comportamento cambia radicalmente al variare dello stato interno, con transizioni complesse e comportamento specifico per stato. Prima di applicarlo, fate questa verifica rapida: contate quante volte controllate lo stesso campo di stato in metodi diversi. Se la risposta supera tre o quattro, probabilmente il pattern vi risparmierà mesi di manutenzione futura.La regola d’oro rimane: preferite sempre la soluzione più semplice che risolve il problema. Un enum con uno switch è più leggibile di una gerarchia di classi per casi banali. Ma quando la complessità cresce, lo State Pattern offre un’architettura che scala senza sforzo.Fonte originale: When to Use State Pattern in C#: Decision Guide with Examples — Dev Leader]]></description><link>https://forum.androidiani.net/topic/8b11c7c5-9756-4b50-acf6-984e1c99b5ae/state-pattern-in-c-guida-decisionale-con-esempi-pratici</link><guid isPermaLink="true">https://forum.androidiani.net/topic/8b11c7c5-9756-4b50-acf6-984e1c99b5ae/state-pattern-in-c-guida-decisionale-con-esempi-pratici</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Sun, 26 Apr 2026 18:13:43 GMT</pubDate></item><item><title><![CDATA[Azure Developer CLI: scrivere gli hook in Python, TypeScript e .NET]]></title><description><![CDATA[L’Azure Developer CLI (azd) è lo strumento open-source di Microsoft pensato per accompagnare lo sviluppatore dall’ambiente locale fino al deployment su Azure. Tra le sue funzionalità più apprezzate, il sistema degli hook permette di iniettare logica personalizzata nei punti chiave del ciclo di vita: prima del provisioning, dopo il deployment, e così via. Fino a poco tempo fa, però, questa logica doveva essere scritta esclusivamente in Bash o PowerShell, costringendo chi lavora in Python o TypeScript a un cambio di contesto non sempre gradito.Con il rilascio più recente di azd, questo limite è stato rimosso: gli hook possono ora essere scritti in Python, JavaScript, TypeScript e .NET, oltre ai già supportati Bash e PowerShell. La selezione del linguaggio avviene automaticamente in base all’estensione del file, senza configurazione aggiuntiva.Cosa sono gli hook in azdGli hook sono script eseguiti automaticamente da azd in corrispondenza di eventi specifici nel workflow:preprovision: prima che venga eseguito il provisioning dell’infrastrutturapostprovision: dopo il provisioningpredeploy / postdeploy: prima e dopo il deployment dell’applicazioneSi definiscono nel file azure.yaml con il blocco hooks:, specificando il percorso allo script da eseguire. azd si occupa autonomamente di rilevare il runtime appropriato, installare le dipendenze e lanciare lo script.Come usare gli hook in PythonPer un hook Python, è sufficiente creare il file .py e, nella stessa directory o in una directory padre, un file requirements.txt o pyproject.toml. azd individuerà automaticamente il file di dipendenze, creerà un virtual environment e installerà i pacchetti necessari prima di eseguire lo script.Struttura tipica:hooks/
├── setup.py
└── requirements.txt
Configurazione in azure.yaml:hooks:
  preprovision:
    run: ./hooks/setup.py
È possibile personalizzare il nome del virtual environment tramite il blocco config:hooks:
  preprovision:
    run: ./hooks/setup.py
    config:
      virtualEnvName: .venv
Hook in JavaScript e TypeScriptPer gli hook JavaScript e TypeScript, azd cerca un file package.json nella stessa directory o in una directory padre. Esegue automaticamente npm install (o il package manager specificato nella configurazione) e lancia lo script.Per TypeScript, la novità più interessante è che non serve un tsconfig.json né una fase di compilazione separata: azd utilizza npx tsx per eseguire il file TypeScript direttamente.hooks/
├── seed.ts
└── package.json
hooks:
  postdeploy:
    run: ./hooks/seed.ts
    config:
      packageManager: pnpm   # npm | pnpm | yarn
Hook in .NET e C#Per i progetti .NET sono supportate due modalità distinte:Project mode: se nella directory dello script (o in una padre) è presente un file .csproj, .fsproj o .vbproj, azd esegue automaticamente dotnet restore e dotnet build.Single-file mode: a partire da .NET 10, i file .cs standalone vengono eseguiti direttamente con dotnet run script.cs, senza necessità di un progetto.hooks/
├── migrate.cs
└── migrate.csproj   # opzionale su .NET 10+
hooks:
  postprovision:
    run: ./hooks/migrate.cs
    config:
      configuration: Release   # Debug | Release
      framework: net10.0
Funzionalità avanzateOverride della directory di lavoroSe la root del progetto e la posizione dello script differiscono, si può usare il campo dir per specificare la working directory:hooks:
  preprovision:
    run: main.py
    dir: hooks/preprovision
Override esplicito del linguaggioSe l’estensione è assente o ambigua, è possibile forzare il runtime con il campo kind:hooks:
  preprovision:
    run: ./hooks/setup
    kind: python
Formato misto e override per piattaformaSi possono combinare hook in linguaggi diversi e specificare script differenti per Windows e sistemi POSIX:hooks:
  preprovision:
    run: ./hooks/setup.py
  predeploy:
    windows:
      run: ./hooks/build.ps1
    posix:
      run: ./hooks/build.sh
  postdeploy:
    run: ./hooks/seed.ts
  postprovision:
    run: ./hooks/migrate.cs
Come aggiornare azdPer assicurarsi di avere questa funzionalità disponibile, è sufficiente aggiornare azd all’ultima versione:azd update
Per una nuova installazione, è possibile seguire la guida ufficiale di installazione.ConclusioniIl supporto multi-linguaggio per gli hook di azd rappresenta un miglioramento concreto per i team che lavorano con stack tecnologici eterogenei. Non dover più mantenere script shell separati per la logica di deployment è un risparmio reale di complessità, soprattutto nei progetti .NET o Python dove gran parte della base di codice esistente può essere riutilizzata direttamente negli hook.La gestione automatica delle dipendenze (virtual env per Python, npm install per JS/TS, dotnet restore per .NET) elimina ulteriore boilerplate, rendendo l’integrazione trasparente. Chi già usa azd nel proprio workflow troverà questa novità immediatamente utile; chi non lo ha ancora esplorato può partire dalla documentazione ufficiale e dalla galleria di template.Fonte: Write azd hooks in Python, JavaScript, TypeScript, or .NET – Azure SDK Blog]]></description><link>https://forum.androidiani.net/topic/673b6796-781d-4080-b8f9-f5996d4b7836/azure-developer-cli-scrivere-gli-hook-in-python-typescript-e-.net</link><guid isPermaLink="true">https://forum.androidiani.net/topic/673b6796-781d-4080-b8f9-f5996d4b7836/azure-developer-cli-scrivere-gli-hook-in-python-typescript-e-.net</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Fri, 24 Apr 2026 09:03:20 GMT</pubDate></item><item><title><![CDATA[12 tecniche per ottimizzare le query PostgreSQL su dataset di grandi dimensioni]]></title><description><![CDATA[Quando una tabella PostgreSQL cresce oltre il milione di righe, query che prima restituivano risultati in millisecondi iniziano ad impiegare secondi — o peggio. La buona notizia è che PostgreSQL offre strumenti potenti per affrontare questo problema. La cattiva notizia è che molti sviluppatori conoscono solo una parte di questi strumenti.In questo articolo passiamo in rassegna le 12 tecniche più efficaci per ottimizzare le query su grandi dataset, con esempi SQL concreti per ciascuna.1. Creare indici sulle colonne frequentemente filtrateIl consiglio più noto, ma non per questo meno importante. Un indice trasforma una scansione sequenziale (O(n)) in una ricerca B-tree (O(log n)). La differenza su una tabella da un milione di righe può essere di due ordini di grandezza.-- Prima: full sequential scan su ordini SELECT * FROM orders WHERE customer_id = 42;  -- Creazione dell'indice CREATE INDEX idx_orders_customer_id ON orders(customer_id);  -- Dopo: index scan, da 240ms a pochi msUsate EXPLAIN ANALYZE per verificare che l’indice venga effettivamente utilizzato.2. Normalizzare il database in modo strategicoLa normalizzazione riduce la ridondanza e migliora la coerenza dei dati, ma va applicata con giudizio. Una normalizzazione eccessiva crea decine di JOIN che possono diventare colli di bottiglia. La regola pratica: normalizzate i dati che cambiano spesso o che hanno alta cardinalità (liste di prodotti, clienti, categorie), denormalizzate i dati storici o di report dove la velocità di lettura è critica.3. Evitare SELECT *Selezionare tutte le colonne ha due costi nascosti: aumenta il volume di I/O e impedisce a PostgreSQL di soddisfare la query direttamente dall’indice (index-only scan). Specificate sempre le colonne necessarie:-- Evitare SELECT * FROM orders WHERE customer_id = 42;  -- Preferire SELECT id, created_at, total_amount FROM orders WHERE customer_id = 42;Quando le colonne selezionate fanno parte di un indice composito, PostgreSQL può restituire i dati senza accedere all’heap, eliminando un intero livello di I/O.4. Ordinare i JOIN in modo efficienteIl query planner moderno di PostgreSQL determina autonomamente l’ordine ottimale dei JOIN grazie al cost-based optimizer. Tuttavia, in scenari con molte tabelle o con join_collapse_limit ridotto, conviene strutturare i JOIN in modo che le tabelle più piccole (o più filtrate) vengano processate per prime, riducendo la cardinalità delle operazioni successive.5. Usare LIMIT durante l’esplorazione dei datiApparentemente ovvio, ma spesso trascurato: se l’interfaccia utente mostra al massimo 50 risultati, non ha senso recuperarne un milione dal database.SELECT id, name, email  FROM customers  ORDER BY created_at DESC  LIMIT 50 OFFSET 0;Attenzione al pagination problem: con OFFSET elevati, PostgreSQL scansiona comunque tutte le righe precedenti. Per paginazione su grandi dataset, preferite il keyset pagination (cursor-based).6. Indici parziali per subset frequentiUn indice parziale indicizza solo le righe che soddisfano una condizione, riducendo dimensioni e costo di manutenzione:-- Indice solo sugli ordini completati (subset più frequentemente interrogato) CREATE INDEX idx_completed_orders ON orders(customer_id) WHERE status = 'Completed';  -- La query deve includere la stessa condizione per usare l'indice SELECT id, total_amount  FROM orders  WHERE customer_id = 42 AND status = 'Completed';In un test pratico, questo indice ha dimezzato i tempi rispetto a un indice standard su tutte le righe.7. Usare i tipi di dato più piccoli necessariOgni byte conta quando moltiplicato per milioni di righe. Preferite sempre il tipo più compatto che soddisfa il requisito:integer (4 byte) invece di bigint (8 byte) per chiavi primarie &lt; 2 miliardismallint (2 byte) per enumerazioni con pochi valoritimestamp invece di timestamptz se il fuso orario è fissovarchar(n) con limite appropriato invece di text illimitato dove possibileTipi più piccoli significano pagine di dati più dense, quindi meno I/O per ogni query.8. Non applicare funzioni sulle colonne indicizzateApplicare una funzione a una colonna indicizzata invalida l’utilizzo dell’indice:-- L'indice su name NON viene usato SELECT * FROM customers WHERE LOWER(name) = 'mario rossi';  -- Soluzione: creare un indice funzionale CREATE INDEX idx_customers_lower_name ON customers(LOWER(name));  -- Ora l'indice viene usato SELECT * FROM customers WHERE LOWER(name) = 'mario rossi';Lo stesso vale per funzioni su date come DATE(created_at): usate range di timestamp o create l’indice sulla funzione.9. Partizionare le tabelle molto grandiIl partizionamento divide una tabella logica in sotto-tabelle fisiche, permettendo a PostgreSQL di escludere partizioni irrilevanti (partition pruning) durante le query:-- Tabella partizionata per anno CREATE TABLE orders_partitioned (     id         serial NOT NULL,     customer_id integer,     created_at  timestamp NOT NULL,     CONSTRAINT pk_orders PRIMARY KEY (id, created_at) ) PARTITION BY RANGE (created_at);  -- Creazione delle partizioni annuali CREATE TABLE orders_2024 PARTITION OF orders_partitioned     FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');  CREATE TABLE orders_2025 PARTITION OF orders_partitioned     FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');Una query che filtra per anno legge solo la partizione corrispondente, ignorando completamente le altre.10. Usare le transazioni per operazioni bulkPostgreSQL esegue un commit (e quindi una scrittura sincrona su WAL) dopo ogni statement. Raggruppare più operazioni in un’unica transazione riduce drasticamente i costi di I/O:-- Lento: un commit per ogni INSERT INSERT INTO log_events VALUES (...); INSERT INTO log_events VALUES (...); -- ... x 10.000  -- Veloce: un solo commit per tutto il batch BEGIN; INSERT INTO log_events VALUES (...); INSERT INTO log_events VALUES (...); -- ... x 10.000 COMMIT;In test pratici, l’approccio con transazione singola completa lo stesso lavoro in meno della metà del tempo rispetto agli inserimenti individuali.11. Evitare transazioni long-runningIl modello MVCC (Multi-Version Concurrency Control) di PostgreSQL mantiene versioni multiple delle righe per garantire la consistenza delle letture. Le transazioni long-running bloccano il processo di VACUUM dal rimuovere le versioni obsolete, causando table bloat: tabelle che crescono fisicamente anche quando i dati logici non aumentano.Spezzettate le operazioni pesanti in batch più piccoli e monitorate le transazioni attive con:SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE state != 'idle' AND query_start IS NOT NULL ORDER BY duration DESC;12. Gestire il bloat con VACUUMOgni UPDATE e DELETE lascia righe “morte” sul disco. VACUUM le recupera:-- VACUUM standard: recupera spazio senza bloccare le letture VACUUM orders;  -- VACUUM FULL: recupera tutto lo spazio ma blocca l'accesso alla tabella -- Usare solo in finestre di manutenzione programmate VACUUM FULL orders;  -- Verificare lo stato del bloat SELECT relname, n_dead_tup, n_live_tup,        round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_pct FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;Per la maggior parte dei workload, autovacuum è sufficiente. Assicuratevi che sia abilitato e calibrate i threshold in base al volume di modifiche della vostra applicazione:-- Verificare la configurazione autovacuum per una tabella specifica SELECT reloptions FROM pg_class WHERE relname = 'orders';Riepilogo operativoNon tutte le tecniche si applicano a ogni scenario. Un approccio efficace inizia sempre dall’analisi con EXPLAIN (ANALYZE, BUFFERS) per identificare i reali colli di bottiglia, poi applica le ottimizzazioni in modo mirato. L’indice sbagliato o il partizionamento mal configurato possono peggiorare le prestazioni invece di migliorarle.Il punto di partenza universale resta lo stesso: misurare prima, ottimizzare dopo.Fonte: 12 practices for optimizing PostgreSQL queries for large datasets — elmah.io Blog]]></description><link>https://forum.androidiani.net/topic/32794ed8-7496-404a-bdc8-836d65bc5f68/12-tecniche-per-ottimizzare-le-query-postgresql-su-dataset-di-grandi-dimensioni</link><guid isPermaLink="true">https://forum.androidiani.net/topic/32794ed8-7496-404a-bdc8-836d65bc5f68/12-tecniche-per-ottimizzare-le-query-postgresql-su-dataset-di-grandi-dimensioni</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Wed, 22 Apr 2026 15:21:43 GMT</pubDate></item><item><title><![CDATA[Command Pattern in C#: guida completa con undo, redo e Dependency Injection]]></title><description><![CDATA[Il Command Pattern è uno dei design pattern comportamentali più utili nel mondo dello sviluppo software. In C#, la sua implementazione è particolarmente elegante e consente di costruire sistemi robusti con funzionalità di undo/redo, accodamento di operazioni e logging senza intaccare la logica di business. In questo articolo vedremo come implementarlo passo dopo passo, con esempi concreti e consigli pratici per applicazioni reali.Cos’è il Command Pattern?Il Command Pattern incapsula una richiesta come un oggetto autonomo, separando chi la emette da chi la esegue. Questo consente di parametrizzare i client con richieste diverse, accodare operazioni, supportare il rollback e costruire sistemi di macro o audit trail. In parole semplici: invece di chiamare direttamente un metodo, si crea un oggetto che “sa come” eseguire quella chiamata, e lo si passa all’esecutore.I casi d’uso più classici includono:Editor di testo o grafica con undo/redo illimitatoSistemi di workflow con operazioni reversibiliTransaction scripts in architetture DDDLogging e auditing di operazioni criticheMacro recording nelle applicazioni di produttivitàI componenti del patternUn’implementazione corretta del Command Pattern in C# prevede quattro attori principali:ICommand: l’interfaccia contrattuale che definisce Execute(), Undo() e una proprietà descrittivaReceiver: la classe che contiene la logica di business effettiva, ignara del patternConcrete Command: le implementazioni di ICommand, che catturano i parametri in costruzione e delegano al ReceiverInvoker: gestisce la coda di esecuzione e le stack di undo/redoImplementazione passo dopo passoStep 1: definire l’interfaccia ICommandIl contratto deve essere minimale. Tutti i dati necessari all’esecuzione viaggiano dentro il comando stesso, non vengono passati ai metodi:public interface ICommand {     string Description { get; }     void Execute();     void Undo(); }La scelta di metodi senza parametri è deliberata: favorisce l’immutabilità del comando dopo la costruzione e impedisce il condizionamento da stato esterno.Step 2: creare il ReceiverIl Receiver contiene la logica reale. Non sa nulla di comandi, stack o undo. È testabile in isolamento:public class TextDocument {     private readonly List&lt;string&gt; _lines = new();      public IReadOnlyList&lt;string&gt; Lines =&gt; _lines.AsReadOnly();      public void AddLine(string text) =&gt; _lines.Add(text);      public void RemoveLine(int index)     {         if (index &gt;= 0 &amp;&amp; index &lt; _lines.Count)             _lines.RemoveAt(index);     } }Step 3: implementare i Concrete CommandsOgni comando cattura i propri dati al momento della costruzione e implementa sia Execute che Undo. Notare che lo stato necessario per l’undo viene salvato durante Execute:public sealed class AddLineCommand : ICommand {     private readonly TextDocument _document;     private readonly string _text;      public string Description =&gt; $"Aggiungi riga: "{_text}"";      public AddLineCommand(TextDocument document, string text)     {         _document = document ?? throw new ArgumentNullException(nameof(document));         _text = text ?? throw new ArgumentNullException(nameof(text));     }      public void Execute() =&gt; _document.AddLine(_text);      public void Undo() =&gt; _document.RemoveLine(_document.Lines.Count - 1); }  public sealed class RemoveLineCommand : ICommand {     private readonly TextDocument _document;     private readonly int _index;     private string? _removedText;      public string Description =&gt; $"Rimuovi riga {_index}";      public RemoveLineCommand(TextDocument document, int index)     {         _document = document;         _index = index;     }      public void Execute()     {         // Salviamo lo stato per poter fare undo         _removedText = _document.Lines[_index];         _document.RemoveLine(_index);     }      public void Undo()     {         if (_removedText is not null)             _document.AddLine(_removedText);     } }Il punto critico è la cattura dello snapshot in Execute(): RemoveLineCommand ricorda il testo rimosso prima di cancellarlo, rendendo possibile il ripristino.Step 4: costruire l’Invoker con historyL’Invoker mantiene due stack: uno per l’undo e uno per il redo. Quando si esegue un nuovo comando, la redo stack viene svuotata per evitare storie ramificate incoerenti:public class CommandInvoker {     private readonly Stack&lt;ICommand&gt; _undoStack = new();     private readonly Stack&lt;ICommand&gt; _redoStack = new();      public void ExecuteCommand(ICommand command)     {         command.Execute();         _undoStack.Push(command);         _redoStack.Clear(); // Nuova azione invalida il redo     }      public bool CanUndo =&gt; _undoStack.Count &gt; 0;     public bool CanRedo =&gt; _redoStack.Count &gt; 0;      public void Undo()     {         if (!CanUndo) return;         var cmd = _undoStack.Pop();         cmd.Undo();         _redoStack.Push(cmd);     }      public void Redo()     {         if (!CanRedo) return;         var cmd = _redoStack.Pop();         cmd.Execute();         _undoStack.Push(cmd);     }      public IEnumerable&lt;string&gt; GetHistory() =&gt;         _undoStack.Select(c =&gt; c.Description); }Step 5: Macro Commands (Composite)Il Command Pattern si combina naturalmente con il Composite Pattern per costruire macro che raggruppano più operazioni atomiche:public sealed class MacroCommand : ICommand {     private readonly IReadOnlyList&lt;ICommand&gt; _commands;      public string Description =&gt; $"Macro ({_commands.Count} operazioni)";      public MacroCommand(IEnumerable&lt;ICommand&gt; commands)     {         _commands = commands.ToList();     }      public void Execute()     {         foreach (var cmd in _commands)             cmd.Execute();     }      public void Undo()     {         // L'undo avviene in ordine inverso         for (int i = _commands.Count - 1; i &gt;= 0; i--)             _commands[i].Undo();     } }Step 6: integrazione con Dependency InjectionIn applicazioni .NET moderne, il pattern si integra perfettamente con il DI container di ASP.NET Core:// Program.cs builder.Services.AddSingleton&lt;TextDocument&gt;(); builder.Services.AddSingleton&lt;CommandInvoker&gt;(); builder.Services.AddTransient&lt;DocumentCommandFactory&gt;();  // Factory per creare comandi on-demand public class DocumentCommandFactory {     private readonly TextDocument _document;      public DocumentCommandFactory(TextDocument document)     {         _document = document;     }      public ICommand AddLine(string text) =&gt; new AddLineCommand(_document, text);     public ICommand RemoveLine(int index) =&gt; new RemoveLineCommand(_document, index); }I componenti longevi (TextDocument, CommandInvoker) sono Singleton; i comandi vengono creati on-demand tramite factory e rimangono short-lived.Errori comuni da evitareChi si avvicina al Command Pattern per la prima volta tende a commettere questi errori:Logica nel comando invece che nel Receiver: i comandi devono orchestrare, non implementare. La logica di business appartiene al Receiver, dove può essere testata in isolamento.Mancato snapshot dello stato per l’undo: se dimenticate di catturare lo stato prima dell’esecuzione, l’undo diventa impossibile o inaffidabile.Stato condiviso tra comandi: ogni comando deve essere autosufficiente. Lo stato mutabile condiviso porta a bug sottili quando i comandi vengono eseguiti in ordine diverso.Undo implementato in modo forzato: per operazioni genuinamente non reversibili (come l’invio di un’email), meglio lanciare un’eccezione o documentare esplicitamente il limite, piuttosto che fingere un undo inesistente.ConclusioneIl Command Pattern è uno strumento potente e sottovalutato. Quando il vostro sistema ha bisogno di storico operazioni, undo/redo, accodamento differito o audit trail, questo pattern vi offre una soluzione elegante e testabile. La separazione netta tra Receiver (logica) e Command (orchestrazione) rende il codice mantenibile anche su larga scala.Se state sviluppando con C# e .NET, consideratelo ogni volta che la vostra architettura richiede operazioni reversibili o la composizione di azioni complesse.Fonte originale: How to Implement Command Pattern in C# – DevLeader.ca]]></description><link>https://forum.androidiani.net/topic/a3f374d1-8259-452e-9bcb-2ae70c1f51dc/command-pattern-in-c-guida-completa-con-undo-redo-e-dependency-injection</link><guid isPermaLink="true">https://forum.androidiani.net/topic/a3f374d1-8259-452e-9bcb-2ae70c1f51dc/command-pattern-in-c-guida-completa-con-undo-redo-e-dependency-injection</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Sun, 19 Apr 2026 09:12:09 GMT</pubDate></item><item><title><![CDATA[Agent Skills in .NET: tre paradigmi di composizione per gli agenti AI]]></title><description><![CDATA[Introduzione: L’evoluzione dei skill negli agent .NETGli agent AI richiedono un modo flessibile e modulare di estendere le loro capacità: questo è il ruolo dei skill. Con il framework Agent di Microsoft per .NET, gli sviluppatori dispongono di tre paradigmi complementari per definire e comporre skill, permettendo ai team di scegliere l’approccio più adatto al loro contesto.I tre paradigmi per creare skill1. Skill basati su file (File-Based Skills)L’approccio più dichiarativo parte da una struttura di directory semplice. Ogni skill è organizzato come una cartella contenente:Un file SKILL.md con metadati nel frontmatter YAMLUna sottocartella opzionale scripts/ con il codice eseguibileUna sottocartella opzionale references/ con documentazione di supportoQuesto paradigma è particolarmente vantaggioso per i team che vogliono gestire i skill come assets indipendenti dentro un repository condiviso. Il caricamento è automatico: l’agent scopre e carica i skill quando l’utente ne fa richiesta.Ecco come si registra un provider file-based:var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    SubprocessScriptRunner.RunAsync);
Il vantaggio decisivo è la separazione tra definizione del skill e implementazione. Non è necessario riconfigurare il codice C# per aggiungere nuovi skill; basta creare una nuova directory.2. Skill basati su classe (Class-Based Skills)Per chi preferisce la sicurezza dei tipi e il supporto IDE completo, gli skill basati su classe offrono un’alternativa fortemente tipizzata. Si eredita da AgentClassSkill&lt;T&gt; e si usano attributi di reflection per marcare le risorse e gli script:public sealed class BenefitsEnrollmentSkill : AgentClassSkill&lt;BenefitsEnrollmentSkill&gt;
{
    [AgentSkillResource("available-plans")]
    public string AvailablePlans =&gt; "Plan A, Plan B, Plan C...";
    
    [AgentSkillScript("enroll")]
    private static string Enroll(string employeeId, string planCode)
    {
        // Logica di iscrizione
        return $"Iscrizione di {employeeId} al piano {planCode} completata";
    }
}
Questo approccio è ideale per skill complessi che richiedono logica C# sofisticata. Gli attributi [AgentSkillResource] e [AgentSkillScript] permettono al framework di scoprire automaticamente quali metodi e proprietà esporre all’agent.Un vantaggio cruciale: i team possono sviluppare e distribuire skill indipendentemente come pacchetti NuGet, mantenendo il proprio ciclo di rilascio e permettendo il riuso tra progetti.3. Skill inline (Inline Code-Defined Skills)Il terzo paradigma è il più flessibile: skill definiti a runtime usando AgentInlineSkill. Sono perfetti per bridge temporanei, skill generati dinamicamente o implementazioni condizionate dallo stato dell’applicazione:var timeOffSkill = new AgentInlineSkill(
    name: "time-off-balance",
    description: "Calcola i giorni di ferie e malattia rimanenti per un dipendente...")
    .AddScript("calculate-balance", (employeeId, leaveType) =&gt; 
    {
        // Logica runtime
        return $"Giorni rimanenti: {remaining}";
    });
I skill inline supportano anche risorse dinamiche:.AddResource("policies", () =&gt; PolicyRepository.GetActivePolicies());
Questa capacità di aggiungere risorse come delegate è cruciale: le politiche possono aggiornarsi senza ricompilare l’applicazione.Composizione flessibile con AgentSkillsProviderBuilderLa vera potenza del design emerge quando si combinano tutti e tre i paradigmi in un’unica applicazione. Il builder pattern permette una composizione dichiarativa:var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
    .UseSkill(new BenefitsEnrollmentSkill())
    .UseSkill(timeOffSkill)
    .UseFileScriptRunner(SubprocessScriptRunner.RunAsync)
    .Build();
In questa configurazione:I skill nel filesystem vengono caricati e resi disponibiliLa classe BenefitsEnrollmentSkill registra i suoi metodi annotatiLo skill inline timeOffSkill aggiunge capacità runtimeIl framework astrae completamente il “come” carica ogni tipo di skill; l’agent li vede come una superficie unificata.Funzionalità avanzateApprovazione degli scriptPer ambienti ad alto rischio, è possibile richiedere una revisione umana prima dell’esecuzione:.UseScriptApproval(true)
In questo caso, l’agent formula il comando ma non lo esegue autonomamente; un operatore deve approvare.Filtraggio di sicurezzaQuando si condividono directory di skill tra team, il filtraggio garantisce che solo gli skill approvati siano disponibili:.UseFilter(skill =&gt; approvedSkills.Contains(skill.Frontmatter.Name))
Iniezione di dipendenzeI metodi degli skill possono ricevere IServiceProvider come parametro. Questo consente l’accesso a servizi registrati nel contenitore DI, indipendentemente dal paradigma di skill:[AgentSkillScript("send-notification")]
private static string SendNotification(string userId, IServiceProvider services)
{
    var emailService = services.GetRequiredService&lt;IEmailService&gt;();
    return emailService.SendAsync(userId, "Notification");
}
ConclusioneIl design tripartito dei skill in .NET Agent Framework non è una complicazione: è un’architettura di composizione che rispetta gli usi diversi. Gli skill basati su file servono la semplicità e la dinamica; quelli basati su classe offrono sicurezza e riusabilità via NuGet; quelli inline forniscono agilità runtime.Per i team che costruiscono sistemi agent complessi, questa flessibilità è fondamentale. Permette di iniziare in semplicità (skill inline), evolversi verso la modularità (skill basati su classe in NuGet) e mantenere agilità operativa (skill file-based per aggiustamenti dinamici) — tutto nello stesso agent, senza compromessi architetturali.Fonte originale: Agent Skills in .NET: Three Ways to Author, One Provider to Run Them — Microsoft Agent Framework Blog]]></description><link>https://forum.androidiani.net/topic/cd793990-3175-4b79-b45d-0f25c7036e88/agent-skills-in-.net-tre-paradigmi-di-composizione-per-gli-agenti-ai</link><guid isPermaLink="true">https://forum.androidiani.net/topic/cd793990-3175-4b79-b45d-0f25c7036e88/agent-skills-in-.net-tre-paradigmi-di-composizione-per-gli-agenti-ai</guid><dc:creator><![CDATA[blog@spcnet.it]]></dc:creator><pubDate>Wed, 15 Apr 2026 13:16:15 GMT</pubDate></item></channel></rss>