IL2122: Type 'type' is not assembly qualified. Type name strings used for dynamically accessing a type should be assembly qualified

Cause

Type name strings representing dynamically accessed types must be assembly qualified. Otherwise, the lookup semantics of Type.GetType will search the assembly with the Type.GetType callsite and the core library. The assembly with the Type.GetType callsite may be different than the assembly which passes the type name string to a location with DynamicallyAccessedMembersAttribute, so the tooling cannot determine which assemblies to search.

Example

// In Assembly 1
void TestInvalidTypeName()
{
    // IL2122: Type 'MyType' is not assembly qualified. Type name strings used for dynamically accessing a type should be assembly qualified.
    RequirePublicMethodOnAType("MyType");
}

void ForwardTypeNameToAnotherMethod(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
    string typeName)
{
    MyTypeFromAnotherAssembly.GetType(typeName);
}
// In Assembly 2
public class MyTypeFromAnotherAssembly
{
    void GetTypeAndSearchThroughMethods(
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
        string typeName)
    {
        Type.GetType(typeName).GetMethods();
    }
}

class MyType
{
    // ...
}

In a non-trimmed app, at runtime the Type.GetType call will discover MyType in Assembly 2. However, trimming will remove MyType because the trimming tools don't have enough information to determine where the type will be found at runtime.

To fix this, consider using a fully-qualified type name instead:

RequirePublicMethodsOnAType("MyType,Assembly2");

Another option is to pass the unqualified type name string directly to Type.GetType, and avoid annotations on string:

var type = Type.GetType("MyType");

void SearchThroughMethods(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
    Type type)
{
    type.GetMethods();
}

This gives the trimming tools enough information to look up the type using the same semantics that GetType(String) has at runtime, causing the type (and public methods in this example) to be preserved.