A blog about my adventures in the .NET/C# world and the Unity Engine.

Debugging MEF

Finding the root cause of failed dependency tree construction

Andreas Schmitt

3 minutes read

If you have ever worked with a Dependency Injection container like MEF, you have probably run into the problem of types not being resolved correctly. Since MEF works mainly by attributes, typically the Export attribute on the class declaration, all it takes is a missing attribute for some type to go missing during bootstrapping.

This can also be the case in a test application, when most of the classes of one layer have been mocked away, and there is always that one mock that cannot be found. This does not sound like a major issue but the console output of MEF is not always extremely helpful to diagnose this problem. If a type cannot be resolved it throws an exception with a pretty readable error message. But the type the cannot be resolved is rarely the root of the problem. It is just the latest error in a potentially long chain.

If type A cannot be constructed then the exception might tell you about a type D which imports type C which imports type B which imports type A. MEF only tells you the latest type in a chain that cannot be successfully built because one of its imports cannot be constructed. But that import also has a reason to not be constructed, and so on and so on. Often you just get an error saying that your ShellViewModel cannot be constructed. Well, good luck finding out what the problem is based on that.

So how can we fix this. First of all we need more information about which types cannot be constructed. We need more than just the latest effect in the chain. We need the entire chain.

private static List<string> GetMissingImports(CompositionContainer container )
{
    return container.Catalog
                    .SelectMany( part => part.ImportDefinitions )
                    .Where( impDef => IsInvalidImport(container, impDef) )
                    .Select( impDef => impDef.ContractName )
                    .Distinct()
                    .OrderBy( contract => contract )
                    .ToList();
}

private static bool IsInvalidImport(CompositionContainer container, ImportDefinition impDef )
{
    try
    {
        container.GetExports( impDef );
    }
    catch
    {
        return true;
    }

    return false;
}

Now this is better. This gives us all the imports that failed, not just the latest one. But we still do not know which one of them was the root cause, which one is actually missing and not just failing because one of its dependencies is missing. So we need to filter this further. We need all imports that fail because they cannot be found, not the ones that fail because they were found but could not be constructed. So let’s try this again:

private static List<string> GetMissingImports(CompositionContainer container )
{
    var brokenExports = GetBrokenExports(container);

    return container.Catalog
                    .SelectMany( part => part.ImportDefinitions )
                    .Where( impDef => IsInvalidImport(container, impDef) )
                    .Select( impDef => impDef.ContractName )
                    .Distinct()
                    .Where( contract => !brokenExports.Contains( contract ) )
                    .OrderBy( contract => contract )
                    .ToList();
}

private static List<string> GetBrokenExports(CompositionContainer container)
{
    return container.Catalog
                    .Where( part => part.ImportDefinitions.Any( impDef => IsInvalidImport(container, impDef) ) )
                    .SelectMany( part => part.ExportDefinitions )
                    .Select( exDef => exDef.ContractName )
                    .Distinct()
                    .OrderBy( contract => contract )
                    .ToList();
}

This gives us what we need. It will print out only the root causes of failed construction. Obviously, this should probably not end up in production code. But a small helper class with a method you can call in your bootstrapper when you are debugging a failed startup can be a huge time saver.

You can find the entire example source code on GitHub.

Recent posts

See more

About

My name is Andreas Schmitt. I am a Software Engineer and Indie Game Developer from Germany