-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



lunedì 14 settembre 2026

NicoGIS Tech Weekly — 14 settembre 2026 - #2

Agents API, full-duplex voice, .NET 11 RC1, C# 15 nei contratti HTTP, Model Router osservabile e il nuovo confine di sicurezza degli agenti

La settimana dal 7 al 13 settembre 2026 ha un segnale tecnico molto più interessante dell'ennesimo incremento di benchmark: il runtime agentico sta diventando un prodotto di piattaforma. OpenAI espone il proprio harness attraverso una Agents API; GPT-Live-1 separa il front-end conversazionale real-time dal backend di reasoning; Microsoft porta .NET 11 alla prima Release Candidate con licenza go-live e rende union types e closed hierarchies parte concreta dei contratti ASP.NET Core; Foundry Model Router aggiunge affinity e telemetria di routing. Sul lato security, il nuovo threat report di Anthropic mostra perché tool permission, sandbox, identità e audit non sono più dettagli implementativi.


1. OpenAI Agents API: il vero prodotto non è il modello, è l'harness

Fatto verificato. Il 10 settembre OpenAI ha annunciato la Agents API in public beta. L'API porta agli sviluppatori l'harness usato per Codex: gestione del contesto, tool discovery, sessioni lunghe, ambienti di esecuzione e subagent. L'ambiente di calcolo può essere un sandbox gestito da OpenAI, infrastruttura propria oppure un sandbox partner. OpenAI dichiara inoltre che non esiste un costo aggiuntivo per la Agents API in sé: restano i costi dei modelli e dei tool utilizzati.

Analisi tecnica. Il cambio di livello di astrazione è importante. Finora molte implementazioni enterprise costruivano manualmente un agent loop attorno alla Responses API o a un framework esterno. Con la Agents API il confine diventa più simile a questo:

User task
   ↓
Agent session
   ↓
Managed harness
   ├── context compaction
   ├── tool search / MCP
   ├── programmatic tool calls
   ├── subagents
   └── sandbox / files / code
   ↓
Result

OpenAI documenta la compattazione automatica del contesto quando una sessione si avvicina al limite, il caricamento selettivo delle definizioni dei tool e l'esecuzione parallela di subagent. Questi tre elementi incidono direttamente su costo, latenza e affidabilità: evitare di reinviare tutte le tool definition riduce token inutili; isolare i subagent evita di contaminare il contesto principale; la compaction consente workflow che superano una singola context window.

Valutazione. Non leggerei però “public beta” come sinonimo di runtime maturo per qualsiasi workload regolato. La semplificazione dell'orchestrazione sposta il lavoro verso governance, evaluation e security boundary. Il pattern che adotterei è: tool allowlist esplicita, sandbox separato dai sistemi core, identità con least privilege e approval per azioni irreversibili. Più il modello è autonomo, meno deve essere implicito il suo perimetro di autorizzazione.

Fonte primaria: OpenAI — Introducing the Agents API


2. GPT-Live-1: la voce diventa un front-end full-duplex, non una pipeline STT → LLM → TTS

Fatto verificato. Sempre il 10 settembre OpenAI ha reso disponibile via API GPT-Live-1, un modello full-duplex in grado di ascoltare e parlare contemporaneamente. OpenAI lo presenta come front-end vocale che può delegare reasoning più profondo e azioni a un modello backend e ai relativi tool. Il prezzo pubblicato per il layer vocale è 0,05 USD al minuto; i costi del modello backend e dei tool restano separati.

Analisi tecnica. Il punto non è solo la naturalezza. Una pipeline classica ha almeno tre fasi con buffering e handoff:

Audio in
  ↓
Speech-to-Text
  ↓
LLM
  ↓
Text-to-Speech
  ↓
Audio out

Il full-duplex riduce la necessità di orchestrare manualmente turn detection, interruzioni e ripresa del parlato. L'architettura più interessante diventa invece:

Voice client ↔ GPT-Live-1 ↔ backend agent/model ↔ tools

Questo permette di mantenere il canale conversazionale responsivo mentre il backend effettua reasoning o tool call più costosi. In pratica, il modello vocale diventa un interaction runtime, mentre il modello testuale può rimanere il deliberative runtime.

Valutazione. Il KPI corretto non è più il solo prezzo per token. Per un voice agent misurerei costo totale per conversazione, latenza al primo audio, percentuale di interruzioni gestite correttamente, tempo medio dei tool e costo backend per minuto di sessione. Il modello economico reale è: voice minutes + backend tokens + tool calls + telephony + retries.

Fonte primaria: OpenAI — Build more natural voice experiences with GPT-Live-1 in the API


3. .NET 11 RC1: da preview tecnologica a candidato utilizzabile con go-live support

Fatto verificato. Microsoft ha pubblicato .NET 11 Release Candidate 1 l'8 settembre. È la prima RC e viene distribuita con go-live support license. Microsoft indica supporto in Visual Studio 2026 Insiders e in Visual Studio Code con C# Dev Kit. Tra gli elementi evidenziati ufficialmente ci sono supporto JSON per closed-type polymorphism e union, immagini container riproducibili con upload ridondanti evitati, Native AOT reuse per file-based programs, stabilizzazione delle union in C# 15, API finalizzate per il refresh dell'autenticazione SignalR e componenti Blazor sperimentali per UI agentiche.

Analisi tecnica. RC1 è il momento in cui inizierei una vera migration lane, non un semplice progetto demo. La presenza della licenza go-live non significa “aggiorna tutto domani”; significa che ha senso testare workload reali e produrre una matrice di compatibilità con package, serializer, source generator, reflection, Native AOT, SignalR, Blazor e container pipeline.

Due dettagli operativi meritano più attenzione del marketing. Le immagini container riproducibili riducono differenze non intenzionali tra build e possono migliorare caching e supply-chain verification. Il refresh dell'autenticazione SignalR, invece, interviene su un problema reale delle applicazioni long-lived: una connessione può sopravvivere più a lungo dello stato di autenticazione iniziale, quindi allineare circuit/connection lifetime e identity lifetime è un miglioramento architetturale concreto.

Valutazione. Per applicazioni enterprise adotterei RC1 prima in CI e in ambienti non critici, con un gate esplicito sui breaking changes. Il valore di una RC non è “avere prima le feature”, ma scoprire prima incompatibilità che in novembre diventerebbero blocchi di progetto.

Fonte primaria: Microsoft .NET Blog — Announcing .NET 11 Release Candidate 1


4. C# 15 union types + ASP.NET Core: il contratto JSON entra nel type system

Fatto verificato. Il 10 settembre il team .NET ha documentato l'integrazione delle native union di C# 15 e delle closed hierarchies con System.Text.Json e ASP.NET Core. Le union funzionano nei punti in cui ASP.NET Core usa STJ — body JSON, SignalR JsonHubProtocol, Blazor JS interop, persisted component state e parametri prerendered — ma non per query string, route values, header o form fields. Il compilatore conosce i case ammessi e può segnalare switch non esaustivi. Per nuove API in cui si controllano tutte le classi, Microsoft raccomanda in generale una closed hierarchy con discriminator; una union è particolarmente adatta quando bisogna conservare un contratto discriminator-free o combinare tipi non correlati, inclusi primitivi.

Un esempio applicativo può essere modellato senza wrapper custom:

public record OrderCreated(int Id);
public record ValidationError(string Message);
public record Unauthorized();

public union CreateOrderResult(
    OrderCreated,
    ValidationError,
    Unauthorized);

Analisi tecnica. La cosa importante è la propagazione del contratto. Con object o un wrapper proprietario il compilatore non può sapere quali alternative siano realmente valide. Con una union, aggiungere un case può produrre warning nei punti in cui gli switch non sono più esaustivi. È un ottimo meccanismo per codice interno e bounded context, ma introduce anche una forma di coupling intenzionale: evolvere il contratto costringe i consumer fortemente tipizzati a prendere posizione sul nuovo caso.

Per API pubbliche, quindi, la scelta fra union e closed hierarchy non è solo estetica. Il discriminator rende l'evoluzione e la deserializzazione di forme object-shaped più esplicite. Un contratto discriminator-free è più elegante quando deve aderire a JSON preesistente, ma diventa ambiguo se due case hanno shape sovrapposte. Microsoft documenta un classificatore strutturale, ma la classificazione basata sulla shape aggiunge lavoro e può cambiare comportamento quando evolvono le proprietà JSON.

Valutazione. Per nuove API enterprise userei closed hierarchy + discriminator quando controllo il dominio. Riservarei union discriminator-free a primitive alternative, interoperabilità con contratti esterni o casi in cui introdurre un envelope romperebbe compatibilità.

Fonte primaria: Microsoft .NET Blog — Use C# unions and closed hierarchies in ASP.NET Core


5. Microsoft Foundry Model Router: session affinity e routing trace rendono il model routing finalmente osservabile

