Using ResourceManger with a CustomAssembly

Using ResourceManger with a CustomAssembly

ResourceMangers provide convenient access to culture-specific resources at run time. Resources may reside in the current or external assemblies. A ResourceManager can be created by specifying the base name of the resource file and assembly where it resides. However one constraint it not documented, the provided assembly must be of the type “ RuntimeAssembly” (not public). While most of the time you will, without knowing, be dealing with RuntimeAssemblies it is possible to end up with System.Reflection.Context.Custom.CustomAssembly instead which is not a RuntimeAssembly. The trouble begins…

1
new ResourceManager("Resources.String", customType.Assembly); // This will result in an error

Example of the error

This post is written for .NET framework 4.8, although it applies to the latest version as well (.NET 5).

Problem at hand

There are many ways to get hold of the assembly to pass to the ResourceManger, one way is to use the Assembly property on Type. When using CustomReflectionContext, which allows one to add custom attributes to existing types, one may end up with a System.Reflection.Context.Custom.CustomType. Its Assembly property will return a System.Reflection.Context.Custom.CustomAssembly, which as noted before is not compatible with the ResourceManger. ReflectionContext can be used in many .NET reflection API’s and e.g. in MEF.

The constraint on using a RuntimeAssembly is nowhere documented, but the code, and if attempted will result in a System.ArgumentException: “Assembly must be a runtime Assembly object.”. For those who were not aware, Assembly is an abstract class which has multiple implementations where not all implementations are equal ;)1

Workaround

While one could obtain the Assembly in another way, e.g. Assembly.LoadFile(path) or for the current Assembly Assembly.GetExecutingAssembly(), sometimes that is not an option.

Another solution is to convert the CustomType to a RuntimeType (which has a RuntimeAssembly) which is surprisingly easy.

1
new ResourceManager("Resources.String", Type.GetType(customType.AssemblyQualifiedName, false).Assembly); // This will always succeed

Solution to convert a CustomType to a RuntimeType

Both CustomAssembly and RuntimeAssembly are internal classed, hence no proper client-side logic can be implemented to handle them differently, fortunately the workaround above always works.


  1. This breaks the Abstraction principle in Object Oriented programming. Resulting in exceptions upon correct usage of the API as described in this post. ↩︎

Noticed an error in this post? Corrections are appreciated.

© Nelis Oostens