<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>TechiesWeb.NET</title>
    <description>Tips and Tricks for ASP.NET MVC,ASP.NET Web Api, AspNetCore and all other cool stuff
</description>
    <link>https://techiesweb.net/</link>
    <atom:link href="https://techiesweb.net/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 13 Apr 2026 18:50:55 +0000</pubDate>
    <lastBuildDate>Mon, 13 Apr 2026 18:50:55 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Durable AI Agent Workflows on Azure Functions</title>
        <description>&lt;p&gt;The &lt;a href=&quot;https://github.com/microsoft/agent-framework&quot;&gt;Microsoft Agent Framework&lt;/a&gt; is an open-source, multi-language framework for building, orchestrating, and deploying AI agents. It provides a workflow programming model for composing AI agents and other units of work into multi-step pipelines. To make these workflows stateful, long-running, and fault-tolerant, the &lt;strong&gt;Azure Functions hosting package&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Agents.AI.Hosting.AzureFunctions&lt;/code&gt;) can be used, which builds on the core Durable Task extension (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Agents.AI.DurableTask&lt;/code&gt;) to host workflows on Azure Functions with automatic durability.&lt;/p&gt;

&lt;p&gt;This post walks through building durable workflows and hosting them on Azure Functions. Starting with a minimal workflow, it progressively adds parallel execution, human-in-the-loop approvals, and MCP tool integration.&lt;/p&gt;

&lt;h2 id=&quot;why-durable-workflows&quot;&gt;Why Durable Workflows?&lt;/h2&gt;

&lt;p&gt;AI agents are powerful on their own, but real-world scenarios often require orchestrating multiple agents together with branching logic, parallel processing, human approval gates, and the ability to survive failures and restarts. That’s where durable workflows come in.&lt;/p&gt;

&lt;p&gt;Durable workflows are powered by the &lt;a href=&quot;https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview&quot;&gt;Durable Task&lt;/a&gt; technology stack, which provides:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Stateful, durable execution&lt;/strong&gt; - workflows survive process restarts and failures&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automatic checkpointing&lt;/strong&gt; - progress is saved after each step&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Long-running orchestrations&lt;/strong&gt; - workflows can run for minutes, hours, or even days&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Observability&lt;/strong&gt; - built-in tools for monitoring and managing workflow executions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-workflow-programming-model&quot;&gt;The Workflow Programming Model&lt;/h2&gt;

&lt;p&gt;Before hosting anything, here are the core building blocks of a MAF workflow.&lt;/p&gt;

&lt;h3 id=&quot;executors&quot;&gt;Executors&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;Executor&lt;/strong&gt; is the fundamental unit of work. It receives a typed message, processes it, and optionally produces output. Think of it as a single step in your pipeline.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Agents.AI.Workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OrderLookup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWorkflowContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Simulate looking up an order by ID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;OrderDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IsCancelled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Jerry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;jerry@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderCancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OrderCancel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWorkflowContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Cancel the order&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCancelled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SendEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SendEmail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWorkflowContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;$&quot;Cancellation email sent for order &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; to &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsCancelled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;workflowbuilder&quot;&gt;WorkflowBuilder&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;WorkflowBuilder&lt;/strong&gt; wires executors into a directed graph. You define edges between executors to control the flow of data and the builder produces an immutable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Workflow&lt;/code&gt; object.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;OrderLookup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OrderCancel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SendEmail&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Build the CancelOrder workflow: OrderLookup -&amp;gt; OrderCancel -&amp;gt; SendEmail&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancelOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CancelOrder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cancel an order and notify the customer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This runs entirely in memory using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InProcessExecution.RunAsync&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InProcessExecution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancelOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;12345&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But if the process crashes, the workflow state is lost. The hosting package solves that.&lt;/p&gt;

&lt;h2 id=&quot;hosting-workflows-on-azure-functions&quot;&gt;Hosting Workflows on Azure Functions&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Agents.AI.Hosting.AzureFunctions&lt;/code&gt; package bridges MAF workflows with the Azure Functions runtime. You define your workflow the same way, then register it with a single call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureDurableWorkflows&lt;/code&gt;. The framework auto-generates all the necessary durable function triggers, orchestrator functions, and HTTP endpoints.&lt;/p&gt;

&lt;h3 id=&quot;why-azure-functions&quot;&gt;Why Azure Functions?&lt;/h3&gt;

&lt;p&gt;Hosting workflows on Azure Functions brings several benefits beyond just durability:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Serverless scaling&lt;/strong&gt; - Azure Functions automatically scales out based on workload. Workflow executions scale independently without managing infrastructure.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pay-per-execution pricing&lt;/strong&gt; - You only pay for the compute time your executors actually use, making it cost-effective for bursty or low-traffic workflows.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Built-in HTTP endpoints&lt;/strong&gt; - Each registered workflow gets an HTTP trigger automatically. No need to write controllers or routing logic.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Durable Task Scheduler integration&lt;/strong&gt; - Workflow state is persisted and managed by the &lt;a href=&quot;https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/choose-orchestration-framework&quot;&gt;Durable Task Scheduler&lt;/a&gt;, providing reliability, observability, and cross-process coordination out of the box.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zero boilerplate&lt;/strong&gt; - The hosting package generates orchestrator functions, activity functions, and entity functions from the workflow definition. The only code to write is the executors and the workflow graph.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MCP tool support&lt;/strong&gt; - Workflows can be exposed as MCP tools with a single flag, making them discoverable by AI agents and other MCP-compatible clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the package:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet add package Microsoft.Agents.AI.Hosting.AzureFunctions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;📂 &lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions/tree/main/SequentialWorkflow&quot;&gt;View the full sample on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a complete &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; for a Functions app that hosts the CancelOrder workflow:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Agents.AI.Hosting.AzureFunctions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Agents.AI.Workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Azure.Functions.Worker.Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Define executors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OrderLookup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OrderCancel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SendEmail&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Build the CancelOrder workflow: OrderLookup -&amp;gt; OrderCancel -&amp;gt; SendEmail&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancelOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CancelOrder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cancel an order and notify the customer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderCancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Host it on Azure Functions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IHost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionsApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancelOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key addition here is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ConfigureDurableWorkflows()&lt;/code&gt; — an extension method provided by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Agents.AI.Hosting.AzureFunctions&lt;/code&gt; package. It’s the single call that bridges your MAF workflow definition to the Azure Functions runtime. Everything else in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; is standard Azure Functions boilerplate.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the hosting layer automatically maps your workflow concepts to Durable Functions primitives:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Your workflow becomes an orchestrator function&lt;/strong&gt; — the workflow graph is translated into a durable orchestration that coordinates the execution of each step&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Each executor becomes an activity function&lt;/strong&gt; — executors are wrapped as durable activity functions, giving them automatic retry, checkpointing, and fault tolerance&lt;/li&gt;
  &lt;li&gt;An HTTP trigger is generated to start the workflow: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /api/workflows/CancelOrder/run&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Integration with the &lt;a href=&quot;https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/choose-orchestration-framework&quot;&gt;Durable Task Scheduler&lt;/a&gt; provides durability and scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because these are standard Azure Functions under the hood, you automatically get all the platform benefits — &lt;strong&gt;auto-scaling&lt;/strong&gt;, &lt;strong&gt;built-in monitoring via Application Insights&lt;/strong&gt;, &lt;strong&gt;distributed tracing&lt;/strong&gt;, and the full Azure Functions &lt;strong&gt;observability and diagnostics&lt;/strong&gt; tooling — without writing any extra infrastructure code.&lt;/p&gt;

&lt;p&gt;When you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func start&lt;/code&gt;, you can see these auto-generated functions in the terminal output. Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-&lt;/code&gt; prefixed functions — these are the durable activity and orchestration triggers that the hosting package generated from your workflow definition:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/func-start_terminal_output.jpg&quot; alt=&quot;func start terminal output showing auto-generated durable functions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http-CancelOrder&lt;/code&gt; function is the HTTP endpoint you’ll call to start the workflow, while the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-*&lt;/code&gt; functions are the internal plumbing: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-CancelOrder&lt;/code&gt; is the orchestrator, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-OrderLookup&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-OrderCancel&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dafx-SendEmail&lt;/code&gt; are the activity triggers — one for each executor in the workflow graph.&lt;/p&gt;

&lt;h3 id=&quot;invoking-the-workflow&quot;&gt;Invoking the Workflow&lt;/h3&gt;

&lt;p&gt;Once the Functions app is running, you can trigger the workflow with a simple HTTP request:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST http://localhost:7071/api/workflows/CancelOrder/run
Content-Type: text/plain

