Skip to the content.

Type is only generic by containment

Interceptors cannot handle types that involve generic type parameters (see DAP016), and one way to involve them is easy to do by accident: declaring an otherwise generic-free type inside a generic class. For example:

public class AnimalTests<TProvider>
{
    class Dog // this actually means AnimalTests<TProvider>.Dog
    {
        public int Age { get; set; }
        public string? Name { get; set; }
    }

    public void GetDogs(DbConnection connection)
        => connection.Query<Dog>("select Age, Name from Dogs"); // DAP051
}

Here Dog does not use TProvider at all - but because it is declared inside AnimalTests<TProvider>, its full identity is AnimalTests<TProvider>.Dog, which is a different type for every TProvider. Generated code cannot name such a type, so Dapper.AOT has to leave the call-site on vanilla Dapper (reflection), which will not work under AOT.

The fix: if the type does not need the enclosing type parameters, move it out of the generic type - to namespace scope, or into a non-generic containing class:

class Dog // no longer entangled with TProvider
{
    public int Age { get; set; }
    public string? Name { get; set; }
}

public class AnimalTests<TProvider>
{
    public void GetDogs(DbConnection connection)
        => connection.Query<Dog>("select Age, Name from Dogs"); // works with Dapper.AOT
}

If the type genuinely needs the enclosing type parameters, this is the same limitation as DAP016: generic types cannot currently be used with Dapper.AOT.