Fatto verificato. La pagina “What's new in model router” di Microsoft Foundry è stata aggiornata il 10 settembre 2026. Le novità di settembre includono session affinity per Chat Completions in preview, metadata di routing per singola richiesta in preview e l'espansione del Model Router a 32 regioni. Con session affinity un'applicazione può fornire un session ID opaco e chiedere al router di tentare di mantenere lo stesso modello eleggibile fra turn correlati, senza disattivare eligibility e fallback. I nuovi metadata possono esporre serving model, routing mode, routing-trace latency, sequenza dei model attempt, HTTP status ed errori.

Analisi tecnica. Questo risolve due problemi classici dei gateway multi-model. Il primo è la consistency drift: due turn della stessa conversazione possono finire su modelli con comportamento differente. L'affinity non garantisce pinning assoluto — e infatti Microsoft conserva il fallback — ma introduce una preferenza di continuità. Il secondo è l'osservabilità: senza sapere quale modello ha servito la richiesta e quali fallback sono avvenuti, è quasi impossibile spiegare variazioni di costo, latenza o qualità.

Request
   ↓
Model Router
   ├── eligibility
   ├── profile / cost-quality policy
   ├── session affinity
   ├── attempt #1
   └── fallback attempt #2
   ↓
Response + routing metadata

Valutazione. Questo è il passaggio che rende il routing multi-model qualcosa di più di una black box. In produzione registrerei per ogni request almeno modello servente, numero di attempt, fallback reason, routing latency, total latency, token e costo. Solo così si può verificare se il router sta davvero ottimizzando qualità/costo oppure sta introducendo varianza non desiderata.

Fonte primaria: Microsoft Learn — What's new in model router in Microsoft Foundry Models


6. ArcGIS Maps SDK for .NET 200.8.3: il messaggio vero è il passaggio di lifecycle verso 300.x

Fatto verificato. Esri indica attualmente ArcGIS Maps SDK for .NET 200.8.3 come versione corrente, datata settembre 2026. Le release note 200.8.3 riportano, tra le correzioni, un crash aprendo nuove mappe di data-management solution da ArcGIS Enterprise, il deadlock BUG-000183194 durante sync e aggiunta contemporanea di feature, un problema di SubtypeFeatureLayer con ScaleSymbols e Map.ReferenceScale, e l'aggiornamento SQLite a 3.53.1. Esri specifica inoltre che la linea 200.x è ora focalizzata esclusivamente su bug fix e security update e indirizza i nuovi sviluppi verso 300.x. La documentazione non espone il giorno esatto di pubblicazione di 200.8.3, quindi non attribuisco la patch a una data specifica della settimana.

Analisi tecnica. Per un team .NET GIS, la notizia più importante non è il singolo bug fix: è il lifecycle. Una linea che passa in maintenance-only cambia il costo di restare fermi. Il debito non si manifesta subito come incompatibilità, ma come progressiva distanza da runtime, tooling e framework moderni.

Il pattern che userei è una matrice esplicita:

App / modulo
  ↓
ArcGIS Maps SDK 200.8.x
  ↓
.NET target / MAUI / WPF / WinUI
  ↓
Local Server dependency?
  ↓
API deprecate?
  ↓
Migration target: 300.x

Valutazione. Non migrerei “perché 300 è più nuovo”; migrerei perché 200.x ha ormai un contratto di manutenzione chiaro. Per app con sync offline, Utility Network, rendering custom o Local Server, costruirei test di regressione prima della migrazione e separerei i rischi di framework dai rischi GIS.

Fonte primaria: Esri Developer — ArcGIS Maps SDK for .NET 200.8 release notes


7. Anthropic Threat Intelligence: gli agenti abbassano il costo operativo dell'attaccante

Fatto verificato. Il 10 settembre Anthropic ha pubblicato il report Detecting and countering misuse of AI: September 2026, basato su casi di abuso identificati e interrotti tra dicembre 2025 e agosto 2026. Anthropic descrive un'evoluzione dall'uso conversazionale del modello verso esecuzione diretta e orchestrazione, inclusi framework multi-agent che in alcuni casi hanno operato per ore o giorni con supervisione minima. Il report sottolinea anche due caveat: le decisioni più importanti, come target selection e monetizzazione, restano spesso umane; autonomia e severità del danno non sono la stessa cosa.

Analisi tecnica. Va trattato come vendor threat intelligence, non come misura statistica della prevalenza globale del fenomeno. Ma il segnale architetturale è utile: l'autonomia riduce il costo marginale di eseguire task ripetitivi, sia per operatori legittimi sia per attaccanti. Questo modifica il threat model dei sistemi agentici.

Se un agente possiede accesso a shell, cloud control plane, storage, posta o database, il problema non è solo prompt injection. Il blast radius dipende dall'intersezione fra:

Model capability
× Tool surface
× Credential scope
× Network reachability
× Autonomy duration
× Approval policy

Valutazione. Per sistemi aziendali adotterei credenziali per-sessione o workload identity, secret isolation, egress filtering, tool allowlist, audit immutabile e human approval sulle operazioni irreversibili. Un agente non dovrebbe ereditare automaticamente i privilegi dell'amministratore che lo sta testando.

Fonte primaria: Anthropic — Detecting and countering misuse of AI: September 2026


DevExpress Watch

Nella finestra 7–13 settembre non ho trovato un nuovo annuncio DevExpress .NET/XAF/Blazor sufficientemente sostanziale da meritare una voce separata rispetto alle roadmap v26.2 già analizzate. L'ultimo post del sito nella settimana è relativo all'Office & PDF File API per Java. Preferisco quindi non riempire il digest con una voce marginale solo per coprire il brand: quando emergerà un aggiornamento concreto su XAF, Blazor, AI Chat, MCP o .NET 11, tornerà nella selezione.


Technical Takeaways

Il runtime agentico sta diventando una piattaforma. Agents API, full-duplex voice e model routing mostrano che il valore non è più concentrato nel singolo modello. Context management, sandbox, tool search, routing, fallback, identity e osservabilità sono ormai parte della piattaforma applicativa.

Il type system sta entrando nei contratti distribuiti. C# 15 union e closed hierarchies non sono solo sugar sintattico: insieme a System.Text.Json e OpenAPI permettono di descrivere in modo verificabile i possibili shape di una API. Il vantaggio è maggiore sicurezza evolutiva; il costo è un coupling più esplicito, che deve essere progettato.

Observability deve arrivare prima dell'automazione. Un router che sceglie automaticamente modelli e un agente che sceglie automaticamente tool sono utili solo se possiamo ricostruire a posteriori cosa è successo. Serving model, fallback, tool call, identity, approval, latenza e costo devono diventare eventi osservabili, non dettagli nascosti.

Il lifecycle conta quanto le feature. .NET 11 entra in RC con go-live support mentre ArcGIS 200.x entra nella fase bug-fix/security-only. In entrambi i casi il lavoro da fare non è inseguire la versione più nuova, ma mantenere una compatibility matrix e pianificare la migrazione prima che diventi emergenza.


Lab della settimana

Lab 1 — C# 15 contract evolution test

Con .NET 11 RC1 crea una Minimal API che restituisce una union con tre case. Genera OpenAPI, serializza ogni case con System.Text.Json e poi aggiungi un quarto case. Verifica quali switch producono warning e come cambia lo schema OpenAPI. Ripeti con una closed hierarchy + discriminator e confronta leggibilità del JSON, compatibilità e comportamento dei client generati.

Documentazione: C# unions and closed hierarchies in ASP.NET Core

Lab 2 — Model Router observability

Su un deployment Microsoft Foundry Model Router abilita i metadata preview per Chat Completions e invia una serie di prompt eterogenei. Registra serving model, routing latency, model attempts, fallback, total latency e token. Poi raggruppa i risultati per task type. Lo scopo non è trovare “il modello migliore”, ma capire se il router riduce il costo medio per task senza degradare il success rate.

Documentazione: Microsoft Foundry Model Router — What's new

Lab 3 — Managed harness vs agent loop custom

Implementa lo stesso task multi-step in due modi: una pipeline custom con Responses API/tool calling e una sessione Agents API con lo stesso set di tool. Misura numero di model call, token totali, tool call, latency, error recovery e quantità di codice infrastrutturale. Aggiungi un task abbastanza lungo da forzare gestione del contesto. Il test interessante non è solo la qualità finale, ma quanto orchestration code e quanta state management vengono realmente eliminati.

Documentazione: OpenAI Agents API


NicoGIS Tech Weekly seleziona solo aggiornamenti con fonti primarie verificabili. Le sezioni indicate come “Analisi tecnica” o “Valutazione” sono interpretazioni architetturali e non affermazioni dei vendor citati.

lunedì 7 settembre 2026

NicoGIS Tech Weekly — 7 settembre 2026 - #1

Agentic AI, C# 15, Native AOT, Azure AI Search, ArcGIS Pro e DevExpress v26.2

La settimana tra il 31 agosto e il 6 settembre 2026 ha un filo conduttore abbastanza evidente: stiamo passando dalla fase in cui l'AI veniva integrata come semplice endpoint generativo alla costruzione di runtime agentici veri e propri, con strumenti, isolamento, retrieval governato, autorizzazioni e osservabilità.