12345
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This starts the orchestration asynchronously and returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;202 Accepted&lt;/code&gt; response with a run ID. The workflow then executes durably in the background, looking up order &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12345&lt;/code&gt;, cancelling it, and sending a confirmation email.&lt;/p&gt;

&lt;p&gt;Complex JSON inputs are also supported:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST http://localhost:7071/api/workflows/BatchCancelOrders/run
Content-Type: application/json

{&quot;orderIds&quot;: [&quot;1001&quot;, &quot;1002&quot;, &quot;1003&quot;], &quot;reason&quot;: &quot;Customer requested&quot;, &quot;notifyCustomers&quot;: true}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can register multiple workflows in a single Functions app. Executors can even be shared across workflows:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// OrderStatus reuses the same OrderLookup executor as CancelOrder&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OrderStatus&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Look up an order and generate a status report&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statusReport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancelOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;batchCancelOrders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;fan-out--fan-in-parallel-execution&quot;&gt;Fan-Out / Fan-In (Parallel Execution)&lt;/h2&gt;

&lt;p&gt;When you need multiple agents to process the same input concurrently, use the &lt;strong&gt;fan-out / fan-in&lt;/strong&gt; pattern. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddFanOutEdge&lt;/code&gt; sends a message to multiple executors in parallel, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddFanInBarrierEdge&lt;/code&gt; waits for all of them to complete before proceeding.&lt;/p&gt;

&lt;p&gt;📂 &lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions/tree/main/FanInFanout&quot;&gt;View the full sample on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a workflow where a physics agent and a chemistry agent both answer the same question, and an aggregator combines their responses:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Create AI agents as executors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AIAgent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;physicist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chatClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsAIAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;You are a physics expert. Be concise (2-3 sentences).&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Physicist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AIAgent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chemist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chatClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsAIAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;You are a chemistry expert. Be concise (2-3 sentences).&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Chemist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ParseQuestionExecutor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parseQuestion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AggregatorExecutor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aggregator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Build workflow: ParseQuestion -&amp;gt; [Physicist, Chemist] (parallel) -&amp;gt; Aggregator&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parseQuestion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ExpertReview&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddFanOutEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parseQuestion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chemist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddFanInBarrierEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chemist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aggregator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Host on Azure Functions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IHost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionsApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AIAgent&lt;/code&gt; instances can be used directly in the workflow graph, just like any other executor. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsAIAgent&lt;/code&gt; extension method creates an agent from a chat client and system prompt.&lt;/p&gt;

&lt;h2 id=&quot;human-in-the-loop&quot;&gt;Human-in-the-Loop&lt;/h2&gt;

&lt;p&gt;One of the most powerful patterns is &lt;strong&gt;human-in-the-loop&lt;/strong&gt;, which pauses a workflow to wait for external approval or input before continuing. This is modeled using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RequestPort&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RequestPort&lt;/code&gt; acts like an executor in the graph, but instead of processing data automatically, it pauses the workflow and waits for an external response. When hosted on Azure Functions, the framework auto-generates HTTP endpoints for checking pending requests and submitting responses.&lt;/p&gt;

&lt;p&gt;📂 &lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions/tree/main/HumanInTheLoop&quot;&gt;View the full sample on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s an expense reimbursement workflow with three approval gates (one manager approval, then two parallel finance approvals):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hitl-workflow-diagram.png&quot; alt=&quot;Human-in-the-Loop workflow diagram&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Define executors and RequestPorts for the HITL pause points&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CreateApprovalRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managerApproval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ManagerApproval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PrepareFinanceReview&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepareFinanceReview&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;budgetApproval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BudgetApproval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;complianceApproval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApprovalRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApprovalResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ComplianceApproval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ExpenseReimburse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reimburse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Build the workflow with sequential and parallel approval gates&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expenseApproval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ExpenseReimbursement&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Expense reimbursement with manager and parallel finance approvals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;managerApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;managerApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepareFinanceReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddFanOutEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prepareFinanceReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;budgetApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;complianceApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddFanInBarrierEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;budgetApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;complianceApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reimburse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Host on Azure Functions with status endpoint enabled&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IHost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionsApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expenseApproval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exposeStatusEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exposeStatusEndpoint: true&lt;/code&gt;, the framework generates three HTTP endpoints for this workflow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /api/workflows/ExpenseReimbursement/run&lt;/code&gt; - Start the workflow&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET /api/workflows/ExpenseReimbursement/status/{runId}&lt;/code&gt; - Check status and see pending approvals&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /api/workflows/ExpenseReimbursement/respond/{runId}&lt;/code&gt; - Submit an approval response to resume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;External systems (or humans via a UI) can poll the status endpoint to see which approvals are pending, then POST a response to unblock the workflow. The respond endpoint expects a JSON body with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventName&lt;/code&gt; (matching the RequestPort name) and the response data:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST http://localhost:7071/api/workflows/ExpenseReimbursement/respond/{runId}
Content-Type: application/json

