Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
155277 stories
·
33 followers

The 60 Second Habit That Builds Better Leadership Instincts

1 Share

The smartest leadership tool you've got isn't in any AI model. It's your gut.

Bob and Josh dig into why intuition still wins the hard calls, the ones data alone can't make for you. Bob owns up to the hires he botched by ignoring his gut. Josh shares the story of the client who told him he was being paid for his pattern recognition and how that changed everything.

Reading a room. Trusting yourself before anyone else does. The 60-second habit that sharpens your instincts. And why even perfect data shouldn't get the final word.

If you've been outsourcing your judgment to a chatbot, this one's for you.


Stay Connected and Informed with Our Newsletters

Josh Anderson's "Leadership Lighthouse"

Dive deeper into the world of Agile leadership and management with Josh Anderson's "Leadership Lighthouse." This bi-weekly newsletter offers insights, tips, and personal stories to help you navigate the complexities of leadership in today's fast-paced tech environment. Whether you're a new manager or a seasoned leader, you'll find valuable guidance and practical advice to enhance your leadership skills. Subscribe to "Leadership Lighthouse" for the latest articles and exclusive content right to your inbox.

Subscribe here

Bob Galen's "Agile Moose"

Bob Galen's "Agile Moose" is a must-read for anyone interested in Agile practices, team dynamics, and personal growth within the tech industry. The newsletter features in-depth analysis, case studies, and actionable tips to help you excel in your Agile journey. Bob brings his extensive experience and thoughtful perspectives directly to you, covering everything from foundational Agile concepts to advanced techniques. Join a community of Agile enthusiasts and practitioners by subscribing to "Agile Moose."

Subscribe here

Do More Than Listen:

We publish video versions of every episode and post them on our YouTube page.

Help Us Spread The Word: 

Love our content? Help us out by sharing on social media, rating our podcast/episodes on iTunes, or by giving to our Patreon campaign. Every time you give, in any way, you empower our mission of helping as many agilists as possible. Thanks for sharing!





Download audio: https://episodes.captivate.fm/episode/5e651f56-e7cf-4c47-9d41-21feffb78d85.mp3
Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

ASP.NET Core background tasks with NCronJob and SignalR

1 Share

I was recommended NCronJob for implementing a background worker in ASP.NET Core and so I decided to give it a try, read the docs and learn this. This NuGet package is open source and works great. I implemented two simple jobs, one concurrent and one not concurrent which sends messages via SignalR.

Code: https://github.com/damienbod/AspNetCoreNCronJob

To implement a demo feature, I used a SignalR service to display both concurrent and non-concurrent messages in an ASP.NET Core Razor Pages UI. Messages are sent every five seconds, when possible. In ASP.NET Core, this only requires implementing a Hub. For this purpose, I created two methods.

using Microsoft.AspNetCore.SignalR;

namespace AspNetCoreNCronJob;

public class JobsHub : Hub
{
    public Task SendConcurrentJobsMessage(string message)
    {
        return Clients.All.SendAsync("ConcurrentJobs", message);
    }

    public Task SendNonConcurrentJobsMessage(string message)
    {
        return Clients.All.SendAsync("NonConcurrentJobs", message);
    }
}

The NCronJob is a simple class that implements the IJob interface. The RunAsync methos is run depending on how the interface is setup in the services definitions. This class uses dependency injection and sends messages to registered SignalR clients.

using Microsoft.AspNetCore.SignalR;
using NCronJob;

namespace AspNetCoreNCronJob.NCronJobServices;

[SupportsConcurrency(5)]
public class NonConconcurrentJob : IJob
{
    private readonly ILogger<NonConconcurrentJob> _logger;
    private static int _counter = 0;
    private readonly IHubContext<JobsHub> _hubContext;

    public NonConconcurrentJob(ILogger<NonConconcurrentJob> logger,
           IHubContext<JobsHub> hubContext)
    {
        _logger = logger;
        _hubContext = hubContext;
    }