Parallelamente anche gli stack tradizionali stanno cambiando. C# introduce costrutti che portano più informazioni nel type system, .NET cerca di rendere i test coerenti con il deployment Native AOT, Azure AI Search sta diventando sempre più un vero knowledge runtime per agenti, mentre Esri e DevExpress stanno rendendo più prevedibili e strutturati i rispettivi cicli evolutivi.


GPT-6 Astra: il modello diventa un runtime per task end-to-end

OpenAI ha annunciato GPT-6 Astra il 3 settembre 2026. Il modello viene distribuito tramite API come gpt-6-astra e supporta sia la Responses API sia Chat Completions. OpenAI lo presenta come un modello orientato a coding, ricerca, computer use e attività complesse multi-step.

La tariffazione Standard pubblicata da OpenAI è di 10 dollari per milione di token di input e 50 dollari per milione di token di output.

Analisi tecnica. Il dato più interessante non è semplicemente che Astra sia "più intelligente". La vera evoluzione è l'accorpamento nello stesso modello di reasoning, coding, browsing, computer interaction e utilizzo di strumenti.

Questo riduce potenzialmente la necessità di pipeline molto frammentate:

planner
   ↓
coding model
   ↓
browser model
   ↓
executor
   ↓
synthesizer

a favore di architetture più compatte:

orchestrator
   ↓
GPT-6 Astra
   ↓
tools / MCP / browser / computer / files

Non significa però che convenga utilizzare il modello più potente per qualsiasi richiesta. In un'architettura enterprise ha molto più senso introdurre model routing: task semplici, classificazione, extraction e RAG elementare possono essere indirizzati verso modelli meno costosi, riservando il modello frontier ai task realmente complessi.

OpenAI ha inoltre dichiarato che GPT-6 Astra è il primo proprio modello a raggiungere il livello Critical per le capacità cyber secondo il Preparedness Framework. Da un punto di vista architetturale questo rafforza un principio importante: un agente più capace deve avere meno privilegi impliciti, non più privilegi.

Tool authorization, approval gates, identity propagation, least privilege e auditing non possono quindi essere considerati semplici elementi UX: diventano parte dell'architettura applicativa.

Fonti ufficiali:
OpenAI — GPT-6 Astra: A new generation of intelligence
OpenAI — Release Notes
OpenAI — Safety overview: GPT-6 Astra


Gemini 3.8 Flash: il vero benchmark diventa capability/costo per task

Google ha presentato Gemini 3.8 Flash il 2 settembre 2026, insieme a Gemini 3.8 Flash Cyber. Google posiziona 3.8 Flash come modello destinato in particolare a software engineering, agentic task e reasoning multi-step.

Il prezzo introduttivo indicato da Google è di 0,75 dollari per milione di token di input e 3,75 dollari per milione di token di output.

Analisi tecnica. Con gli agenti il confronto basato esclusivamente sul costo per milione di token diventa sempre meno significativo.

TotalTaskCost =
    ModelInputTokens
  + ModelOutputTokens
  + ReasoningOverhead
  + Retrieval
  + ToolCalls
  + Retries
  + ExecutionTime

Un modello nominalmente economico che richiede molti cicli agentici può risultare più costoso di un modello più caro che conclude correttamente il task in pochi step.

Nei benchmark interni inizierei quindi a misurare almeno:

  • Task Success Rate
  • token totali consumati per task
  • numero medio di tool call
  • numero di retry
  • wall-clock latency
  • costo medio per task completato

Questo è particolarmente importante perché stiamo passando dal benchmark del modello al benchmark dell'intero agent runtime.

Google ha inoltre introdotto Gemini 3.8 Flash Cyber all'interno di un programma a disponibilità limitata per scenari avanzati di cyber defense, ulteriore segnale della progressiva specializzazione dei modelli e delle relative policy di accesso.

Fonte ufficiale:
Google — Introducing Gemini 3.8 Flash and 3.8 Flash Cyber


C# 15: Union Types e Closed Hierarchies cambiano il domain modeling

Microsoft ha reso disponibili in .NET 11 Preview 7 diverse funzionalità previste per C# 15, tra cui Union Types, Closed Hierarchies, una nuova preview del modello unsafe, collection-expression arguments, extension indexers e labeled break/continue.

Una delle novità più interessanti è la possibilità di modellare direttamente un insieme chiuso di tipi:

public record Success(string Value);
public record Failure(string Error);

public union Result(Success, Failure);

Il compilatore conosce i casi che compongono l'unione e può quindi aiutare nella verifica dell'exhaustiveness durante il pattern matching.

Analisi tecnica. Questo può eliminare una quantità non trascurabile di codice infrastrutturale che oggi implementiamo tramite:

object
marker interfaces
abstract base classes
custom Result<T>
manual discriminators
OneOf-like libraries

Un domain result può diventare concettualmente:

CreateOrderResult
    ├── OrderCreated
    ├── ValidationError
    └── CreditRejected

Il vantaggio non è soltanto sintattico. Una nuova variante può rendere non esaustivo uno switch esistente, spostando il controllo da una convenzione progettuale a un vincolo verificato dal type system.

Le Closed Hierarchies seguono la stessa direzione: rendere esplicito quali tipi possono appartenere a una gerarchia invece di affidarsi esclusivamente alla disciplina dello sviluppatore.

Questa evoluzione rende C# decisamente più interessante per domain modeling, state machine, workflow e API che devono rappresentare esplicitamente un insieme finito di risultati.

Fonti ufficiali:
Microsoft .NET Blog — Explore new features available in C# 15 preview
Microsoft .NET Blog — Explore union types in C# 15


MSTest 4.4 + Native AOT: finalmente si può testare il deployment model reale

Microsoft ha documentato con MSTest 4.4 la possibilità di pubblicare ed eseguire progetti di test come executable Native AOT.

Il punto tecnico è importante: Native AOT esegue compilazione ahead-of-time e trimming, per cui un'applicazione pubblicata può comportarsi diversamente dallo stesso codice eseguito normalmente sotto runtime managed.

Una configurazione di base è:

<Project Sdk="MSTest.Sdk/4.4.0">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <PublishAot>true</PublishAot>
  </PropertyGroup>
</Project>

e quindi:

dotnet publish MyProject.Tests.csproj -c Release -r win-x64

MSTest utilizza source generation per registrare preventivamente test class e modalità di invocazione, prima che il trimming elimini metadata apparentemente non necessari.

Analisi tecnica. Questo risolve una differenza reale tra:

code tested under JIT/runtime reflection

e:

actual trimmed + AOT compiled artifact

Il caso classico riguarda reflection, serializer, dependency injection dinamica, plugin discovery o altri pattern che dipendono da metadata disponibili a runtime.

Non sostituirei comunque i test managed tradizionali. Una pipeline più sensata è:

Pull Request
   └── Managed tests
       └── feedback rapido

Release validation
   ├── Managed tests
   ├── Native AOT tests
   └── End-to-end test dell'artifact pubblicato

In questo modo il costo del publish AOT non rallenta necessariamente ogni singolo ciclo di sviluppo, ma viene utilizzato dove serve realmente: nella validazione dell'artifact destinato alla produzione.

Fonte ufficiale:
Microsoft .NET Blog — Test what you ship: MSTest and Native AOT


Azure AI Search 2026-08-01-preview: da vector search a knowledge runtime

Microsoft ha introdotto la REST API preview 2026-08-01-preview per Azure AI Search. Tra le funzionalità documentate figurano supporto private network per indexed knowledge source, default configurabili nelle knowledge base, controllo del reranking per singola sorgente, streaming dei risultati di retrieve e citation URL per indexed knowledge source.

Per esempio, il controllo:

"resultsProcessing": "none"

permette di bypassare il reranking per una knowledge source e mantenere l'ordinamento prodotto dalla sorgente sottostante.

Analisi tecnica. L'evoluzione interessante è concettuale.

Il modello tradizionale era:

User Query
    ↓
Embedding
    ↓
Vector Search
    ↓
Top K Documents
    ↓
LLM

Il modello verso cui Azure AI Search si sta muovendo è più vicino a:

User Query
    ↓
Knowledge Base
    ↓
Query Planning
    ↓
Knowledge Source Selection
    ↓
Permissions / Filters
    ↓
Retrieval
    ↓
Per-source Processing
    ↓
Reranking
    ↓
Answer / Grounding / Citations

Questa è una differenza importante: Azure AI Search non viene più utilizzato esclusivamente come vector database o motore di semantic search, ma tende a diventare un retrieval orchestration layer.

Particolarmente rilevante in ambito enterprise è il supporto private network per knowledge source indicizzate basate, tra le altre, su Blob, SharePoint e Azure SQL.

Per sistemi RAG aziendali, caratteristiche come networking, permission filtering, citation provenance e governance possono essere molto più importanti di qualche punto percentuale guadagnato su un benchmark di retrieval.

Va comunque ricordato che queste funzionalità appartengono a una API preview. Microsoft avverte inoltre che l'utilizzo di servizi Microsoft o di terze parti collegati può comportare data processing o storage al di fuori dell'Azure compliance boundary.

Quindi: interessante tecnologicamente, ma da sottoporre a una vera data-flow e governance review prima di utilizzarlo su dati sensibili.

