C# dynamic is a trap door: stop the leaks before they spread (Must read for Dapper users) — DeepSeek Blog | Neura Market
    Neura MarketNeura Market/DeepSeek
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsBlogVideosGuidesCoursesCommunityTrendingGenerate
    DeepSeekBlogC# dynamic is a trap door: stop the leaks before they spread (Must read for Dapper users)
    Back to Blog
    C# dynamic is a trap door: stop the leaks before they spread (Must read for Dapper users)
    csharp

    C# dynamic is a trap door: stop the leaks before they spread (Must read for Dapper users)

    Dmitry Dorogoy January 14, 2026
    0 views

    In C#, dynamic can be handy at boundaries. The problem is that it can silently leak into places where...

    In C#, `dynamic` can be handy at boundaries. The problem is that it can silently leak into places where you expect static typing, and your method signatures can still look perfectly safe. I ran into these leaks in real projects, so I built a small Roslyn analyzer to catch them early. I am the author of **[DynamicLeakAnalyzer](https://github.com/DimonSmart/DynamicLeakAnalyzer)** (NuGet: `DimonSmart.DynamicLeakAnalyzer`), and this post explains the problem it targets and how to fix leaks at the boundary. ## What `dynamic` really means In C#, `dynamic` is a **static type**, but it **bypasses compile-time type checking** for the expression. Member access, overload resolution, operators, and conversions are bound at runtime. At runtime, values still flow as `object`. The difference is that the compiler emits runtime binding (DLR call sites). Those call sites have caching, so repeated calls can get faster after the first bind. ## How "dynamic contagion" happens Two patterns make leaks hard to spot during review: - **Invisible runtime work:** member access and conversions happen at runtime. - **Deceptive signatures:** a method can return `int` and still perform dynamic conversions inside. - **`var` can silently become `dynamic`:** when the right side is dynamic, the inferred type becomes dynamic. Here is a small example that shows both problems. The method looks fully static and returns `int`, yet dynamic enters through the parameter. ```csharp class Program { static int GetX(int i) => i; static void Main() { dynamic prm = 123; int a = GetX(prm); // DSM001: implicit dynamic conversion at runtime var b = GetX(prm); // DSM002: b becomes dynamic because the invocation is dynamic } } ``` In real code, `prm` is often not a local variable. It can be an object passed into the method, and dynamic can hide in a field or property, for example `prm.Payload.Id`. ## The Dapper trap Dapper makes it easy to introduce `dynamic` without noticing it. Calling `QueryFirst` without `<T>` returns a dynamic row object (usually `DapperRow`), so property access becomes dynamic. ```csharp using Dapper; using System.Data; public static class Repo { public static int GetActiveUserId(IDbConnection cn) { // QueryFirst() without <T> returns a dynamic row (DapperRow). var row = cn.QueryFirst("select Id from Users where IsActive = 1"); // row.Id is dynamic, the conversion to int happens at runtime. return row.Id; // DSM001 } } ``` This kind of code often passes reviews because the signature says `int`. The dynamic binding is hidden in the middle. ### Safer alternatives in Dapper Prefer typed APIs at the boundary: ```csharp int id = cn.QuerySingle<int>("select Id from Users where IsActive = 1"); ``` Or map to a small DTO: ```csharp public sealed record UserId(int Id); int id = cn.QuerySingle<UserId>("select Id from Users where IsActive = 1").Id; ``` If you really must use a dynamic row, kill it immediately: ```csharp var row = cn.QueryFirst("select Id from Users where IsActive = 1"); int id = (int)row.Id; ``` The goal is not "never use dynamic". The goal is "stop the leak at the boundary". ## The solution: DynamicLeakAnalyzer **DynamicLeakAnalyzer** is a Roslyn analyzer that makes these leaks loud before they spread. It reports two rules: - **DSM001 (Implicit dynamic conversion):** a `dynamic` expression is used where a static type is expected (return, assignment, argument, etc.). The code compiles, but the conversion happens at runtime. - **DSM002 (`var` inferred as `dynamic`):** `var` captures a dynamic result and becomes dynamic. ## Install and enforce Add the analyzer: ```bash dotnet add package DimonSmart.DynamicLeakAnalyzer ``` Make warnings hurt using `.editorconfig`: ```ini root = true [*.cs] dotnet_diagnostic.DSM001.severity = error dotnet_diagnostic.DSM002.severity = error ``` ## Where `dynamic` is fine, and where it is not Good boundary examples: - COM interop - JSON adapters and glue code - database adapters (including dynamic Dapper rows) Avoid `dynamic` in: - core domain logic - hot loops - libraries meant for other developers ## Next steps - Run the analyzer on a real codebase and see where dynamic leaks already exist. - If you use Dapper, search for `QueryFirst(` and non-generic `Query(` calls that return dynamic rows. - If you have false positives or missed cases, open an issue with a minimal repro.

    Tags

    csharproslyndapperdotnet

    Comments

    More Blog

    View all
    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠ai

    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠

    Hi everyone! 👋 I’m Tara, a Senior Software Engineer and Consultant. Over the years, I've jumped...

    T
    tworrell
    Local AI Will Save Us All (The Math Says So, Trust Me)ai

    Local AI Will Save Us All (The Math Says So, Trust Me)

    Every few weeks a take goes viral in tech circles making the case for ditching cloud AI and running...

    S
    Sebastian Schürmann
    Lost in the AI Hype, I Started Smallai

    Lost in the AI Hype, I Started Small

    And it helped me get back into tech without drowning TL;DR at the end Coming back to...

    R
    Rohini Gaonkar
    Building a Replay-Tested Interactive Brokers Client in Gogo

    Building a Replay-Tested Interactive Brokers Client in Go

    I wanted an IBKR library that felt like Go and had testing I could trust. So I wrote one.

    T
    Thomas Marcelis
    Playwright in Pictures: Fully Parallel Modeplaywright

    Playwright in Pictures: Fully Parallel Mode

    Playwright’s fullyParallel mode is often treated as a simple performance switch. In practice, it...

    V
    Vitaliy Potapov
    Designing a CLI for Both Humans and Agentscli

    Designing a CLI for Both Humans and Agents

    Learn how Alpic designed its CLI for both human developers and AI agents — covering tradeoffs like polling, context windows, interactivity, and statelessness.

    J
    Julien Vallini

    Stay up to date

    Get the latest DeepSeek prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for DeepSeek and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.