    public async Task RunAsync(IJobExecutionContext context, CancellationToken token)
    {
        var count = _counter++;

        var beginMessage = $"NonConcurrentJob Job BEGIN {count} {DateTime.UtcNow}";
        await _hubContext.Clients.All.SendAsync("NonConcurrentJobs", beginMessage);
        _logger.LogInformation("{BeginMessage}", beginMessage);

        await Task.Delay(7000, token);

        var endMessage = $"NonConcurrentJob Job END {count} {DateTime.UtcNow}";
        await _hubContext.Clients.All.SendAsync("NonConcurrentJobs", endMessage);
        _logger.LogInformation("{EndMessage}", endMessage);
    }
}

The ASP.NET Core UI uses the SignalR Javascript library to to connect to the SignalR server and consume the messages. The messages are displayed in the UI.

This is super simple to use and provides all of the features I need in most of my scheduling requirements.

Links

https://github.com/NCronJob-Dev/NCronJob

https://docs.ncronjob.dev/

https://steven-giesel.com/blogPost/fb1ce2ab-dd27-43ed-aaab-077adf2d15cd

https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction



Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Chris "Woody" Woodruff: AI-Assisted Software Architecture - Episode 405

1 Share

https://clearmeasure.com/developers/forums/

Chris Woodruff, or as his friends call him, Woody, is a software architect of over 25 years. Woody loves software engineering, especially allowing applications and services to communicate across networks and through Web APIs. He has received Microsoft MVP awards in SQL, Data and C# in the past, along with multiple years of being awarded the AWS Community Builder Award. He's a current board member of the .NET Foundation Woody lives in Grand Rapids, Michigan, where he explores the many breweries in West Michigan and travels with his family. Woody is also a long-time bourbon fan and loves hunting for whiskey bottles.

Website - https://woodruff.dev/
LinkedIn - https://www.linkedin.com/in/chriswoodruff/
Twitter - https://twitter.com/cwoodruff
Simplicity-First Website - https://simplicity-first.dev/

Previous Appearances on the Azure & DevOps Podcast:

Episode 262 - Chris "Woody" Woodruff: Network Programming https://azuredevopspodcast.clear-measure.com/chris-woody-woodruff-network-programming-episode-262

----------------------------------------
Want to Learn More?
Visit AzureDevOps.Show for show notes and additional episodes.





Download audio: https://traffic.libsyn.com/clean/secure/azuredevops/Episode_405.mp3?dest-id=768873
Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Corvus.Text.Json V5: Why V5 Exists

1 Share

At endjin, we maintain Corvus.JsonSchema, an open source high-performance library for serialization and validation of JSON using JSON Schema.

Today we're introducing something we've been working on for a while: the V5 engine. It is a new code generator and runtime library that sits alongside our existing V4 engine, and represents a fundamentally different set of design trade-offs.

In this series, we'll walk through what V5 brings, why it exists, and how to get started.

The problem we're solving

If you work with JSON in .NET, you've probably noticed a few gaps in the built-in tooling.

System.Text.Json provides excellent low-level readers and writers, and a high-performance serialization framework for code-first projects. But it has no built-in schema validation, no query language support, and its mutable document model (JsonNode) allocates per node with no control over memory lifetime.

Meanwhile, JSON itself has become the lingua franca of APIs, configuration, message passing, and data exchange. You need to parse it, validate it, query it, transform it, patch it, and write it back out. This often happens in a high-throughput request/response pipeline where every allocation matters.

That's what Corvus.Text.Json is for.

Two engines, one toolchain

The V5 engine doesn't replace V4. It offers a different set of trade-offs.

Aspect V4 (Corvus.Json) V5 (Corvus.Text.Json)
Mutation model Immutable-functional - With*() returns a new instance Mutable builder - Set*() mutates in-place
Memory JsonElement backed by JsonDocument ParsedJsonDocument<T> backed by ArrayPool<byte>
Safety Stale references impossible (immutable) Version-tracked stale reference detection
Performance Good Considerably faster, lower allocation
Target frameworks net8.0, net10.0, netstandard2.0 net9.0, net10.0, netstandard2.0, netstandard2.1

V4 is the right choice when you want the guarantees of an immutable document model - no aliasing bugs, no stale references, and the ability to share values freely across threads.