Fonti ufficiali:
Microsoft Learn — What's New in Azure AI Search
Microsoft Learn — Azure AI Search REST API Versions


ArcGIS Pro 3.8: STS/LTS cambia la strategia di upgrade enterprise

Esri ha annunciato un cambio significativo al lifecycle di ArcGIS Pro a partire da ArcGIS Pro 3.8.

Il nuovo modello distingue tra release Short-Term Support (STS) e Long-Term Support (LTS).

Secondo Esri, le release LTS saranno supportate per circa due anni, mentre le STS per circa un anno.

Analisi tecnica. Questa modifica è molto più importante per le installazioni enterprise di quanto possa sembrare.

In ambienti GIS complessi una release ArcGIS Pro non vive isolatamente. La matrice reale comprende spesso:

ArcGIS Pro
   ↕
ArcGIS Enterprise
   ↕
Enterprise Geodatabase
   ↕
DBMS / Client Drivers
   ↕
ArcGIS Pro SDK Add-ins
   ↕
Citrix / RDS
   ↕
Python environments
   ↕
Business extensions

Per organizzazioni che devono validare tutto questo stack, aggiornare ogni release può essere costoso e rischioso.

Il pattern più naturale diventa quindi:

Innovation / Early Adoption
          ↓
        STS

Enterprise Baseline
          ↓
        LTS

Questo consente di distinguere molto meglio una release destinata a provare rapidamente nuove capability da quella utilizzata come baseline aziendale stabile.

Per chi sviluppa ArcGIS Pro Add-in, una conseguenza positiva potrebbe essere una test matrix più razionale: LTS come baseline stabile e STS utilizzata per verificare anticipatamente compatibilità e nuove API.

Fonte ufficiale:
Esri — Update to ArcGIS Pro Product Life Cycle


DevExpress v26.2: Blazor e XAF entrano in una fase più enterprise

DevExpress ha pubblicato le roadmap di fine 2026 per Blazor v26.2 e XAF v26.2, entrambe previste per dicembre 2026.

Per Blazor sono previste, tra le altre cose, supporto .NET 11, innalzamento a .NET 10 della versione minima supportata con v26.2, supporto RTL in CTP, release ufficiale della nuova Icon Library, nuove fondamenta di styling basate su CSS variable e diverse evoluzioni del componente AI Chat.

La roadmap documenta in particolare una funzionalità interessante per gli agenti: tool approval visualization.

L'AI Chat potrà presentare all'utente una UI di conferma prima dell'esecuzione di una tool call, ad esempio prima di aggiornare un record CRM o inviare un'email.

Analisi tecnica. Questo è un dettaglio importante perché sposta lo Human-in-the-Loop da implementazione custom a pattern supportato direttamente dal componente UI.

LLM
 ↓
Tool Call Request
 ↓
DevExpress AI Chat
 ↓
Human Approval
 ↓
Execute / Reject

È esattamente il pattern che serve quando un sistema AI passa dalla sola generazione di testo all'esecuzione di azioni con effetti reali.

Sul fronte XAF, DevExpress continua inoltre a lavorare sulla scalabilità Blazor, dichiarando test specifici per scenari Crowd/Peak Load, Activity e Requests Per Second, oltre a ottimizzazioni su XAF Core, consumo di memoria e rendering Blazor.

Questo è probabilmente uno dei fronti più interessanti per chi utilizza XAF in applicazioni aziendali con numerosi utenti concorrenti.

Con XAF, infatti, il punto centrale non è soltanto aggiungere una chat AI. L'architettura realmente interessante è:

XAF Security System
        ↓
Application User Context
        ↓
AI / Agent Layer
        ↓
Tools / RAG / MCP
        ↓
Business Objects
        ↓
Database / Services

La sfida enterprise sarà fare in modo che retrieval e tool invocation rispettino lo stesso security model applicato all'interfaccia XAF: object-level security, member-level security, ruoli, tenant e autorizzazioni.

Una chat integrata è semplice. Un agente che non possa oltrepassare i privilegi dell'utente è invece una caratteristica architetturale.

La roadmap XAF v26.2 merita inoltre attenzione per la parte scalability: DevExpress dichiara esplicitamente l'intenzione di pubblicare condizioni di test, configurazioni, risultati, limitazioni e metriche relative a concorrenza e carico.

Fonti ufficiali:
DevExpress — Blazor Year-End 2026 Roadmap v26.2
DevExpress — XAF Year-End Roadmap v26.2


Technical Takeaways

Questa settimana evidenzia almeno tre tendenze strutturali.

1. Il modello non è più l'architettura AI

GPT-6 Astra e Gemini 3.8 continuano ad aumentare le capability del modello, ma un sistema enterprise ha bisogno di molti altri componenti:

Identity
Authorization
Model Routing
Tools
MCP
Retrieval
Knowledge Sources
Observability
Auditing
Human Approval
Cost Governance

Il modello deve possibilmente rimanere un componente sostituibile dell'architettura, non diventare l'architettura stessa.

2. Sempre più regole vengono trasformate in vincoli espliciti

C# porta insiemi chiusi di alternative dentro il type system. Azure AI Search porta knowledge source, processing e retrieval policy nella configurazione. Le UI AI iniziano a incorporare esplicitamente approval e controllo dell'esecuzione.

Ogni volta che una regola passa da:

"il developer deve ricordarsene"

a:

"la piattaforma può verificarla o applicarla"

l'architettura diventa più robusta.

3. Deployment e produzione stanno tornando al centro

MSTest Native AOT testa ciò che viene realmente pubblicato. ArcGIS introduce un lifecycle più facilmente gestibile in ambiente enterprise. DevExpress parla apertamente di concurrency, peak load, RPS e memory usage.

È un'evoluzione positiva: meno demo tecnologiche e più production engineering.


Lab della settimana

Lab 1 — Native AOT Test Pipeline

Prendere un progetto .NET reale che utilizza System.Text.Json, dependency injection e qualche componente basato su reflection.

Eseguire prima:

dotnet test

e successivamente una pipeline separata:

dotnet publish -c Release -r win-x64

con PublishAot=true, verificando che numero di test e comportamento coincidano con l'esecuzione managed.

Documentazione: MSTest and Native AOT

Lab 2 — Domain Modeling con C# 15 Union Types

Creare un risultato applicativo:

public record OrderCreated(int Id);
public record ValidationError(string Message);
public record Unauthorized();

public union CreateOrderResult(
    OrderCreated,
    ValidationError,
    Unauthorized);

e utilizzare pattern matching esaustivo per trasformarlo in una risposta HTTP o in un risultato applicativo.

Documentazione: C# 15 Union Types

Lab 3 — Azure AI Search come Retrieval Orchestrator

Creare una knowledge base utilizzando la REST API 2026-08-01-preview e confrontare il comportamento con una pipeline RAG tradizionale.

Misurare almeno:

  • latency end-to-end;
  • numero di query eseguite;
  • qualità del grounding;
  • precisione delle citazioni;
  • token consumati;
  • costo per query;
  • comportamento con permission filtering.

Documentazione: Azure AI Search — What's New


NicoGIS Tech Weekly è una selezione tecnica delle novità che possono avere un impatto concreto su sviluppo software, architetture enterprise, cloud, GIS e applicazioni AI. I fatti sono collegati alle fonti originali; le considerazioni indicate come analisi rappresentano una valutazione tecnica delle possibili implicazioni architetturali.

giovedì 10 marzo 2016

La sicurezza prima di tutto!

A partire da ArcGIS Server 10.4 abbiamo a disposizione un tool da eseguire da riga di comando chiamato serverScan.py. Il tool esegue la scansione del tuo site ArcGIS Server per verificare se è stato configurato seguendo le best practise raccomandate da Esri per quel che riguarda la sicurezza.
Il tool è presente nella cartella

C:\Program Files\ArcGIS\Server\tools\admin
e, una volta eseguito, restituisce un report html.
Come potete notare dall'estensione del file è un tool scritto in Python.
Analogamente è presente un tool chiamato portalScan.py ( ..\Portal\tools\security) che controlla la configurazione del Portal per verificare che siano rispettate le best practise raccomandate da Esri.

Qui potete vedere un esempio di report generato dal tool.


Il tool analizza 12 problematiche legate alla sicurezza dividendole in tre livelli di severità: critico, importante e raccomandato. Qui potete trovare l'elenco.

I parametri da passare al tool sono il nome completo della macchina, la user/password dell'admin del site e la cartella di output dove generare il report.

Con ArcGIS Server federato è possibile anche passare il token (-t in riga di comando) generato dal portal da https://mydomain/portal/sharing/rest/generateToken con Client impostato a Webapp URL al nome del dominio (esempio https://mydomain ) al posto della user/password del PSA
Mentre, se si genera il token dall'administrator directory di ArcGIS Server ( arcgis/admin/generateToken ), occorre impostare per il Client Request IP.



Esempio:

python serverScan.py -n gisserver.domain.com -u admin -p mypassword -o C:\Temp


Vediamo ora nel dettaglio i criteri che vengono verificati:

SS01 Critico
Verifica se la comunicazione con ArcGIS Server è criptata su canale ssl derminando se è abilitato l'https. Se il web adaptor è installato è raccomandato che la comunicazione sia protetta.
In questo caso viene evidenziato che la connessione avviene solo in http con arcgis server.


