This post was authored by Morgan Brown, a Software Development Engineer on the .NET Native team. It is the third post in a series of five about Runtime Directives. Please see the two posts in this series, Dynamic Features in Static Code and Help! I Hit a MissingMetadataException!, before reading this post.
Sometimes the only thing worse than having your app throw an exception is having it not throw an exception (but do the wrong thing anyway.) This post will examine certain corner cases where .NET Native compiled apps might fail due to missing reflection information without throwing a MissingMetadataException and how to debug them when they do.
Are You Sure?
Before trying the other suggestions in this article, it’s worth making sure there really isn’t a MissingMetadataException. We find that lots of components catch all types of exceptions, so a new one might not make it all the way to somewhere you would notice it. To find that out for sure, enable debugger breaking when an exception is thrown (a.k.a. “first-chance exception handling”) and debug your app to see if there are any MissingMetdadataExceptions. If there are, take a look at Help! I Hit a MissingMetadataException! for ideas on how to handle it. To set up breaking in the debugger:
- From the Visual Studio “Debug” menu, select “Exceptions…”
- Next to “Common Language Runtime Exceptions,” check the “Thrown” checkbox
- Click “OK”
- Debug your app and try the scenario that isn’t working right
- If any exception is thrown, the debugger will stop and give you information about it
Data Binding Troubles?
Ok, so you really didn’t get a MissingMetadataException. That probably means you’re working with a component that’s tolerant to things being missing. One of the most common cases for that is XAML data binding – it’s not an error to bind to something that doesn’t exist (but of course, something’s likely to be missing from your UI if the binding doesn’t work). Fortunately, even though it doesn’t throw any exceptions, broken data bindings get logged to the Visual Studio debug output window. To get them:
- In the Visual Studio “Debug” menu, under “Windows,” select “Output”.
- Debug your app and make sure to go to the page that is missing something.
- Check the text in the “Output” window. You might need to scroll up a little to find it.
A binding error usually looks something like:
"Error: BindingExpression path error: 'MissingProperty' property not found
on MyApp.Data.SampleDataGroup, MyApp, Version=126.96.36.199, Culture=neutral,
PublicKeyToken=null'. BindingExpression: Path='MissingProperty'
DataItem=MyApp.Data.SampleDataGroup, MyApp, Version=188.8.131.52,
Culture=neutral, PublicKeyToken=null'; target element is
'Windows.UI.Xaml.Controls.TextBlock' (Name='MyTextBlock'); target
property is 'Text' (type 'String')"
That would mean XAML bound to the class SampleDataGroup, but when it tried to bind to the MissingProperty property on SampleDataGroup, it wasn’t found. That’s something we can fix with a runtime directive (take a look at Dynamic Features in Static Code if you haven’t used those yet). A good one for this case might be:
<Type Name="MyApp.Data.SampleDataGroup" Dynamic="Required Public" />
which means that all public members of SampleDataGroup should be kept available to reflection so that data binding can bind to it.
Other Stuff Broken?
If some reflection code of yours or in a NuGet library isn’t doing what you expect it to or is throwing a different exception, the problem might vary. That said, here are a few things you can try to get to the bottom of the issue:
- Start off by checking back with the library website or author. With luck, they’ll have updated their library for .NET Native. If they haven’t, this series has a post on authoring Runtime Directives for libraries that they should read. While you’re waiting on that, keep reading.
A common case for silent failures is that some code iterates collections like Assembly.ExportedTypes or TypeInfo.DeclaredMembers and does something with the values that get returned. Those and similar properties only return types and members if they have been metadata enabled. That means if some of your types aren’t enabled, something iterating over a set of types won’t know they’re there and might miss processing them. If you’re expecting a callback that never comes or only some properties of a class get serialized, those might be hints you’re seeing this case. If you wrote the reflection code in question, hopefully you can work out what types you’re expecting that are missing. If you didn’t write the code, try starting with enabling metadata for the types or members you were hoping would get used by the library. For example, if you were expecting the MyClass.MyMethod() method to be called and it wasn’t, try adding this to your RD.xml:
<Type Name="MyClass" Dynamic="Required All" />
If you can, try to make a general conclusion about what might be needed so that you can write a directive that covers roughly the right set of things (by using a GenericParameter or Namespace directive, for instance).
If you still haven’t found what might need a runtime directive, it’s time to put on your code detective hat. When working through similar issues, we’ve been able to find out what’s missing by paying attention to the debug output window and by breaking on exceptions that aren’t MissingMetadataExceptions and trying to make sense of the context. Sometimes those things will mention a type or something you’ve asked a library to do that can give an idea of things to try.
It’s no fun, but brute force can sometimes get you through silent failures. Try adding directives for whole namespaces or assemblies. Hopefully, one of those fixes the issue. Be careful when you do this though – you can cause lots of extra code to be compiled that will never run. That can make your app bigger, slower, and make building it take much longer. If you solve a bug through adding a ton of stuff, try using that as a starting point to narrow back down to a more reasonable set of types you actually need.
We welcome your feedback about .NET Native. Please feel free to send us mail at email@example.com with any feedback, suggestions or concerns.