{&quot;eventName&quot;: &quot;ManagerApproval&quot;, &quot;response&quot;: {&quot;approved&quot;: true, &quot;comments&quot;: &quot;Looks good&quot;}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;exposing-workflows-as-mcp-tools&quot;&gt;Exposing Workflows as MCP Tools&lt;/h2&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exposeMcpToolTrigger: true&lt;/code&gt; option, your workflows become callable as &lt;a href=&quot;https://modelcontextprotocol.io/&quot;&gt;MCP (Model Context Protocol)&lt;/a&gt; tools. Other AI agents or MCP-compatible clients can discover and invoke your workflows as tools.&lt;/p&gt;

&lt;p&gt;📂 &lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions/tree/main/WorkflowMcpTool&quot;&gt;View the full sample on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Define workflow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderLookupWorkflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookupOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OrderLookup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Look up an order by ID and return enriched order details&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookupOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enrichOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Expose as MCP tool&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IHost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionsApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableWorkflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orderLookupWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;exposeStatusEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exposeMcpToolTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Functions host generates a remote MCP endpoint at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/runtime/webhooks/mcp&lt;/code&gt; with a tool for each registered workflow. The workflow’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.WithName()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.WithDescription()&lt;/code&gt; are used as the MCP tool name and description.&lt;/p&gt;

&lt;h2 id=&quot;agents-and-workflows-together&quot;&gt;Agents and Workflows Together&lt;/h2&gt;

&lt;p&gt;When you need to register both standalone AI agents and workflows in a single Functions app, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureDurableOptions&lt;/code&gt; — the more general sibling of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureDurableWorkflows&lt;/code&gt; that gives you access to both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options.Agents&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options.Workflows&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;📂 &lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions/tree/main/AgentsAndWorkflows&quot;&gt;View the full sample on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Define a standalone AI agent&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AIAgent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assistant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chatClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsAIAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;You are a helpful assistant. Answer questions clearly and concisely.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Assistant&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;A general-purpose helpful assistant.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Define a workflow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Workflow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;translateWorkflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translateText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Translate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Translate text to uppercase and format the result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translateText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Register both with Azure Functions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IHost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionsApplication&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureDurableOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Agents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAIAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assistant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;enableHttpTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableMcpToolTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;translateWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;exposeStatusEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exposeMcpToolTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;more-workflow-patterns&quot;&gt;More Workflow Patterns&lt;/h2&gt;

&lt;p&gt;The workflow programming model supports several additional patterns that work with both in-process and durable execution:&lt;/p&gt;

&lt;h3 id=&quot;conditional-routing&quot;&gt;Conditional Routing&lt;/h3&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddSwitch&lt;/code&gt; to route messages to different executors based on the output of a previous step:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spamDetectionExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;switchBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;switchBuilder&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DetectionResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spamDecision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SpamDecision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotSpam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailAssistantExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DetectionResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spamDecision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SpamDecision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;handleSpamExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handleUncertainExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;shared-state&quot;&gt;Shared State&lt;/h3&gt;

&lt;p&gt;Executors can share data through scoped key-value state, useful when parallel executors need access to the same source data:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Write to shared state in one executor&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;QueueStateUpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fileID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scopeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FileContentState&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Read from shared state in another executor&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadStateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scopeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FileContentState&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sub-workflows&quot;&gt;Sub-Workflows&lt;/h3&gt;

&lt;p&gt;Embed a workflow as an executor inside another workflow for modular, hierarchical architectures:&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Build a sub-workflow&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subWorkflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uppercase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uppercase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithOutputFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Use it as an executor in a parent workflow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ExecutorBinding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subWorkflowExecutor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BindAsExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TextProcessing&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mainWorkflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WorkflowBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subWorkflowExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subWorkflowExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithOutputFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;postProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When running on the durable runtime, sub-workflows execute as sub-orchestrations with proper result propagation.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;Durable workflows in the Microsoft Agent Framework bring together the flexibility of AI agents and the reliability of durable orchestrations. With a few lines of code, you can build complex multi-step workflows and host them on Azure Functions with auto-generated HTTP endpoints, human-in-the-loop support, and MCP tool integration.&lt;/p&gt;

&lt;p&gt;Here are some useful links to get started:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/agent-framework&quot;&gt;Microsoft Agent Framework on GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/03-workflows&quot;&gt;Workflow samples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/04-hosting/DurableWorkflows/AzureFunctions&quot;&gt;Azure Functions hosting samples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/kshyju/AgentWorkflowsOnAzureFunctions&quot;&gt;Standalone sample repo for this blog post&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Agents.AI.Hosting.AzureFunctions&quot;&gt;Microsoft.Agents.AI.Hosting.AzureFunctions NuGet package&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Agents.AI.DurableTask&quot;&gt;Microsoft.Agents.AI.DurableTask NuGet package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Give it a try and share your feedback or questions on the &lt;a href=&quot;https://github.com/microsoft/agent-framework&quot;&gt;GitHub repo&lt;/a&gt;. Issues and contributions are welcome!&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Apr 2026 07:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2026/04/12/hosting-durable-ai-agent-workflows-on-azure-functions.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2026/04/12/hosting-durable-ai-agent-workflows-on-azure-functions.html</guid>
        
        <category>Microsoft Agent Framework</category>
        
        <category>Durable Task</category>
        
        <category>AI Agents</category>
        
        <category>Workflows</category>
        
        <category>Azure Functions</category>
        
        <category>MCP</category>
        
        <category>Durable Functions</category>
        
        
      </item>
    
      <item>
        <title>Extending input binding pipeline in dotnet isolated Azure functions</title>
        <description>&lt;p&gt;The dotnet isolated model of azure functions has a binding pipeline which is responsible for populating your function inputs. While the built-in mechanism satisfies the majority of the binding use cases, there may be instances where you want to customize this pipeline.&lt;/p&gt;

&lt;p&gt;Let’s get familiarized with a few terminologies before we move forward&lt;/p&gt;

&lt;h3 id=&quot;input-conversion&quot;&gt;Input conversion&lt;/h3&gt;

&lt;p&gt;The process of using the raw input data to populate all the function inputs appropriately.&lt;/p&gt;

&lt;h3 id=&quot;input-converter&quot;&gt;Input converter&lt;/h3&gt;

&lt;p&gt;A small component responsible for converting the raw input to a specific type of function input type. The dotnet isolated package ships with a default set of converters and the Input conversion pipeline uses these converters to successfully bind all the function parameters.&lt;/p&gt;

&lt;h2 id=&quot;extending-the-input-conversion-pipeline&quot;&gt;Extending the input conversion pipeline&lt;/h2&gt;

&lt;p&gt;If the built-in converters are not handling your use cases, you may write your own input converter and register that with your function app. Let’s take a look at an example.&lt;/p&gt;

&lt;p&gt;Imagine you have POCO like below to represent customer information.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Customer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gender&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Culture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and you want to use that type as a parameter of your function and you want to populate this from using some minimal information from your http request. For example, assume your http request includes the unique id of the customer in the route (ex: “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/customers/123&lt;/code&gt;”) and you want to use that id value to pull the customer details from another source (ex: a database /XML file/ a REST API etc..)&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Update&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Anonymous&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Route&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;customers/{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpRequestData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processing request for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//do something with the customer instance.&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processed &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make this happen, we will extend the input conversion pipeline with a custom converter.&lt;/p&gt;

&lt;h3 id=&quot;writing-a-custom-input-converter&quot;&gt;Writing a custom input converter&lt;/h3&gt;

&lt;p&gt;Create a class and have it implement the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.converters.iinputconverter?view=azure-dotnet&quot;&gt;IInputConverter&lt;/a&gt; interface. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputConverter&lt;/code&gt; interface has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConvertAsync&lt;/code&gt; method which we are going to implement with our custom logic.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomerConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputConverter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConverterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// we are going to replace this with actual implementation shortly.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConvertAsync&lt;/code&gt; method receives an instance of &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.converters.convertercontext?view=azure-dotnet&quot;&gt;ConverterContext&lt;/a&gt;, from which we can get information about the raw input data and the target type (the parameter type) we are going to bind to. In this specific example, we need the Id value from the request URL/route. We could access that from the BindingContext on the FunctionContext.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConverterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FunctionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BindingContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BindingData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomerFromRestAPIAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Failed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Unhandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here we are reading the Id route param value and using that to populate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Customer&lt;/code&gt; instance. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetCustomerFromRESTAPIAsync&lt;/code&gt; method is simply calling a REST API to get the customer details.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetCustomerFromRestAPIAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;https://anapioficeandfire.com/api/characters/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFromJsonAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;em&gt;httpClient is a static instance of HttpClient.&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;The above method is kept as minimal and simple. Modify as needed to make it more robust as needed.&lt;/em&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can see that we used 3 helper methods in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConvertAsync&lt;/code&gt; code. Let’s take a look at what they do.&lt;/p&gt;

&lt;h3 id=&quot;conversionresultsuccess&quot;&gt;ConversionResult.Success&lt;/h3&gt;

&lt;p&gt;If the conversion is successful we return the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConversionResult.Success&lt;/code&gt; helper method, which returns an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConversionResult&lt;/code&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Status&lt;/code&gt; property value set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Succeeded&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; property value set to the populated customer object.&lt;/p&gt;

&lt;h3 id=&quot;conversionresultfailed&quot;&gt;ConversionResult.Failed&lt;/h3&gt;

&lt;p&gt;If there is was exception during the conversion, we use the  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConversionResult.Failed&lt;/code&gt; helper which returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConversionResult&lt;/code&gt; instance with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Error&lt;/code&gt; property populated. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Status&lt;/code&gt; property value will be set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failed&lt;/code&gt; in this case.&lt;/p&gt;

&lt;h3 id=&quot;conversionresultunhandled&quot;&gt;ConversionResult.Unhandled&lt;/h3&gt;

&lt;p&gt;Finally the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConversionResult.Unhandled&lt;/code&gt; method can be used when you want to tell the calling pipeline that this converter is not able to convert this specific input. Remember this converter will be called while trying to populate every function input (&lt;em&gt;although there is a way to optimize this which you will see below&lt;/em&gt;). For example, the date time converter returns Unhandled when the target type is not a DateTime type or the source is not string.&lt;/p&gt;

&lt;p&gt;https://github.com/Azure/azure-functions-dotnet-worker/blob/7d659ced053af41d58274ea4a36d2b5db2138750/src/DotNetWorker.Core/Converters/DateTimeConverter.cs#L16-L19&lt;/p&gt;

&lt;h3 id=&quot;register-the-custom-converter-in-the-pipeline&quot;&gt;Register the custom converter in the pipeline.&lt;/h3&gt;

&lt;p&gt;The custom converter can be hooked up to the function app in any of the following 3 approaches.&lt;/p&gt;

&lt;h3 id=&quot;1-app-bootstrapping-time&quot;&gt;1) App bootstrapping time&lt;/h3&gt;
&lt;p&gt;Add the new converter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WorkerOptions.InputConverters&lt;/code&gt; while bootstrapping the function app.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureFunctionsWorkerDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workerOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;workerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputConverters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CustomerConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The above approach will add the custom converter as the last entry in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputConverters&lt;/code&gt; collection. The binding pipeline calls each converter sequentially in the order they are registered. So in this case the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomerConverter&lt;/code&gt; will be called only after all the built-in converters are called and all of them returned an Unhandled result.&lt;/p&gt;

&lt;p&gt;If you want the custom converter to be used earlier, you can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterAt&lt;/code&gt; method.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;// Adds MyCustomConverter to the set of available (built-in) converters, but at index 3. 
workerOptions.InputConverters.RegisterAt&amp;lt;CustomerConverter&amp;gt;(3);
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;2-type-level&quot;&gt;2) Type level&lt;/h3&gt;
&lt;p&gt;Decorate the type used as the parameter of the function with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputConverter&lt;/code&gt; attribute where you will define the custom converter type to be used.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InputConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BookConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Isbn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GetBook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Anonymous&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Route&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;books/{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpRequestData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processing request for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//do something with the Book instance.&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processed &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The above example assumes that you have written a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BookConverter&lt;/code&gt; similar to the CustomerConverter above which populates a Book instance.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;3-function-parameter-level&quot;&gt;3) Function parameter level&lt;/h3&gt;
&lt;p&gt;Decorate the function parameter using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputConverter&lt;/code&gt; attribute&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GetBook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HttpTrigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Anonymous&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Route&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;books/{id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpRequestData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InputConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BookConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processing request for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//do something with the Book instance.&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Processed &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this case, you do not need to decorate your book POCO with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputConverter&lt;/code&gt; attribute.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Isbn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last 2 approaches are more optimized than the first one since those will directly pick the explicitly specified converter for the parameter instead of going through all the registered converters.&lt;/p&gt;

&lt;h3 id=&quot;dependency-injection&quot;&gt;Dependency Injection&lt;/h3&gt;

&lt;p&gt;Yes, dependency injection work with your custom input converters. You can inject dependencies to your converter class constructor.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IInputConverter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BookConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BookConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConversionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConvertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConverterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;BookConverter is the best!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Take a look at &lt;a href=&quot;https://github.com/kshyju/IsolatedFunctionSamples/tree/input-converters&quot;&gt;this repo&lt;/a&gt; for a complete working sample.&lt;/p&gt;

&lt;p&gt;Give it a try. If you are running into issues, please open a new issue in the &lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/issues&quot;&gt;dotnet worker repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;
</description>
        <pubDate>Sat, 11 Feb 2023 08:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2023/02/11/azure-functions-input-converters.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2023/02/11/azure-functions-input-converters.html</guid>
        
        <category>Azure functions</category>
        
        <category>Functions</category>
        
        <category>Isolated azure functions</category>
        
        <category>Azure functions binding</category>
        
        <category>Azure functions model binding</category>
        
        
      </item>
    
      <item>
        <title>Azure functions V1 to V4 migration</title>
        <description>&lt;p&gt;Azure functions dotnet isolated model &lt;a href=&quot;https://techcommunity.microsoft.com/t5/apps-on-azure-blog/announcing-general-availability-for-azure-functions-v4-net/ba-p/3637605&quot;&gt;recently started supporting&lt;/a&gt; function apps targeting .NET Framework. This is great news for devs maintaining V1 version of function apps because they can now migrate their V1 apps to the latest function runtime version(&lt;a href=&quot;https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-functions-4-0-and-net-6-support-are-now-generally/ba-p/2933245&quot;&gt;V4&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Let’s take a look at the version history of function apps with the TFM &amp;amp; hosting models it supported.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Function runtime&lt;/th&gt;
      &lt;th&gt;TFM support&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;V1&lt;/td&gt;
      &lt;td&gt;.NET framework, In-proc.only&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;V2 (Deprecated)&lt;/td&gt;
      &lt;td&gt;.Net core 2.X.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;V3&lt;/td&gt;
      &lt;td&gt;Out of process model is introduced. .NET 5.&lt;br /&gt;In-proc model with .Net core 3.X.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;V4&lt;/td&gt;
      &lt;td&gt;In-proc model with .Net 6.&lt;br /&gt;Out of process model is .Net6, .Net7 and .NET framework.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Until now, function apps created with V1 version could not migrate to the newer versions of functions runtime while maintaining the target framework, but it is possible now with the isolated model support of .NET framework TFM.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The important thing to keep in mind is that, you will be migrating your in-proc app to isolated model.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;in-proc-to-isolated-migration&quot;&gt;In-proc to Isolated migration&lt;/h3&gt;

&lt;p&gt;We will use a very simple V1 function app with Http trigger for the migration example.&lt;/p&gt;

&lt;h3 id=&quot;1-update-the-project-file&quot;&gt;1) Update the project file&lt;/h3&gt;

&lt;p&gt;Open your project file (.csproj) and make the below edits.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AzureFunctionsVersion&lt;/code&gt; value from v1 to v4.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputType&lt;/code&gt; entry with value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exe&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Update PackageReferences.&lt;/p&gt;

    &lt;p&gt;The in-proc model uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.NET.Sdk.Functions&lt;/code&gt; package. In the isolated model, we use a different set of packages.&lt;/p&gt;

    &lt;p&gt;The below 2 packages are needed for all isolated function apps.&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker&quot;&gt;Microsoft.Azure.Functions.Worker&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Sdk&quot;&gt;Microsoft.Azure.Functions.Worker.Sdk&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;The below package is needed for Http functions. Since our example is migrating a function app with HttpTrigger, we need this package as well. In the in-proc model, the Microsoft.NET.Sdk.Functions implicitly brings the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Azure.WebJobs.Extensions.Http&lt;/code&gt; package for http function support.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http&quot;&gt;Microsoft.Azure.Functions.Worker.Extensions.Http&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So after these changes, your .csproj file will look like this:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/463f9adaa8fd4387b8e3ec1bd6d3b81a.js?file=V4NetFxcsproj.cs&quot;&gt;&lt;/script&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The isolated model function app should not have a reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.NET.Sdk.Functions&lt;/code&gt; package. That package is only meant for in-proc function apps.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;2-update-configuration-files&quot;&gt;2) Update configuration files&lt;/h3&gt;