Inoltre abbiamo la possibilità di specificare i protocolli SSL. I predefiniti ArcGIS Server sono il TLS v1.0, TLS v1.1 e il TLS v1.2. Sono stati tolti SSL 2.0 e SSL 3.0 perché obsoleti e insicuri. Se decidiamo di abilitare solo il TLS v1.2 - attualmente il più sicuro in base ai cifrari utilizzati e comunque quello che supporta le criptazioni autenticate più moderne (ad esempio AEAD) - occorrerà verificare il web server che ospita il web adaptor possa comunicare con il TLS 1.2. Il TLS v1.0 è un protocollo legacy che viene ancora utilizzato ma, se possibile, è meglio evitarlo visto che anche se i moderni browser mitigano le sue debolezze presenta comunque problemi.

Per disabilitare SSL2  e SSL3 in Windows occorre

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols\SSL 2.0\Client]
“DisabledByDefault”=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols\SSL 2.0\Server]
“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols\SSL 3.0\Client]
“DisabledByDefault”=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols\SSL 3.0\Server]
“Enabled”=dword:00000000

Inoltre è raccomandato disabilitare l'algoritmo di cifratura RC4 che è insicuro

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128]
"Enabled"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128]
"Enabled"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128]
"Enabled"=dword:00000000

Per disabilitare Diffie-Hellman vedi questo articolo.

Inoltre dovrebbe essere evitate le seguenti obsolete primitive crittografiche:
- Anonymous Diffie-Hellman (ADH) non forniscono autenticazione;
- NULL cipher non forniscono crittografia.
- Export cipher sono insicuri quando negoziati in una connessione, ma possono anche essere utilizzati con server che preferiscono cipher forti
- cipher deboli  (tipicamente di 40 e 56 bits) usano crittografia che può essere decifrata.
- 3DES è lento e debole.

Questa potrebbe essere un buon punto di partenza come sequenza di cifratori da utilizzare

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

Purtroppo di cifrari occorre prevederne più di uno per la retrocompatibilità con i client che utilizzano il servizio e pertanto, per garantire la compatibilità, occorre trovare il miglior compromesso di suite di priorità di ordine.

Se volete testare il vostro ssl utilizzate l'indirizzo: ssllabs

Per ricevere un rating A+ su sistemi windows occorre avere solo abilitato il TLS 1.2 e l'HSTS (vedi questo articolo per abilitarlo su IIS)





  


SS02 Critico
Verifica se le query standardizzate sono imposte da ArcGIS Server. Per fornire protezione ad attacchi di SQL injection - essendo tale livello considerato critico - l'opzione è abilitata e ArcGIS Server verifica le query non consentendo di utilizzare specifiche funzioni e sintassi del database sottostante.
Qui puoi vedere come utilizzare le funzioni con query standardizzate.
Se non presente, è abilitato mentre se occorre disabilitarlo bisogna dichiararlo in system->properties

SS03 Critico
Verifica se la generazione del token via GET è supportata. Quando viene generato il token via GET le credenziali sono inviate come parte dell'URL e possono così essere visualizzate attraverso la history del browser o i log della rete. Dovrebbe essere disabilitato a meno che sia richiesto esplicitamente da altre applicazioni. Se non presente, è false altrimenti occorre dichiararlo in security->tokens. L'impostazione predefinita è false.

SS04 Critico
Verifica se la generazione del token via POST con le credenziali nei parametri della query string è supportata. Le problematiche sono le stesse dell'SS3 e pertanto andrebbe disabilitato. Se non presente è false altrimenti occorre dichiararlo in security->tokens. L'impostazione predefinita è false.


SS05 Critico
Visualizza un elenco di feature service dove la proprietà filter web content è disabilitata. Disabilitando questa proprietà, consenti all'utente di inserire nei campi di input qualsiasi cosa, ciò potrebbe esporre il servizio a potenziali attacchi XSS. Questa proprietà è abilitata in modo  predefinito e, a meno di entità o attributi HTML richiesti, non deve essere abilitata. Qui potete vedere la lista delle entità e attributi HTML supportati.



SS06 Critico
Controlla se i permessi non predefiniti sono applicati alla cartella System nel Server Manager.
L'impostazione predefinita consente solo agli amministratori e ai pubblicatori di avere accesso ai servizi contenuti nella cartella System.



SS07 Importante
Controlla se la directory dei servizi REST è accessibile attraverso un web browser. A meno che non venga utilizzata attivamente per cercare e trovare servizi da parte degli utenti, dovrebbe essere disattivata per ridurre la possibilità che i servizi possano essere visualizzati, trovati tramite una ricerca su web o interrogati tramite i moduli HTML. Inoltre fornisce un'ulteriore protezione contro attacchi XSS. In system->handlers->rest->servicesdirectory->edit può essere disabilitata.