V5 is the right choice when you're building request/response pipelines, message processing, or other scenarios where you create a document, use it briefly, and dispose it. The builder pattern pools memory across operations, dramatically reducing GC pressure.

Both engines ship in the same CLI tool (corvusjson) and share the same JSON Schema analysis engine. You choose which to use with the --engine flag.

What V5 brings

Pooled-memory parsing

ParsedJsonDocument<T> uses ArrayPool<byte> for its backing memory. It adds just 136 bytes of GC pressure per document, regardless of size, compared with 1,528 bytes for a comparable JsonNode. That's 92% less memory.

using var doc = ParsedJsonDocument<Person>.Parse(
    """{"name":"Alice","age":30}""");
Person person = doc.RootElement;
string name = (string)person.Name;  // "Alice"

Mutable documents with JsonWorkspace

JsonDocumentBuilder<T> and JsonWorkspace provide a pooled builder pattern for creating and modifying JSON in place, with version tracking to catch stale element references at runtime.

using JsonWorkspace workspace = JsonWorkspace.Create();
using var builder = person.CreateBuilder(workspace);
Person.Mutable root = builder.RootElement;
root.SetAge(31);
Console.WriteLine(root.ToString()); // {"name":"Alice","age":31}

Source-generated types

The [JsonSchemaTypeGenerator] attribute triggers a Roslyn incremental source generator that produces strongly-typed C# models from JSON Schema at build time. You get full IntelliSense as you type.

using Corvus.Text.Json;

namespace MyApp.Models;

[JsonSchemaTypeGenerator("Schemas/person.json")]
public readonly partial struct Person;

Schema validation - over 10× faster

Full JSON Schema validation for drafts 4, 6, 7, 2019-09, and 2020-12 (including OpenAPI 3.0 and 3.1). Over 10× faster than other .NET JSON Schema validators, with detailed hierarchical diagnostics when you need them.

bool valid = person.EvaluateSchema();  // true

Three query and transformation languages

V5 includes complete implementations of three JSON query/transformation languages, all with 100% conformance against their official test suites:

  • JSONata - a Turing-complete functional query and transformation language. On average 2× faster than Jsonata.Net.Native with 90–100% less memory allocation across 32 benchmarks.
  • JMESPath - a standard query language used by AWS CLI and Azure CLI. On average 28× faster than JmesPath.Net with zero allocation across 21 benchmarks.
  • JsonLogic - a safe, side-effect-free rule engine for evaluating business rules stored as JSON. On average 3× faster than JsonEverything with zero allocation across 19 benchmarks.

All three support interpreted runtime evaluation, build-time source generation, and CLI code generation.

YAML 1.2 to JSON

YAML is a superset of JSON, and in practice the two are interchangeable in many configuration and data-exchange scenarios. Kubernetes manifests, OpenAPI specifications, GitHub Actions workflows, Azure DevOps pipelines, Helm charts, Docker Compose files - a huge amount of the infrastructure-as-code ecosystem uses YAML as its primary format. If you're processing these in .NET, you eventually need to convert YAML to JSON so you can validate, query, or transform it with standard JSON tooling.

V5 includes a zero-allocation ref struct YAML 1.2 tokenizer that converts directly to a ParsedJsonDocument<T> or a System.Text.Json.JsonDocument with 100% yaml-test-suite conformance. There is no intermediate object model and no per-node allocations.

JSON Patch, JSON Pointer, and extended types

  • JSON Patch - RFC 6902 with a fluent PatchBuilder and pooled-memory operations
  • JSON Pointer - RFC 6901 zero-allocation path resolution with source location mapping
  • BigNumber - arbitrary-precision decimal arithmetic without floating-point conversion
  • NodaTime - first-class support for LocalDate, OffsetDateTime, Period, and other temporal types

At a glance: System.Text.Json vs Corvus.Text.Json