&lt;p&gt;Open your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local.settings.json&lt;/code&gt; file and add a new entry under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Values&lt;/code&gt; with key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FUNCTIONS_WORKER_RUNTIME&lt;/code&gt; and value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet-isolated&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AzureWebJobsDashboard&lt;/code&gt; entry if present in your config.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;IsEncrypted&quot;: false,
  &quot;Values&quot;: {
    &quot;AzureWebJobsStorage&quot;: &quot;UseDevelopmentStorage=true&quot;,
    &quot;FUNCTIONS_WORKER_RUNTIME&quot;: &quot;dotnet-isolated&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-add-a-programcs-with-startup-code&quot;&gt;3) Add a Program.cs with startup code&lt;/h3&gt;

&lt;p&gt;Create a new class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; and paste the below code to it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;internal class Program
{
    static void Main(string[] args)
    {
        FunctionsDebugger.Enable();

        var host = new HostBuilder()
            .ConfigureFunctionsWorkerDefaults()
            .Build();

        host.Run();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the bootstrapping code for the isolated function application. It creates a &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host&quot;&gt;generic host&lt;/a&gt;, configure the services needed for it to work as a function app, build the host and run it. This is where you will register additional &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection&quot;&gt;dependencies to the DI container&lt;/a&gt; or configure logging etc, as needed.&lt;/p&gt;

&lt;h3 id=&quot;4-switch-to-the-new-types&quot;&gt;4) Switch to the new types&lt;/h3&gt;

&lt;p&gt;The v1 in-proc version uses types from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Azure.WebJobs&lt;/code&gt; namespace. In the isolated model, we do not use these types, instead a new set of types were introduced in the isolated model. So replace the using statements with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using Microsoft.Azure.Functions.Worker&lt;/code&gt; from which we will use a few types.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In in-proc model, functions are decorated with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FunctionName&lt;/code&gt; attribute. In the isolated model, we will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Function&lt;/code&gt; attribute.&lt;/li&gt;
  &lt;li&gt;In the Isolated model, the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.http.httprequestdata?view=azure-dotnet&quot;&gt;HttpRequestData&lt;/a&gt; type wraps information about the http request, instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpRequestMessage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;In the Isolated model, the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.http.httpresponsedata?view=azure-dotnet&quot;&gt;HttpResponseData&lt;/a&gt; type should be used as the return type for Http functions.&lt;/li&gt;
  &lt;li&gt;Use the extension methods of &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.http.httprequestdataextensions?view=azure-dotnet&quot;&gt;HttpRequestData&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.http.httpresponsedataextensions?view=azure-dotnet&quot;&gt;HttpResponseData&lt;/a&gt; to read/write http request/response.&lt;/li&gt;
  &lt;li&gt;For logging, V1 in-proc app uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TraceWriter&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Azure.WebJobs.Host&lt;/code&gt;. In the V4 isolated model, you will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ILogger&lt;/code&gt; type. This experience will be same as &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0&quot;&gt;logging in .net/ASP.NET core apps&lt;/a&gt;. You can inject an ILogger instance via constructor injection. See example &lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/blob/4400fa36120327130b73496970d7c9740b26f981/samples/NetFxWorker/HttpFunction.cs#L13-L18&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can add a parameter of type &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.functions.worker.functioncontext?view=azure-dotnet&quot;&gt;FunctionContext&lt;/a&gt; in your functions to get contextual information about the function being executed. See example &lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/blob/4400fa36120327130b73496970d7c9740b26f981/samples/CustomMiddleware/HttpFunction.cs#L15-L16&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Ideally, An isolated function should not have any reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Azure.WebJobs&lt;/code&gt; package. Packages with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Azure.Functions.Worker&lt;/code&gt; &lt;a href=&quot;https://www.nuget.org/packages?q=Microsoft.Azure.Functions.Worker&quot;&gt;prefix&lt;/a&gt; is what you need in your isolated function app.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should be good to run your app now. If you are using Visual studio, press F5 to start debugging.&lt;/p&gt;

&lt;h3 id=&quot;ide-support&quot;&gt;IDE support&lt;/h3&gt;

&lt;p&gt;For users who uses Visual Studio as their IDE, you need to use &lt;a href=&quot;https://visualstudio.microsoft.com/vs/preview/&quot;&gt;Visual studio 2022 Preview&lt;/a&gt; 17.4.0 Preview 3 or later for F5 debugging to work.&lt;/p&gt;

&lt;p&gt;If you are still running into issues when debugging a .net framework function app, even after installing &lt;a href=&quot;https://visualstudio.microsoft.com/vs/preview/&quot;&gt;preview 3 or later&lt;/a&gt;, It is possible that VS failed to automatically update the azure functions tools to latest version. In this case, you shall manually update the azure functions tools using the Options dialog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Options&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Projects &amp;amp; Solutions&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Azure functions&lt;/strong&gt; and click on the “&lt;strong&gt;Check for updates&lt;/strong&gt;” button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/vs-azfunc-updates-check.png&quot; alt=&quot;Check Azure functions tools updates available&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Install the updates as needed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/updates-available.png&quot; alt=&quot;Install Azure functions tools Updates&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Restart Visual Studio and try to create a new Azure functions project and you should see “.NET Framework Isolated” in the functions worker dropdown.&lt;/p&gt;

&lt;p&gt;Here are some useful links to make your migration experience a breeze:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide&quot;&gt;Guide for running C# Azure Functions in an isolated process&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/kshyju/NetFXMigrationSample&quot;&gt;Refer this sample solution&lt;/a&gt; which has V1 in-proc project and V4 isolated version of the same.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/tree/main/samples&quot;&gt;Isolated function app samples in the dotnet worker repo&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/wiki/.NET-Worker-bindings&quot;&gt;Bindings &amp;amp; Triggers in isolated model&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/AzureFunctions&quot;&gt;Follow Azure functions on twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are running into issues with the migration process, please open a new issue in the &lt;a href=&quot;https://github.com/Azure/azure-functions-dotnet-worker/issues&quot;&gt;dotnet worker repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sun, 16 Oct 2022 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2022/10/16/azure-functions-v1-to-v4-migration-guide.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2022/10/16/azure-functions-v1-to-v4-migration-guide.html</guid>
        
        <category>Azure functions</category>
        
        <category>Functions</category>
        
        <category>Isolated azure functions</category>
        
        <category>Azure functions V1 to V4 migration</category>
        
        <category>Azure functions migration</category>
        
        
      </item>
    
      <item>
        <title>Useful APIs for writing azure functions middleware</title>
        <description>&lt;p&gt;One of the benefits of using isolated (out-of-process) model is the ability to plug in custom middlewares in the invocation pipeline. In this post, I will go through some of the handy APIs you can use to make your middleware authoring experience easier. All these APIs are extension methods on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FunctionContext&lt;/code&gt; instance which is available in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; method of the middleware.&lt;/p&gt;

&lt;h3 id=&quot;http-trigger-specific-apis&quot;&gt;HTTP Trigger specific APIs&lt;/h3&gt;

&lt;p&gt;Let us take a look at 2 APIs specific to Http triggers.&lt;/p&gt;

&lt;h4 id=&quot;gethttprequestdataasync&quot;&gt;GetHttpRequestDataAsync&lt;/h4&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHttpRequestDataAsync&lt;/code&gt; extension method can be used to obtain an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpRequestData&lt;/code&gt; when called during an http trigger invocation. In the isolated function model, the HttpRequestData instance represents the http trigger specific information, which is useful when you want to read/update http trigger specific data, such as request/response headers and cookies. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHttpRequestDataAsync&lt;/code&gt; returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueTask&amp;lt;HttpRequestData?&amp;gt;&lt;/code&gt; instance, which you could await to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpRequestData&lt;/code&gt; instance.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HttpRequestData? httpReqData = await functionContext.GetHttpRequestDataAsync();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This method will return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; if called during a non http trigger invocation.&lt;/p&gt;

&lt;h4 id=&quot;gethttpresponsedata&quot;&gt;GetHttpResponseData&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHttpResponseData&lt;/code&gt; extension method can be used to get an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpResponseData&lt;/code&gt;  when called during an http trigger invocation. This method will return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; if called during a non http trigger invocation or when the function return type is not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpResponseData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following is an example of a middleware implementation which reads the HttpRequestData instance and updates the HttpResponseData instance during function execution. This middleware checks for the presence of a specific request header(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-correlationId&lt;/code&gt;), and when present uses the header value to stamp a response header. Otherwise, it generates a new GUID value and uses that for stamping the response header.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=StampHttpHeaderMiddleware.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseWhen&lt;/code&gt; extension method to register this middleware, where we can write a predicate which returns true, only when the invocation is for an http trigger. This way, our middleware will be participating in the invocation pipeline only for http trigger invocations.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=UseWhenExtensionUse.cs&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;apis-applicable-to-all-trigger-types&quot;&gt;APIs applicable to all trigger types.&lt;/h3&gt;

&lt;h4 id=&quot;getinvocationresult&quot;&gt;GetInvocationResult&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInvocationResult&lt;/code&gt; method gets an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvocationResult&lt;/code&gt;, which represents the result of the current function execution. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvocationResult&lt;/code&gt; type has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; property to get or set the value as needed.&lt;/p&gt;

&lt;p&gt;This method is useful when you want to alter the return value of the function inside a middleware. For example, you can write a global exception handler like below where you will update the return value when an exception happens during the invocation.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=ExceptionHandlingMiddleware.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;There is another overload of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInvocationResult&lt;/code&gt; where you can specify the type you are expecting as the return type of the invocation.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; var invocationResult = context.GetInvocationResult&amp;lt;HttpResponseData&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;getoutputbindings&quot;&gt;GetOutputBindings&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetOutputBindings&lt;/code&gt; method gets the output binding entries collection for the current function execution. Each entry in the collection is of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputBindingData&amp;lt;T&amp;gt;&lt;/code&gt;. You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; property to get or set the value as needed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;IEnumerable&amp;lt;OutputBindingData&amp;lt;object&amp;gt;&amp;gt;? allOutputBindings = context.GetOutputBindings&amp;lt;object&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This method is useful if you want to update the invocation result of a function which has multiple output bindings.&lt;/p&gt;

&lt;p&gt;For example, if you have a function like below where the return type of the function is a type which has properties decorated with output binding attributes.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=HttpTriggerWithMultipleOutputBindings.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetOutputBindings&lt;/code&gt; method to get the output binding entries and update them as needed. Below is an example where we are updating the value of the queue output binding entry.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=GetOutputBindingsToUpdateValue.cs&quot;&gt;&lt;/script&gt;

&lt;h4 id=&quot;bindinputasync&quot;&gt;BindInputAsync&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BindInputAsync&lt;/code&gt; method binds an input binding item for the requested BindingMetadata instance. For example, you can use this method when you have a function with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlobInput&lt;/code&gt; input binding that needs to be accessed or updated by your middleware. Imagine you have a function like below,&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=HttpTriggerWithBlobInputFunction.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;You can access all the input bindings for your function from the function context(via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FunctionDefinition&lt;/code&gt; property) and update the entry values. In the below snippet, I am updating only the blob input entry’s value.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/8b9c4a611f773369f14f40578dd74892.js?file=UpdateBlobInputBindingEntryValue.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;Please give these APIs a try. These are available from version &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker/1.8.0-preview1&quot;&gt;1.8.0-preview1&lt;/a&gt; onwards.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sun, 15 May 2022 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2022/05/15/azure-functions-isolated-middleware-apis.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2022/05/15/azure-functions-isolated-middleware-apis.html</guid>
        
        <category>Azure functions</category>
        
        <category>Functions</category>
        
        <category>Isolated azure functions</category>
        
        <category>Middleware</category>
        
        <category>Functions middleware</category>
        
        <category>IFunctionsWorkerMiddleware</category>
        
        <category>GetHttpRequestDataAsync</category>
        
        <category>GetInvocationResult</category>
        
        <category>GetHttpResponseData</category>
        
        
      </item>
    
      <item>
        <title>Go-load, A simple load testing tool written in go</title>
        <description>&lt;h2 id=&quot;go-load-a-simple-load-testing-tool-written-in-go&quot;&gt;Go-load, A simple load testing tool written in go&lt;/h2&gt;
&lt;p&gt;As I was learning golang, I wrote a simple load testing tool in it and have been using it for generating consistent load test traffic while I try to collect profiling data on my .net apps.&lt;/p&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;h4 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h4&gt;
&lt;p&gt;Make sure you have installed&lt;a href=&quot;https://golang.org/dl/&quot;&gt; go runtime in your machine&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;install-go-load-binary&quot;&gt;Install go-load binary&lt;/h4&gt;

&lt;p&gt;Open a terminal/command prompt and run the below &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt; command which will install the go-load binary. This will download the source code to your machine and build the go-load.exe.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go get github.com/kshyju/go-load
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;command-line-options&quot;&gt;Command line options&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c&lt;/code&gt; : Specifies the number of concurrent connections(goroutines) to use. You can consider this as the RPS you want for the test. Ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c 50&lt;/code&gt; will send 50 requests/second.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; : Specifies the duration of the load test in seconds. Ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d 30&lt;/code&gt; will run the tests for 30 seconds.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h&lt;/code&gt; : Specifies the request headers to send in a comma separated form. Each header item can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key:value&lt;/code&gt; format. Ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-apikey:foo,cookie:uid&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-body&lt;/code&gt; : Specifies the path to a local file name which has the request payload data present. go-load will read the content of this file and use that as the request body. When this option is passed, Http POST method will be used to send the request.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-u&lt;/code&gt; : Specifies the request URL explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;h4 id=&quot;send-10-requestssec-to-bingcom-for-30-seconds&quot;&gt;Send 10 requests/sec to bing.com for 30 seconds&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go-load -c 10 -d 30 https://www.bing.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you have special characters like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; in your URL, you need to pass the value in quotes.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go-load -c 10 -d 30 &quot;https://www.bing.com?how=are&amp;amp;you=today&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;sending-request-headers&quot;&gt;Sending request headers&lt;/h4&gt;
&lt;p&gt;Request headers can be passed as a comma separated string with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h&lt;/code&gt; flag. The string should have header key and value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key:value&lt;/code&gt; format. Ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User-Agent:Go-http-client/1.1&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go-load -c=10 -d=30 -h my-apikey:foo,cookie:uid:bar https://www.bing.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above example is sending 2 request headers, “my-apikey” and “cookie”.&lt;/p&gt;

&lt;h4 id=&quot;sending-http-post-request-with-payload-from-a-local-file&quot;&gt;Sending HTTP POST request with payload from a local file&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;go-load -c=10 -d=30 -body=&quot;C:\\temp\\my-payload.json&quot; &quot;http://your.app/which/accepts/http-post?foo=bar&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end of the run, go-load will generate a run summary which includes latency(different percentiles) and aggregated status code count.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_05_23_go-load-result-summary.png&quot; alt=&quot;Go-Load run summary&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/kshyju/go-load?from=tw&quot;&gt;Source code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sun, 23 May 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/05/23/go-load_load_testng_tool.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/05/23/go-load_load_testng_tool.html</guid>
        
        <category>Go</category>
        
        <category>GoLoad</category>
        
        <category>Go-Load</category>
        
        <category>Load testing tool</category>
        
        <category>stress testing tool</category>
        
        
      </item>
    
      <item>
        <title>A fast alternative to Linq&apos;s Intersect.Any() expression result</title>
        <description>&lt;h2 id=&quot;a-fast-alternative-to-linqs-intersectany-expression-result&quot;&gt;A fast alternative to Linq’s Intersect.Any() expression result&lt;/h2&gt;

&lt;p&gt;While looking at the profiling data of one of our production systems, I stumbled across this interesting block in the CPU flame graph.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_5_15_before-linq-intersect.png&quot; alt=&quot;linq taking 80 percent cpu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s call the obscured method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetData&lt;/code&gt;. Over 80 % of the time in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetData&lt;/code&gt; is spent in the LINQ expression where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; extension method is called on the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect&lt;/code&gt; extension method. This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetData&lt;/code&gt; method was obviously in hot path of the application, getting executed multiple times per incoming request.&lt;/p&gt;

&lt;p&gt;The goal of using this LINQ expression in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetData&lt;/code&gt; method was to find whether there was a common element present between two string collections. Something like below.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/d1903a06b84263de4a458f7046247dab.js?file=Blog2021LinqIntersectAnyUsageSample.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;The profiler also tells us that this method took ~5 % of total CPU of our service process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_5_15_before-total-cpu.png&quot; alt=&quot;total cpu usage of method&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looking at the source code of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect&lt;/code&gt; method, we can see it does two things.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Creates a Hash set and add items from first collection to it.&lt;/li&gt;
  &lt;li&gt;Tries to remove each items of second collection from this hash set as we iterate through the result of this method. The Remove method returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt; when it successfully removes an item (a match has been found) and return those removed items(the common items).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_05_15_intersect-sourcecode.png&quot; alt=&quot;Intersect source code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect&lt;/code&gt; method returns an Enumerable collection(deferred execution). So If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; method returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;, it won’t execute the code for remaining elements in second collection.&lt;/p&gt;

&lt;p&gt;In my case, All I care about is finding out whether we have a common element present between these 2 collections. We could do this just with a nested for loop without allocating the HashSet. We could also eliminate the hash generation during the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove&lt;/code&gt; method call. I wrote an extension method like below which uses 2 simple for loops.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/d1903a06b84263de4a458f7046247dab.js?file=Blog2021MyIntersectAnyExtension.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;This may look like a brute force solution. Would it do better compared to the LINQ expression? Only way to tell that is by measuring both implementations. So I wrote benchmarking code for these 2 implementations with 2 types of source collections, Arrays and Lists. Our extension method uses a for loop on an array. So If the collection passed in is not an array, it will call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsArray&lt;/code&gt; extension method to convert that collection to an array.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/d1903a06b84263de4a458f7046247dab.js?file=Blog2021MyIntersectAnyBenchmarks.cs&quot;&gt;&lt;/script&gt;

&lt;p&gt;The benchmark results&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kshyju/d1903a06b84263de4a458f7046247dab.js?file=Blog2021MyIntersectAnyBenchmarkResults.md&quot;&gt;&lt;/script&gt;

&lt;p&gt;The benchmark results clearly shows:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;When both the source collections are Arrays, the custom implementation is &lt;strong&gt;7 times faster&lt;/strong&gt; than the LINQ expression. Also it allocates 0 bytes (no hash set allocation in managed heap. The for loop runs on the stack)&lt;/li&gt;
  &lt;li&gt;When both the source collections are Lists, the custom implementation is &lt;strong&gt;4.5 times faster&lt;/strong&gt; than the LINQ expression. There is allocation where our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsArray&lt;/code&gt; method has to convert the collection to an Array. But still a lot smaller than what the LINQ expression allocated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And finally, after deploying the fix to prod, I went back and checked the prod profiler data again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_05_15_after-getdata.png&quot; alt=&quot;total cpu usage of method&quot; /&gt;
&lt;img src=&quot;/assets/2021_05_15_after-total-cpu.png&quot; alt=&quot;total cpu usage of method&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetData&lt;/code&gt; method now consumes 1.6 % CPU where it used to consume around 5 %.&lt;/p&gt;

&lt;p&gt;Key takeaways are,&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;LINQ expression is concise, but may not be optimal if that is being used in a hot path. Many times, you can write a faster implementation which handles &lt;em&gt;your specific use case&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Measure, Fix, Measure again, Repeat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sat, 15 May 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/05/15/an_alternative_to_linq_intersect_any.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/05/15/an_alternative_to_linq_intersect_any.html</guid>
        
        <category>LINQ</category>
        
        
      </item>
    
      <item>
        <title>Deserialize JSON arrays from object type with $type and $values in System.Text.Json</title>
        <description>&lt;h2 id=&quot;deserializing-json-arrays-from-object-type-with-type-and-values-in-systemtextjson&quot;&gt;Deserializing JSON arrays from object type with $type and $values in System.Text.Json&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://tools.ietf.org/html/rfc8259#section-2&quot;&gt;JSON spec&lt;/a&gt; says an array type property should be represented in a structure surrounded by square brackets.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;An array structure is represented as square brackets surrounding zero
or more values (or elements).  Elements are separated by commas.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;array = begin-array [ value *( value-separator value ) ] end-array&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine working with a class like this for serialization/deserialization.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public sealed class Person
{
    public string FullName { get; set; }        
    public List&amp;lt;Vehicle&amp;gt; Vehicles { get; set; }
}

public sealed class Vehicle 
{
    public int Year { get; set; }
    public string Model { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A valid JSON string adhering to RFC spec, which can be successfully deserialized to an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; would be like this&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;fullName&quot;: &quot;Kramer&quot;,
    &quot;vehicles&quot;: [
        {
            &quot;year&quot;: 2012,
            &quot;model&quot;: &quot;Accord&quot;
        },
        {
            &quot;year&quot;: 2000,
            &quot;model&quot;: &quot;Altima&quot;
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Deserialization for the above JSON string works fine with both Newtonsoft JSON.NET and System.Text.JSON&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var personSystemTextJson = System.Text.Json.JsonSerializer.Deserialize&amp;lt;Person&amp;gt;(json, _options);
var personNewtonSoft = Newtonsoft.Json.JsonConvert.DeserializeObject&amp;lt;CardInfo&amp;gt;(json);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-other-array-format&quot;&gt;The other array format&lt;/h2&gt;

&lt;p&gt;I recently learned that arrays can be represented in another way in the JSON string. This is clearly not standard syntax as per the JSON spec. Take a look at this JSON string.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;fullName&quot;: &quot;Kramer&quot;,
    &quot;vehicles&quot;: {
    &quot;$type&quot;: &quot;System.Collections.Generic.List`1[[Vehicle, My.Project]], mscorlib&quot;,
    &quot;$values&quot;: [
            {
                &quot;year&quot;: 2012,
                &quot;model&quot;: &quot;Accord&quot;
            },
            {
                &quot;year&quot;: 2000,
                &quot;model&quot;: &quot;Altima&quot;
            }
        ]
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vehicles&lt;/code&gt; is using an object structure. The object has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$type&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$values&lt;/code&gt; sub properties. The value part of  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$values&lt;/code&gt; token actually holds the real array. &lt;a href=&quot;https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm&quot;&gt;Newtonsoft.JSON.NET will successfully deserialize&lt;/a&gt; this JSON string to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; instance while System.Text.Json Will throw.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;System.Text.Json.JsonException: The JSON value could not be converted to System.Collections.Generic.List`1[TestProject1.Vehicle]. Path: $.Vehicles | LineNumber: 2 | BytePositionInLine: 45.
    at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
   at System.Text.Json.JsonSerializer.HandleStartObject(JsonSerializerOptions options, ReadStack&amp;amp; state)
   at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader&amp;amp; reader, ReadStack&amp;amp; readStack)
   at System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, Utf8JsonReader&amp;amp; reader)
   at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Deserialize&lt;a href=&quot;String json, JsonSerializerOptions options&quot;&gt;TValue&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was recently bit by this behavior while trying to migrate a system from JSON.NET to STJ. STJ is more strict and tries to adhere to the RFC spec, hence it consider this JSON string as non valid.&lt;/p&gt;

&lt;p&gt;Ideally we should consider fixing the JSON to be following the spec. But in my case, this was not a straight forward option for the other team to change this right away. So I ended up writing a custom JsonConverter to do this.&lt;/p&gt;

&lt;p&gt;A converter basically allows us to customize the behavior of serialization/deserialization. So in my case, I created a converter for handling the deserialization of the type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;Vehicle&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-core-3-1&quot;&gt;Spend a few minutes to read the official documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ended up writing my converter like below. The trick is to loop through the tokens, when you find the StartArray type token, deserialize from that position onwards.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public sealed class VehiclesArrayConverter : JsonConverter&amp;lt;List&amp;lt;Vehicle&amp;gt;&amp;gt;
{
    public override List&amp;lt;Vehicle&amp;gt; Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // Let&apos;s check we are dealing with a proper array format([]) adhering to the JSON spec.
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            // Proper array, we can deserialize from this token onwards.
            return JsonSerializer.Deserialize&amp;lt;List&amp;lt;Vehicle&amp;gt;&amp;gt;(ref reader, options);
        }
        
        // If we reached here, it means we are dealing with the JSON array in non proper array form
        // ie: using an object structure with &quot;$type&quot; and &quot;$values&quot; format like below 
        // We will go through each token and when we get the array type inside (for $values),
        // We will deserialize that token. We exit when we reaches the next end object.
        
        List&amp;lt;Vehicle&amp;gt; list = null;
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.StartArray)
            {
                list = JsonSerializer.Deserialize&amp;lt;List&amp;lt;Vehicle&amp;gt;&amp;gt;(ref reader, options);
            }
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                // finished processing the array and reached the outer closing bracket token of wrapper object.
                break;
            }
        }

        return list;
    }

    public override void Write(Utf8JsonWriter writer, List&amp;lt;Vehicle&amp;gt; value, JsonSerializerOptions options)
    {
        // Nothing special to do in write operation. So use default serialize method.
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
} 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, you have to hook this up with the type/property. I chose to do this on the property level using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JsonConverter&lt;/code&gt; attribute.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public sealed class Person
{
    public string FullName { get; set; }
    
    [JsonConverter(typeof(VehiclesArrayConverter))]
    public List&amp;lt;Vehicle&amp;gt; Vehicles { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this STJ can deserialize our non standard JSON string to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; instance properly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2021_04_24-STJ-JsonConverter-Tests.png&quot; alt=&quot;Test results&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can make our implementation a bit more generic with &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#why-use-constraints&quot;&gt;C# generics&lt;/a&gt;. Here is a generic version of our converter.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public sealed class NonStrictArrayConverter&amp;lt;T&amp;gt; : JsonConverter&amp;lt;T&amp;gt;  where T : IEnumerable
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            // Proper array, we can deserialize from this token onwards.
            return JsonSerializer.Deserialize&amp;lt;T&amp;gt;(ref reader, options);
        }
        
        var value = default(T);
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.StartArray)
            {
                value = JsonSerializer.Deserialize&amp;lt;T&amp;gt;(ref reader, options);
            }
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                // finished processing the array and reached the outer closing bracket token of wrapper object.
                break;
            }
        }

        return value;
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and we can hook it up to any collection properties.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public sealed class Person
{
    public string? FullName { get; set; }
    
    [JsonConverter(typeof(NonStrictArrayConverter&amp;lt;IReadOnlyList&amp;lt;Vehicle&amp;gt;&amp;gt;))]
    public IReadOnlyList&amp;lt;Vehicle&amp;gt;? Vehicles { get; set; }
    
    [JsonConverter(typeof(NonStrictArrayConverter&amp;lt;List&amp;lt;Color&amp;gt;&amp;gt;))]
    public List&amp;lt;Color&amp;gt;? Colors { get; set; }
    
    public string[]? Aliases { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Couple of unit tests covering different cases: &lt;a href=&quot;https://gist.github.com/kshyju/54d3dd1ee196a4d6c5fd61794d70027a&quot;&gt;https://gist.github.com/kshyju/54d3dd1ee196a4d6c5fd61794d70027a&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/issues/30969#issuecomment-535779492&quot;&gt;https://github.com/dotnet/runtime/issues/30969#issuecomment-535779492&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-core-3-1&quot;&gt;How to write custom converters for JSON serialization&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.text.json.utf8jsonreader?view=net-5.0&quot;&gt;Utf8JsonReader&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf&quot;&gt;JSON attacks&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://blog.maartenballiauw.be/post/2020/01/29/deserializing-json-into-polymorphic-classes-with-systemtextjson.html&quot;&gt;https://blog.maartenballiauw.be/post/2020/01/29/deserializing-json-into-polymorphic-classes-with-systemtextjson.html&lt;/a&gt;(This was a really helpful post for me while writing the converter for the first time)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sat, 24 Apr 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/04/24/system_text_json-deserialize-json-array-in-type-and-value-format.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/04/24/system_text_json-deserialize-json-array-in-type-and-value-format.html</guid>
        
        <category>System.Text.Json deserializing JSON arrays with $type and $values</category>
        
        
      </item>
    
      <item>
        <title>C# to Go - How to wait for an array of sub tasks to finish in Go</title>
        <description>&lt;h2 id=&quot;c-to-go---how-to-wait-for-an-array-of-sub-tasks-to-finish&quot;&gt;C# to Go - How to wait for an array of sub tasks to finish&lt;/h2&gt;

&lt;p&gt;In C#, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task.WhenAll&lt;/code&gt; allows us to wait for a collection of tasks(which may be executed concurrently) to finish. In go, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WaitGroup&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync&lt;/code&gt; package along with goroutines to achieve similar thing.&lt;/p&gt;

&lt;h2 id=&quot;whenall&quot;&gt;WhenAll&lt;/h2&gt;

&lt;p&gt;C# Console program code using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task.WhenAll&lt;/code&gt; to wait for the completion of a bunch of tasks running parallely.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace ConsoleApp
{
    using System;
    using System.Diagnostics;
    using System.Net.Http;
    using System.Threading.Tasks;

    class Program
    {
        static async Task Main(string[] args)
        {
            var start = Stopwatch.StartNew();

            var tasks = new Task[10];

            for (var i = 0; i &amp;lt; 10; i++)
            {
                tasks[i] = MakeRestCallAsync(&quot;https://bing.com&quot;);
            }
            await Task.WhenAll(tasks);

            start.Stop();
            var elapsed = start.Elapsed;

            Console.WriteLine($&quot;Total Elapsed time {elapsed.TotalMilliseconds}ms&quot;);
        }

        static async Task MakeRestCallAsync(string url)
        {
            try
            {
                var start = Stopwatch.StartNew();

                using var httpClient = new HttpClient();
                using HttpResponseMessage resp = await httpClient.GetAsync(url);
                resp.EnsureSuccessStatusCode();

                start.Stop();
                var elapsed = start.Elapsed;
                Console.WriteLine($&quot;{url} {(int)resp.StatusCode} {resp.StatusCode} Elapsed: {elapsed.TotalMilliseconds}ms&quot;);
            }
            catch (Exception ex)
            {
                Console.WriteLine($&quot;{ex}&quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go program (almost) equivalent waiting for parallel sub tasks.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;net/http&quot;
    &quot;sync&quot;
    &quot;time&quot;
)

func main() {

    var wg sync.WaitGroup

    start := time.Now()
    for counter := 0; counter &amp;lt; 10; counter++ {
        wg.Add(1) 
        // &quot;go&quot; keyword prefix to run the method as goroutine, achieving concurrency
        go makeRestCallAsync(&quot;https://bing.com&quot;, &amp;amp;wg)
    }

    wg.Wait()  // wait for all sub tasks to finish

    end := time.Now()
    elapsed := end.Sub(start)

    fmt.Printf(&quot;Total Elapsed time %s&quot;, elapsed)
}

func makeRestCallAsync(url string, wg *sync.WaitGroup) {
    start := time.Now()
    var resp, httpCallError = http.Get(url)
    if httpCallError == nil {
        end := time.Now()
        elapsed := end.Sub(start)
        fmt.Printf(&quot;%s %s Elapsed: %s \n&quot;, url, resp.Status, elapsed)

        wg.Done() // mark this instance of the task as done.
    } else {
        log.Fatal(httpCallError)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check &lt;a href=&quot;https://github.com/kshyju/CSharpToGo&quot;&gt;this github repo&lt;/a&gt; for more samples.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sun, 28 Mar 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/03/28/csharp-to-go-task.whenalll.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/03/28/csharp-to-go-task.whenalll.html</guid>
        
        <category>CSharp to Go</category>
        
        <category>C# to go</category>
        
        <category>Task.WhenAll in Go</category>
        
        <category>When.All in Go</category>
        
        <category>Go Task.WhenAll</category>
        
        <category>Go Wait for goroutines</category>
        
        
      </item>
    
      <item>
        <title>C# to Go - How to use StopWatch in Go</title>
        <description>&lt;h2 id=&quot;c-to-go---how-to-measure-elapsed-time&quot;&gt;C# to Go - How to measure elapsed time&lt;/h2&gt;

&lt;p&gt;StopWatch type in C# provides an easy way to measure the time it took for a specific method execution. In go, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time&lt;/code&gt; package to do similar thing.&lt;/p&gt;

&lt;h2 id=&quot;stopwatch&quot;&gt;Stopwatch&lt;/h2&gt;

&lt;p&gt;C# Console program code using StopWatch to record elapsed time.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace ConsoleApp
{
    using System;
    using System.Diagnostics;
    using System.Net.Http;
    using System.Threading.Tasks;

    class Program
    {
        static async Task Main(string[] args)
        {
            var start = Stopwatch.StartNew();

            for (var i = 0; i &amp;lt; 5; i++)
            {
                await MakeRestCallAsync(&quot;https://bing.com&quot;);
            }

            start.Stop();
            var elapsed = start.Elapsed;

            Console.WriteLine($&quot;Total Elapsed time {elapsed.TotalMilliseconds}ms&quot;);
        }

        static async Task MakeRestCallAsync(string url)
        {
            try
            {
                var start = Stopwatch.StartNew();

                using var httpClient = new HttpClient();
                using HttpResponseMessage resp = await httpClient.GetAsync(url);
                resp.EnsureSuccessStatusCode();

                start.Stop();
                var elapsed = start.Elapsed;
                Console.WriteLine($&quot;{url} {(int)resp.StatusCode} {resp.StatusCode} Elapsed time: {elapsed.TotalMilliseconds}ms&quot;);
            }
            catch (Exception ex)
            {
                Console.WriteLine($&quot;{ex}&quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go program (almost) equivalent for recording elapsed time.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;net/http&quot;
    &quot;time&quot;
)

func main() {

    start := time.Now()
    for i := 0; i &amp;lt; 5; i++ {
        makeRestCallAsync(&quot;https://bing.com&quot;)
    }
    end := time.Now()
    elapsed := end.Sub(start)

    fmt.Printf(&quot;Total Elapsed time %s&quot;, elapsed)
}

func makeRestCallAsync(url string) {
    start := time.Now()
    var resp, httpCallError = http.Get(url)
    if httpCallError == nil {
        end := time.Now()
        elapsed := end.Sub(start)

        fmt.Printf(&quot;%s %s Elapsed time: %s \n&quot;, url, resp.Status, elapsed)
    } else {
        log.Fatal(httpCallError)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check &lt;a href=&quot;https://github.com/kshyju/CSharpToGo&quot;&gt;this github repo&lt;/a&gt; for more samples.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Sun, 21 Mar 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/03/21/csharp-to-go-stopwatch.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/03/21/csharp-to-go-stopwatch.html</guid>
        
        <category>CSharp to Go</category>
        
        <category>C# to go</category>
        
        <category>Stopwatch in Go</category>
        
        <category>Measure method execution time in Go</category>
        
        <category>Go stopwatch</category>
        
        
      </item>
    
      <item>
        <title>C# to Go - How to make HTTP call in Go</title>
        <description>&lt;h2 id=&quot;c-to-go---how-to-make-http-call-in-go&quot;&gt;C# to Go - How to make HTTP call in Go&lt;/h2&gt;

&lt;p&gt;I have been dabbling with Go programming language lately(&lt;em&gt;really late to the party&lt;/em&gt;😅) and surely liking this language. Me, being a C# developer, thought it might be a good idea to rewrite some of the C# snippets as part of learning Go. As I was doing this exercise, I decided to scribble it down here as a quick reference to help anyone taking the same route (C# developer learning Go) or for myself in the future.&lt;/p&gt;

&lt;p&gt;Step 1, &lt;a href=&quot;https://golang.org/dl/&quot;&gt;Install go for your OS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am going to skip the Hello world sample here for obvious reasons and will be doing the Go equivalent of some the the C# snippets we will use in real world application development.&lt;/p&gt;

&lt;h2 id=&quot;http-calls&quot;&gt;Http calls&lt;/h2&gt;

&lt;p&gt;C# Console program code for making Http call&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine(&quot;Http REST call demo&quot;);
        await MakeRestCallAsync(&quot;https://bing.com&quot;);
    }

    static async Task MakeRestCallAsync(string url)
    {
        try
        {
            using var httpClient = new HttpClient();
            using HttpResponseMessage response = await httpClient.GetAsync(url);
            response.EnsureSuccessStatusCode();
            Console.WriteLine($&quot;{url} {(int)response.StatusCode} {response.StatusCode}&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;{ex}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go program (almost) equivalent for making Http call&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;net/http&quot;
)

func main() {
    fmt.Println(&quot;Http REST call demo&quot;)
    makeRestCallAsync(&quot;https://bing.com&quot;)
}

func makeRestCallAsync(url string) {
    var resp, httpCallError = http.Get(url)
    if httpCallError == nil {
        fmt.Printf(&quot;%s %s&quot;, url, resp.Status)
    } else {
        log.Fatal(httpCallError)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check &lt;a href=&quot;https://github.com/kshyju/CSharpToGo&quot;&gt;this github repo&lt;/a&gt; for more samples.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
        <pubDate>Wed, 03 Mar 2021 05:00:00 +0000</pubDate>
        <link>https://techiesweb.net/2021/03/03/csharp-to-go-how-to-make-http-call.html</link>
        <guid isPermaLink="true">https://techiesweb.net/2021/03/03/csharp-to-go-how-to-make-http-call.html</guid>
        
        <category>CSharp to Go</category>
        
        <category>C# to go</category>
        
        <category>HttpClient in Go</category>
        
        <category>REST call in Go</category>
        
        <category>Http call in go</category>
        
        
      </item>
    
  </channel>
</rss>