SS08 Importante
Controlla se le richieste cross - domain sono limitate a specifici domini. Per ridurre la possibilità che applicazioni sconosciute inviino comandi malevoli ai tuoi servizi web, una best practice è limitare l'utilizzo dei tuoi servizi web alle applicazioni ospitate solo ai domini sicuri. Pertanto in system->handlers->rest->servicesdirectory->edit è possibile impostare nella casella AllowedOrigins al posto dell'asterisco la lista dei domini consentiti separati da una virgola (es: http://host.arcgis.com, https://gisserver.example.com)


SS09 Importante
Visualizza una lista di servizi dove il database può essere accessibile via workspace dinamici.
Abilitare il servizio ai workspace dinamici deve essere adeguatamente salvaguardato per evitare che accessi di terze parti possano creare dei danni alla banca dati connettendosi via rest. Pertanto la funzionalità deve essere abilitata solo se realmente necessaria all'applicazione web. In quest'ultimo caso occorrerà connettersi al database dando solo i privilegi minimi di sola lettura alle tabelle strettamente necessarie.


SS10 Raccomandato
Verifica se uno o più web adaptor sono registrati su HTTPS. Per consentire al Server Manager di reindirizzare in https, tutti i web adaptor dovrebbero essere registrati su HTTPS.


SS11 Raccomandato

Verifica se l'account del PSA è abilitato. E' consigliato disabilitare questo account per assicurarsi che non ci sia altro modo di amministrare ArcGIS Server se non tramite i gruppi o i ruoli specificato nell'identity store.  In security->psa è possibile disabilitare l'account PSA.




SS12 Raccomandato
Visualizza una lista di servizi feature che ha le operazioni di update e delete abilitate e non sono protetti.


Questo consente che i dati possano essere modificati o cancellati senza autenticazione.




sabato 10 ottobre 2015

ArcGIS Pro: Nun te regghe Queued(Task)

ArcGIS Pro si differenzia nettamente dalle applicazioni ArcGIS desktop esistenti, poichè è sviluppato secondo un'architettura multithread per sfruttare le moderne CPU/GPU con più core di esecuzione. Per lo sviluppatore di add-in che vuole estendere le funzionalità di ArcGIS Pro, questo significa modificare il modello di programmazione e familiarizzare, in un primo momento, con alcuni nuovi concetti che possono apparire bizzarri. Ovviamente, come con qualsiasi novità, lavorare con questi modelli diventerà gradualmente sempre più facile, ed i benefici del multithreading saranno sempre più evidenti.
Sfide che affronterai nella programmazione multithreading:
Le seguenti quattro differenze fondamentali distinguono le applicazioni multithread tra cui ArcGIS Pro, da una classica applicazione a singolo thread singolo:
  • Per garantire un’esperienza d’uso (UX) responsiva, il thread dell'interfaccia grafica utente (GUI) deve essere in grado di ricevere l'input da parte dell'utente e produrre output senza intoppi e senza interruzioni. Ciò significa che le esecuzioni delle azioni programmate devono essere eseguite in modo asincrono su thread di lavoro separati; il thread della GUI non dovrebbe mai svolgere un lavoro di qualsiasi tipo che blocchi nell’attesa. Ciò è in contrasto con le esistenti applicazioni ArcGIS desktop dove la maggior parte del lavoro viene eseguito direttamente sul singolo thread della GUI.
  • Mentre il lavoro è in esecuzione sui thread in background, agli utenti deve essere presentata un’interfaccia utente logicamente coerente e informativa. Comandi, strumenti, e altre parti dell'interfaccia utente devono essere attivate o disattivate opportunamente basandosi su quali operazioni sono in esecuzione, e dovrebbe essere fornito un adeguato feedback. Se un'operazione di lunga esecuzione è logicamente annullabile, dovrebbe essere fornita la possibilità di annullarla.
  • Operazioni che possono generare conflitto non dovrebbero essere eseguite simultaneamente e devono sempre essere eseguite in una sequenza logica appropriata. Ad esempio, le operazioni su una mappa non possono essere eseguite mentre il progetto che contiene la mappa è ancora in fase di caricamento ed un insieme di feature selezionate non può essere cancellato finché la selezione stessa non è stata completata. La maggior parte delle operazioni avviate tramite l'interazione dell'utente sono logicamente dipendenti dall’ordine e dovrebbero essere eseguite in serie.
  • Bisogna fare attenzione a garantire che l'accesso allo stato volatile, cioè l'accesso alle variabili all'interno del programma, sia correttamente sincronizzato quando tale stato è condiviso tra i thread. Ad esempio, se un oggetto collection è condiviso tra un thread di lavoro e il thread della GUI, entrambi i thread devono essere coordinati per l’accesso alla collection in modo che un thread non legga gli elementi della collection, mentre un altro thread simultaneamente sta aggiungendo o rimuovendo elementi. Questo tipo di protezione è comune a tutti i tipi di programmazione multithreaded e viene normalmente realizzato utilizzando un lock. In un'applicazione in cui più parti indipendenti possono estendere il comportamento dell'applicazione, le operazioni di coordinamento possono diventare complesse e fuori controllo senza un comune framework che orchestra i vari componenti a lavorare insieme.


Il modello di threading interno ad ArcGIS Pro

Gli ingegneri di Esri hanno posto la massima priorità per rendere ArcGIS Pro facile da programmare il più possibile nella nuova architettura multithread. A tal fine, ArcGIS Pro incorpora le più recenti funzionalità della programmazione asincrona fornita da Microsoft insieme a nuove infrastrutture threading specifiche dell'applicazione su misura per ridurre la complessità del codice.
Nella maggior parte dei casi, gli sviluppatori di add-in dovrebbero solo aver bisogno di trattare con due thread: il thread dell'interfaccia utente e un thread di lavoro specializzato forniti dall'applicazione.
Internamente, ArcGIS Pro utilizza un gran numero di thread per vari scopi, tra cui la rasterizzazione, il rendering dei grafici, il caricamento dei dati e la selezione degli algoritmi di geoprocessing che sfruttano il parallelismo per velocizzare il calcolo. Mantenere tutte queste attività senza blocchi e senza conflitti richiede una notevole attività di coordinamento e di complessità associata; per questo motivo, questi thread sono completamente interni e isolati agli sviluppatori nell'ambito delle implementazioni con l’SDK pubblico. Quando viene chiamato un metodo nella API pubblica, l'implementazione interna può, quando applicabile, dividere l’operazione e delegare parti ad uno o più di questi thread interni specializzati, o accodare le operazioni che alla fine vengono eseguite all'interno di un processo esterno o un servizio web .



Attività (Tasks) e il modello asincrono basato su attività (TAP)

I metodi all'interno ArcGIS Pro SDK rientrano in tre categorie:
- Metodi asincroni che possono essere chiamati in qualsiasi thread. Metodi di questo tipo sono nominati utilizzando il suffisso Async e di solito restituiscono Tasks. In alcuni casi, può essere fornita sia la versione sincrona che asincrona del metodo.
- Metodi sincroni che dovrebbero essere chiamati solo sul thread di lavoro. Metodi di questo tipo sono segnalati nella guida in linea delle API e un tip sul codice appariranno quando siamo col cursore sul metodo.
- Metodi sincroni che dovrebbero essere chiamati solo sul thread della GUI. Questi tipi di metodi sono di solito associati con WPF.
Se un metodo su un particolare oggetto viene chiamato sul thread sbagliato, la chiamata genera un'eccezione di tipo ArcGIS.Core.CalledOnWrongThreadException. Se non sei sicuro su un particolare caso, è possibile consultare la guida del componente SDK o l’help fornito da Microsoft per determinare se un particolare metodo o una proprietà ha delle restrizioni.
All'interno dell’SDK - in particolare all'interno del namespace ArcGIS.Core – il thread di lavoro associa metodi e proprietà che tendono ad essere a grana fine. Per ridurre il tempo associato con la schedulazione e la commutazione di contesto, questi metodi sono sincroni e devono essere utilizzati nel codice usando task.
Il .NET Task Parallel Library di Microsoft (TPL) e il modello di programmazione associata noto come il modello asincrono basato su attività (TAP) semplificano la creazione di codice asincrono all'interno di un'applicazione multithread. La classe Task viene utilizzata per rappresentare un'operazione eseguita in modo asincrono.
Nel seguente esempio, il metodo PrintReportAsync viene richiamato e restituisce immediatamente un oggetto Task al chiamante. Nel frattempo, la funzione di stampa continua l’esecuzione in background su un altro thread.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      Task t = PrintReportAsync ("HP1");
      // Attendo che il task abbia finito.
      t.Wait ();
      MessageBox.Show ("Il Report è pronto!");
    }

Questo esempio vuole mostrare un messaggio quando la stampa è completata e utilizza il metodo Wait sull'oggetto Task restituito per sospendere il thread chiamante fino a quando il task ha completato il suo compito.
Questo approccio presenta due grandi inconvenienti: in primo luogo il thread chiamante non può fare altro mentre è in attesa; in realtà è meno efficiente che semplicemente chiamare una versione sincrona della funzione di stampa. In secondo luogo, poiché il thread chiamante è un thread della GUI, l'interfaccia utente risulta ‘congelata’. Un thread sospeso, ovviamente, non è in grado di elaborare l'input dell'utente, aggiornare elementi grafici o fare qualsiasi altra cosa. Per queste ragioni, non si dovrebbe mai usare il metodo Wait su un thread della GUI.
Fortunatamente, .NET introduce le funzionalità al linguaggio async e await. Il modificatore async segna il metodo in modo che il compilatore sappia che il metodo è asincrono e usi l'operatore await. L'operatore await è molto utile in quanto è utilizzato per chiamare i metodi in modo asincrono e dopo per forzare il thread chiamante a tornare automaticamente alla riga successiva e continuare l'esecuzione una volta che l'operazione asincrona è stata completata. Il thread chiamante - normalmente il thread della GUI - non è bloccato ed è libero di adottare altre azioni mentre il task sul thread di lavoro è ancora in esecuzione.
Si noti che possiamo modificare ora l'obiettivo originale con poche variazioni, ma in questo caso l'interfaccia utente non si blocca.

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
      Task t = PrintReportAsync ("HP1");
      // Attendere (senza blocco) fino a quando il task è completato.
      await t;
      // riparte da qui quando il task è completato.
      MessageBox.Show ("Il report è pronto!");
    }

L’utilizzo di Run

Quando una funzione asincrona non è disponibile, è possibile scrivere facilmente le proprie funzioni wrapper che eseguono internamente uno o più metodi sincroni. L'esempio seguente utilizza il metodo statico Run per accodare l'esecuzione della funzione WorkFunc ad una thread casuale nel pool di thread dei Task. Si noti che il metodo click restituisce immediatamente il controllo al chiamante, mentre il WorkFunc continua a eseguire sul thread di lavoro.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      Task t = Task.Run ((Azione) WorkFunc);
    }
    private void WorkFunc ()
    {
      // my work
    }

Invece di utilizzare una funzione separata, può essere impiegata una funzione anonima chiamata anche lambda. Utilizzando le lambda, mantieniamo il codice della funzione di lavoro nella funzione stessa consentendoci di utilizzare gli argomenti e le variabili locali all'interno della lambda come se fossero parte della funzione stessa.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      int steps = GetSteps();
      Task t = Task.Run (() =>
      {
        // Posso utilizzare la variabile steps qui anche se mi trovo in una
        // diversa funzione in esecuzione su un diverso thread!
        // my work
      });
    }

I Task possono anche essere parametrizzati per restituire un tipo particolare, come risultato di un calcolo della lambda.

      Task<double> t = Task.Run<double>(() =>
      {
        double risultato;
        // Calcolo della variabile risultato …
        return risultato;
      });

L'operatore await può essere utilizzato anche in linea per ottenere il risultato della funzione asincrona e senza doverlo estrarre dal Task restituito.

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
      double computedValue = await Task.Run <double>(() =>
      {
        double risultato;
        // Calcolo della variabile risultato ...
        return risultato;
      });
      // L’esecuzione riprende automaticamente qui quando il task sopra ha  completato!
      MessageBox.Show (String.Format ("Il risultato era {0}", computedValue.ToString ()));
    }

C'è un piccolo ‘carico’ associato ad await, quindi è sempre più efficiente chiamare più metodi sincroni all'interno della propria lambda che chiamare molte funzioni asincrone utilizzando await. Questo è particolarmente vero quando si scrive codice in un loop, dove il costo di utilizzo await attraverso centinaia o migliaia di iterazioni diventa sostanziale.


L’utilizzo di QueuedTask

Mentre i Task sono un appuntamento fisso all'interno di qualsiasi codice dell’add-in, i task devono essere forniti in ArcGIS Pro in modo diverso dal tradizionale TAP. Il framework fornisce una schedulazione personalizzata del Task che dovrebbe essere utilizzato quando fornisci Task che effettuano chiamate ai metodi sincroni di ArcGIS Pro SDK. Invece di chiamare Task.Run, gli sviluppatori di add-in devono chiamare QueuedTask.Run.

      Task t = QueuedTask.Run (() =>
      {
        // Chiama metodi SDK sincroni
      });

La classe QueuedTask viene utilizzata al posto della classe Task per i seguenti motivi:

Controllo della concorrenza e delle code

Quando i Task vengono inviati utilizzando Task.Run, il Task associato sarà in esecuzione su un thread a caso nel pool di thread gestito ogni volta che viene chiamato. Se una chiamata successiva a Task.Run viene fatta da qualsiasi altra parte dell'applicazione, il nuovo Task inizierà a funzionare immediatamente su un altro thread mentre il primo Task è ancora in esecuzione sul primo thread. Tornando alla lista delle sfide da affrontare nel codice multithread, dovrebbe essere ovvio che l'esecuzione delle operazioni non organizzate e concorrenti rischia di far andare in crash l’applicazione o di corrompere lo stato dell'applicazione. Il comportamento di accodamento QueuedTask.Run garantisce il corretto ordine delle chiamate e riduce il rischio di conflitti. Ricordate che il parallelismo all'interno di ArcGIS Pro si realizza internamente; questo semplifica il modello di programmazione pubblica e riduce notevolmente la probabilità di conflitti.

Affinità e stato

Per motivi di prestazioni, ArcGIS Pro mantiene un notevole stato su specifici thread e, in molti casi, utilizza oggetti che hanno affinità di thread. Affinità di thread significa che un oggetto è legato ad un particolare thread e non deve interagire con qualsiasi thread ma con il thread col quale ha affinità. Vincoli di affinità sono comuni in sistemi operativi e componenti, tra cui connessioni a database, windows, controlli, code di input, timer, WPF Bitmap e server COM. In WPF, per esempio, chiamate a metodi su un qualsiasi oggetto derivato dalla classe WPF DependencyObject si tradurrà in un'eccezione se la chiamata viene effettuata da un thread da dove l'oggetto non è stato creato.
Thread nel pool di thread gestiti sono anche incompatibili con la maggior parte dei componenti COM, per cui non si dovrebbe tentare di utilizzare Task.Run con codice che potrebbe eseguire direttamente o indirettamente, componenti COM.

Integrazione dell’applicazione

Quando i Task vengono eseguiti utilizzando QueuedTask.Run, essi vengono integrati automaticamente con varie funzionalità all'interno dell'applicazione come segue:
  • L’estensione della Progress/Cancellation del framework, in cui la progress include la finestra di dialogo con lo stato di avanzamento programmabile, viene visualizzata e nascosta automaticamente e lo stato cancellazione è correttamente comunicato tra le parti interessate dell’applicazione.
  • Lo stato occupato del sistema dell’applicazione dove gli elementi dell'interfaccia utente come pulsanti e strumenti sono attivati e disattivati automaticamente quando i Task sono in esecuzione. Esecuzione dei task possono essere coordinati anche con fasi critiche quali la creazione di viste e chiusura dell'applicazione.
  • La coda dei Task è scritta nelle strutture di diagnostica del framework, quando abilitate. Questo consente agli sviluppatori di monitorare la sequenza di esecuzione dei task, i task in esecuzione e la durata dell'esecuzione. Questo tipo di informazione è preziosa per il debugging e l'analisi delle prestazioni.

Casi in cui è accettabile l'utilizzo di Task.Run

Ci sono casi in cui l'uso di Task.Run è accettabile, ad esempio quando si eseguono operazioni in background indipendenti costituite interamente da codice in modo gestito a condizione che i particolari componenti gestiti in uso non abbiano affinità di thread. Lo sviluppatore si assume la piena responsabilità della gestione della cancellazione, della visualizzazione dello stato di avanzamento, dell'abilitazione/disabilitazione dell'interfaccia utente in modo appropriato e del coordinamento delle operazioni e della gestione della logica dei conflitti.

Progress e cancellation

I metodi asincroni possono talvolta accettare un argomento Progressor, un oggetto che viene utilizzato dal chiamante per configurare le impostazioni della finestra di dialogo per lo stato di avanzamento e annullamento e di coordinare la comunicazione tra il chiamante e il chiamato. I metodi asincroni che non sono annullabili prendono una classe Progressor, mentre i metodi annullabili prendono una classe CancelableProgressor.
Gli oggetti Progressor seguono il modello stabilito dal CancelationToken di Microsoft e non possono essere creati direttamente; invece, lo sviluppatore deve creare un ProgressorSource o CancelableProgressorSource.
Gli oggetti "source" consentono di configurare come il progressor gestirà il progress senza esporre queste impostazioni al codice esterno, che potrebbero accedere al Progressor. L'oggetto ProgressorSource espone i seguenti costruttori:

  public ProgressorSource (Action<Progressor> callback)
  public ProgressorSource (ProgressDialog progDlg)
  public ProgressorSource (string message, bool delayedShow = false)

Il primo override prende un delegato che sarà chiamato, a intervalli regolari, mentre il Task è in esecuzione. Questa opzione è appropriata quando si desidera fornire un feedback specializzato durante l'esecuzione del Task.
Il secondo override prende una finestra dialogo di avanzamento, oggetto costruito separatamente. Se non è già visualizzato, il progressor mostrerà automaticamente questa finestra di dialogo di avanzamento quando il Task inizia l’esecuzione e si nasconderà automaticamente al completamento del Task. Se la finestra di dialogo è già visibile, il progressor aggiornerà il contenuto della finestra di dialogo durante l'esecuzione e sarà compito dello sviluppatore nascondere la finestra di dialogo di avanzamento al momento opportuno. Questa opzione è appropriata quando si desidera controllare manualmente la visibilità della finestra dello stato di avanzamento, ad esempio quando è necessario mantenere la finestra dello stato di avanzamento tra diverse attività separate.
Il terzo override creerà automaticamente e mostrerà una finestra di dialogo di avanzamento quando il Task inizia l'esecuzione e lo nasconde al termine del completamento del Task. Il parametro delayedShow controlla se la finestra di dialogo di avanzamento deve essere mostrata immediatamente o ritardare la sua apparizione per consentire operazioni rapide da completare ed evitare di apparire, se non necessario. Se si prevede che il Task sia rapido nel completamento dell’esecuzione, impostare questo parametro a true. Se si prevede che il Task abbia bisogno di più di uno o due secondi per completare l’operazione, impostare delayedShow a false in modo che la finestra di dialogo di avanzamento appaia immediatamente per trasmettere responsività.
CancelableProgressors richiedono un ulteriore argomento che specifica cosa dovrebbe visualizzare il messaggio di annullamento. Verrà visualizzato il messaggio di annullamento, non appena l'utente fa clic sul pulsante Annulla nella finestra di dialogo.

  public CancelableProgressorSource(Action <CancelableProgressor> callback)
  public CancelableProgressorSource(ProgressDialog progDlg)
  public CancelableProgressorSource(string message, string cancelMessage, bool delayedShow = false)

Esempio di implementazione del metodo utilizzando cancellation

Lo specializzato CancelableProgressor espone una proprietà CancellationToken che può essere utilizzata per comunicare l’annullamento. All'interno dell'implementazione del metodo, il codice in esecuzione nel loop dovrebbe controllare la proprietà IsCancellationRequested e uscire dal metodo gettando l’eccezione OperationCanceledException (che riconosce la richiesta di cancellazione) come illustrato di seguito:

public Task<long> CalcFactorialAsync(int x, CancelableProgressor progressor)
{
  return QueuedTask.Run<long>(() =>
  {
    long result = 1;
    for (int i = 1; i < x; ++i)
    {
      if (progressor.CancellationToken.IsCancellationRequested)
        throw new OperationCanceledException();
      result *= i;
    }
    return result;
  });
}

Utilizzare con i metodi asincroni la finestra di dialogo integrata

Se il Progressor è stato configurato per mostrare lo stato di avanzamento, il Task in esecuzione può aggiornare le informazioni visualizzate nella finestra di avanzamento utilizzando il progressor (entrambe Progressor e CancelableProgressor supportano finestre di dialogo di avanzamento):

public Task<long> CalcFactorialAsync(int x, Progressor progressor)
{
  return QueuedTask.Run<long>(() =>
  {
    long result = 1;
    for (int i = 1; i < x; ++i)
    {
      progressor.Message = string.Format("Working on step:{0}", i);
      result *= i;
    }
    return result;
  }, progressor);
}

Complicazioni comuni

Ipotesi di stato costante

Si consideri il seguente esempio. Questa chiamata viene richiamata dal thread della GUI e l'intento è quello di eliminare lo specifico layer dalla mappa della vista attiva.

private Task DeleteSelectedLayerAsync(Layer layer)
{
  return QueuedTask.Run(() =>
  {
      MapView.Active.Map.RemoveLayer(layer);
  });
}

Anche se semplice in apparenza, questa funzione a volte può causare un'eccezione quando in uso all'interno dell'applicazione. L'errore qui è stato quello di pensare che lo stato del sistema rimanga statico attraverso i thread. Ci possono essere precedentemente delle operazioni in coda in esecuzione e queste devono essere completate prima che un'altra operazione possa iniziare l'esecuzione. Durante questo tempo, lo stato dell’applicazione può cambiare a causa dell’interazione con l'utente o il risultato di una operazioni ancora in esecuzione. In questo caso, l’oggetto attivo potrebbe diventare una tabella prima che la lambda effettivamente inizi l’esecuzione, nel qual caso la mappa sarà nulla determinando un'eccezione. L'approccio sicuro è quello di evitare "concatenamenti" di chiamate su variabili membro o variabili passate tra thread; utilizzare variabili locali come istantanee dello stato dell'applicazione quando il metodo è stato chiamato, dal momento che non cambieranno al loro interno.

private Task DeleteSelectedLayerAsync (Layer layer)
{
  // Prendere una "istantanea" della mappa sulla vista attiva.
  Map m = MapView.Active.Map;
  return QueuedTask.Run (() =>
  {
      m.RemoveLayer(layer);
  });
}

In un ambiente multithreading si dovrebbe gestire il codice allestendo una strategia difensiva. Si consideri un Task che modifica come un particolare layer debba essere simboleggiato. Se tale Task finisce in una coda dietro un altro Task che rimuove questo stesso layer dalla mappa, la seconda operazione è logicamente invalidata dalla prima. Per gestire questo caso correttamente, il secondo Task dovrebbe essere gestito per visualizzare un avviso o annullare l'operazione in modalità silente quando viene a sapere che il layer è stato eliminato.

Thread safe con associazione dati  in WPF

Per impostazione predefinita, i dati di collection associati a WPF devono essere modificati sul thread dove è stata creata l’associazione al controllo WPF. Questa limitazione diventa un problema quando si desidera riempire la collection da un thread di lavoro per produrre una buona esperienza per l’utente. Ad esempio, un elenco dei risultati di ricerca che viene riempito gradualmente man mano che vengono trovate corrispondenze, senza costringere l'utente ad attendere fino a quando l'intera ricerca è completata.
Per ovviare a questa limitazione, WPF fornisce una classe statica BindingOperations che permette di stabilire un'associazione tra un lock e una collection (ad esempio, ObservableCollection<T>). Tale associazione consente a collection associate di essere aggiornate dai thread esterni al thread principale della GUI, in modo coordinato senza generare la consueta eccezione.
BindingOperations.EnableCollectionSynchronization(Items, _lockObj);
Nell'esempio sopra, la variabile _lockObj - di tipo oggetto - è in genere istanziata quando viene creata la classe contenitore e servirà come lock per il coordinamento. Una volta che si chiama EnableCollectionSynchronization, WPF entrerà nello specificato lock quando c’è lettura o scrittura alla collection associata. Come proprietari della collection, si è obbligati a inserire il lock durante la lettura alla collection o scrittura della collection.
I wrapper ReadOnlyObservableCollection sono comunemente usati per forzare le sole letture semantiche sulle proprietà della collection osservabile. Per impostare correttamente la sincronizzazione con multithreading, è necessario chiamare EnableCollectionSynchronization sul wrapper, invece che sulla collection stessa, dal momento che è il wrapper al quale il WPF sarà associato effettivamente.

internal class HelloWorld
{
  private ObservableCollection<string> _items = new ObservableCollection<string>();
  private ReadOnlyObservableCollection<string> _itemsRO;
  private Object _lockObj = new Object();
  internal HelloWorld()
  {
    _itemsRO = new ReadOnlyObservableCollection<string>(_items);
    BindingOperations.EnableCollectionSynchronization(_itemsRO, _lockObj);
  }
//la proprietà pubblica utilizzata per il binding
public ReadOnlyObservableCollection<string> Items { get { return _itemsRO; } }
//all’interno della funzione di lavoro, il lock è inserito prima di modificare //la collection:
public void FillCollectionAsync()
{
  QueuedTask.Run(() =>
  {
    // letture e scritture dovrebbe essere fatte all’interno del lock
    lock (_lockObj)
    {
       _items.Add( GetData() );
    }
  });
}

Oggetti "Live" come proprietà

Si deve prestare attenzione quando si espongono oggetti – soprattutto collection - come proprietà pubbliche se la collezione è destinata a cambiare in un thread separato. Se qualcuno ottiene e trattiene tale proprietà e poi inizia l'enumerazione attraverso un thread A, un'eccezione può essere generata se il proprio codice modifica la collection sul thread B in quanto non vi è alcun lock di collaborazione tra essi. Distribuire in sola lettura istantanee della collection è più sicuro.

Eseguire codice sul thread della GUI

Ci sono casi in cui di tanto in tanto, mentre il codice viene eseguito su un thread di lavoro, si incontrano situazioni in cui è necessario chiedere input da parte dell'utente prima di procedere. Non si dovrebbe cercare di presentare una finestra direttamente dal thread di lavoro poiché le finestre hanno affinità di thread. Una finestra di dialogo creata sul thread di lavoro non si collega alla coda di input del thread della GUI e non rispetta lo z-order e la politica di focus stabilita dal thread GUI. In generale, è possibile eseguire codice sul thread della GUI da un thread di lavoro usando l'oggetto dispatcher dell'applicazione.
Questo può essere fatto in modo sincrono.

      FrameworkApplication.Current.Dispatcher.Invoke (() =>
      {
        // Fare qualcosa sul thread  della GUI
        System.Windows.MessageBox.Show ("Pronto!");
      });

O in modo asincrono:

      FrameworkApplication.Current.Dispatcher.BeginInvoke (() =>
      {
        // Fare qualcosa sul thread della GUI
        System.Windows.MessageBox.Show ("Pronto!");
      });

Si dovrebbe cercare di raccogliere le informazioni necessarie da parte dell'utente sul thread della GUI prima di eseguire il lavoro in modo da non dover utilizzare questa scorciatoia. Bloccando chiamate tra thread c’è rischio di deadlock e di trattenere operazioni in esecuzione sul thread di lavoro.

La gestione delle eccezioni in ambiente asincrono

Come le funzioni sincrone, le funzioni asincrone possono gettare eccezioni. Questo introduce un problema interessante da quando il chiamante fornisce il try/catch in un thread, e l'eccezione viene generata su un altro. Inoltre, il frame del chiamante non è solitamente ancora nello stack quando viene generata l'eccezione.
Tuttavia, .NET permette di utilizzare async/await con try/catch in modo che, se viene generata un'eccezione dal codice in esecuzione all'interno del Task, sarete in grado di catturare di nuovo da dove la funzione asincrona è stata chiamata. Si noti che la funzione asincrona deve restituire Task o Task<T> per eccezioni asincrone per essere adeguatamente rediretta (non void).

      try
      {
        var result = await PrintMapAsync ();
      }
      catch (Exception e)
      {
        // gestione eccezione.
      }

Se viene generata un'eccezione da parte del worker e non è stato fornito un try/catch intorno a dove si attende il worker, il runtime .NET si collegherà all'eccezione come eccezione inner, UnobservedException.
Eccezioni Unobserved solitamente compaiono solo quando l'oggetto eccezione viene raccolto dalla garbage collection di .NET, in nessun posto vicino a dove in realtà si è verificata l'eccezione. Se si ottiene uno di questi, esaminare l'eccezione interna per ottenere lo stack delle chiamate fallite. Nella finestra di controllo di VisualStudio, è possibile utilizzare la pseudo variabile $exception per esaminare il corrente oggetto eccezione.

Oggetti Freezable

WPF definisce un modello in cui determinati tipi di oggetti possono essere "congelati". Una volta che l'oggetto è congelato, modifiche non possono essere apportate all'oggetto senza che si generi un'eccezione. Oggetti freezable possono migliorare le prestazioni in alcune situazioni, e permettono anche di condividere l'oggetto tra i thread (vedi affinità di thread). Ad esempio, se un BitmapImage viene creato su un thread di lavoro, non è possibile utilizzarlo in seguito sul thread della GUI a meno di congelarlo prima.
Si consideri un caso comune in cui l'associazione venga utilizzata in combinazione con le immagini che sono state generate su un thread di lavoro. La classe di esempio VM qui sotto espone una proprietà chiamata Img:

public class VM: INotifyPropertyChanged
  {
    public BitmapImage Img {get {return _image; }}
    ...
  }

Questa proprietà restituisce un'istanza di BitmapImage (un oggetto Freezable), che è poi associata ad un pulsante in XAML:

<Button>
<Image Source = "{Binding Img}"> </ Immagine>
</ Button>

La bitmap sottostante viene periodicamente aggiornata sul thread di lavoro come segue; notare che verrà creata la bitmap sul thread di lavoro:

public Task Refresh ()
    {
      return QueuedTask.Run (() =>
      {
        var uri = GenerateThumbnail();
        Img = new BitmapImage (uri);
      });
    }

Nel processo di rendering dell'interfaccia utente, WPF tenterà di accedere alla proprietà bitmap dal thread della GUI ... ma questo si tradurrà in un'eccezione perché la bitmap è ancora "scongelata" e quindi ancorata al suo thread di lavoro principale. Questo problema può essere risolto semplicemente "congelando" la Bitmap dopo il suo l'aggiornamento.

var uri = GenerateThumbnail ();
Img = new BitmapImage (uri);
Img.Freeze ();

Nota: non tutte le classi che ereditano da System.Windows.Freezable possono essere congelate. Utilizzare la proprietà CanFreeze per verificare se l’oggetto può essere congelato.