Feature System.Text.Json Corvus.Text.Json
Read-only parsing JsonDocument (pooled) ParsedJsonDocument<T> (pooled, generic)
Mutable documents JsonNode (allocates per node) Builder pattern on pooled memory
Schema validation None built-in Draft 4/6/7/2019-09/2020-12, detailed diagnostics
Code generation Serialization to/from POCOs Strongly-typed entities from JSON Schema
Date/Time DateTime, DateTimeOffset All .NET types plus NodaTime
Numeric precision decimal (28 digits) BigNumber (arbitrary precision), Int128, Half
Query languages None JSONata, JMESPath, JsonLogic
YAML support None YAML 1.2 with zero-allocation tokenizer

Getting started

# Core library
dotnet add package Corvus.Text.Json

# Source generator
dotnet add package Corvus.Text.Json.SourceGenerator

# CLI code generator
dotnet tool install --global Corvus.Json.Cli

What's coming in this series

We'll start with the developer experience: how the source generator and CLI tool turn a JSON Schema into a strongly-typed C# struct with full IntelliSense, and how the standalone evaluator gives you annotation collection for schema-driven tooling like form generators.

Then we'll dig into the runtime. We'll look at how ParsedJsonDocument<T> achieves 136 bytes per document with pooled-memory parsing, how the mutable JsonDocumentBuilder trades V4's immutable safety for in-place mutation with version tracking, and how schema validation runs over 10× faster across all major drafts.

From there, we'll move into the three query and transformation languages. JSONata handles general-purpose JSON transformation, JMESPath provides the concise extraction queries used by AWS and Azure CLIs, and JsonLogic offers safe, side-effect-free business rules you can store in a database and evaluate without code execution.

We'll cover YAML 1.2 conversion with a zero-allocation tokenizer that achieves 100% yaml-test-suite conformance, JSON Patch for RFC 6902 document mutation with a fluent builder, JSON Pointer for zero-allocation path resolution, and V5's extended type system with UTF-8 URI/IRI parsing, arbitrary-precision numerics, and first-class NodaTime integration.

Finally, we'll wrap up with the migration path from V4 to V5, the 10 production Roslyn analyzers that ship with the library, and how to get started.



Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

MCP integration in the GitHub Copilot SDK

1 Share

After a short break, I'm back with my series about the GitHub Copilot SDK. So far, we have covered:

Today, we cover MCP integration — connecting your agent to external context and services via the Model Context Protocol, so your agent can reach databases, APIs, and cloud services without you having to build the glue.

The agent we've built so far knows how to reason and plan, and can call C# tools you define directly. But there's a whole ecosystem of pre-built capabilities out there. Servers that talk to Azure, GitHub, databases, Slack, internal systems — all speaking the same open standard. MCP is how your agent connects to that ecosystem. Instead of writing tool integrations from scratch, you point the SDK at an MCP server and the agent gains its entire tool catalogue instantly.

What MCP actually is

Model Context Protocol is an open standard for connecting AI assistants to external tools and data sources. An MCP server is a process that exposes a set of named tools via a standardised protocol. The client, in this case the Copilot SDK, discovers what tools the server offers, and the agent can invoke them the same way it invokes any other tool: by deciding it needs one, forming a call, getting a result, and incorporating that into its reasoning.

The key point is that the agent doesn't care whether a tool is defined in your C# code or served by an external MCP process. From the agent's perspective, tools are tools. MCP just dramatically expands how many you can have without writing any integration code.

Two flavours

The SDK supports two types of MCP server connection: local servers that run as a subprocess communicating over stdio, and remote servers accessible over HTTP.

  • Local (stdio) — the SDK spawns the MCP server as a child process and communicates with it over standard input/output. This is the most common pattern for servers distributed as npm packages or executables. The server starts with your application and stops with it.
  • Remote (HTTP) — the SDK connects to an MCP server running at a URL. Useful for shared internal servers, SaaS-hosted MCP endpoints, or any server you don't want to run locally. Authentication is typically handled via headers.

Both are configured the same way in .NET: through a McpServers dictionary on SessionConfig.

Connecting a local MCP server

Here's the minimal setup to connect a local MCP server — in this case one distributed as an npm package, started with npx:

var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-4.1",
    McpServers = new Dictionary<string, McpServerConfig>
    {
        ["my-server"] = new McpStdioServerConfig
        {
            Command = "npx",
            Args = ["-y", "@my-org/my-mcp-server@latest"],
            Tools = ["*"]  // "*" = all tools, [] = none, or list specific tool names
        }
    }
});

The tools: ["*"] parameter is essential — it enables all tools from the MCP server for the session. If you want the agent to only have access to a subset of what the server exposes, list the tool names explicitly:

Tools = ["read_file", "search_files", "list_directory"]

You can also pass environment variables and set the working directory for the spawned process:

["my-server"] = new McpStdioServerConfig
{
    Command = "node",
    Args = ["./mcp-server.js"],
    Env = new Dictionary<string, string>
    {
        ["DEBUG"] = "true",
        ["DB_CONNECTION"] = Environment.GetEnvironmentVariable("DB_CONNECTION")!
    },
    WorkingDirectory = "./servers",
    Tools = ["*"],
    Timeout = 30000
}

Connecting a remote MCP server

For remote servers, provide a URL instead of a command. Authentication is passed via headers:

["github"] = new McpHttpServerConfig
{
    Url = "https://api.githubcopilot.com/mcp/",
    Headers = new Dictionary<string, string>
    {
        ["Authorization"] = $"Bearer {Environment.GetEnvironmentVariable("GITHUB_TOKEN")}"
    },
    Tools = ["*"]
}

Remote MCP servers are useful for any capability you want to centralise — a shared internal knowledge base, a company-wide data access layer, or a third-party SaaS integration that's already running at a stable URL.

Multiple MCP servers in one session

A session can have multiple MCP servers attached simultaneously. The agent picks the right tool from whichever server provides it, based on the tool descriptions:

var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-4.1",
    McpServers = new Dictionary<string, McpServerConfig>
    {
        ["azure-mcp"] = new McpStdioServerConfig
        {
            Command = "npx",
            Args = ["-y", "@azure/mcp@latest", "server", "start"],
            Tools = ["*"]
        },
        ["github"] = new McpHttpServerConfig
        {
            Url = "https://api.githubcopilot.com/mcp/",
            Headers = new Dictionary<string, string>
            {
                ["Authorization"] = $"Bearer {Environment.GetEnvironmentVariable("GITHUB_TOKEN")}"
            },
            Tools = ["*"]
        },
        ["internal-kb"] = new McpHttpServerConfig
        {
            Url = "https://mcp.internal.yourcompany.com/knowledge-base",
            Headers = new Dictionary<string, string>
            {
                ["X-API-Key"] = Environment.GetEnvironmentVariable("KB_API_KEY")!
            },
            Tools = ["search_docs", "get_runbook"]
        }
    }
});

With these three servers attached, the agent can cross-reference Azure resource state, GitHub issues, and internal runbooks in a single conversation — without you writing any routing logic between them.

Tool filtering: Granting minimal capability

One of the more useful aspects of MCP integration is fine-grained tool control. Rather than granting "*" (all tools), you can specify exactly which tools from a server the agent is allowed to call. This is the principle of least privilege applied to your agent.

// Only allow read operations from the Azure MCP server
["azure-mcp"] = new McpStdioServerConfig
{
    Command = "npx",
    Args = ["-y", "@azure/mcp@latest", "server", "start"],
    Tools =
    [
        "azure_resource_group_list",
        "azure_storage_account_list",
        "azure_subscription_list"
        // Deliberately not including write or delete operations
    ]
}

This matters in production. An agent that can only read from Azure can't accidentally delete a resource group, even if it's prompted to try. Match the tool access to what the agent's task actually requires.

What's next

You now have an agent that can reach well beyond your application boundary — into Azure, GitHub, internal systems, and any MCP-compatible service — without writing the integration code yourself.

In the next post, we take a look at Skills. Skills are about what the agent knows — and more specifically, how to apply that knowledge in a repeatable, composable way

More information

Using MCP servers with the GitHub Copilot SDK - GitHub Docs

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

EF Core 11 makes your split queries faster

1 Share

If you use AsSplitQuery anywhere in your codebase, EF Core 11 has a present for you: your queries get faster.

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories