I have the following scenario, with exception being thrown when I try to cast:
- I added a project reference and imported the project's namespace.
- The LoadFile line loads the dll that is generated when this project is built.
- I am trying to access the public field of an attribute that decorates a property of an object from the dll.
- Here is the exception text:
[A]MyNamespace.PropertyMetaDataAttribute cannot be cast to [B]MyNamespace.PropertyMetaDataAttribute. Type A originates from 'A, Version=12.0.0.25, Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at location 'C:\projectA\bin\debug\A.dll'. Type B originates from 'A, Version=12.0.0.25, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\currentProject\bin\debug\A.dll'.
code snippet:
using MyNamespace; // added project reference to this item
m_Assembly = Assembly.LoadFile(ConfigurationManager.AppSettings["DLL_File_Path"]);
Type objectType = m_Assembly.GetType(string.Format("{0}.{1}", NAMESPACE_PREFIX, "MyObject"));
// Crash happens on line below:
Attribute attr = (Attribute) objectType.GetProperty("Name").GetCustomAttributes(false)[0];
//This is the layout of the object which has the property
MyObject
{
[MyAttribute(Name="FooName")]
Foo {get;set;}
}
// This is the definition of the attribute
MyAttribute :Attribute
{
// Want to access the value
public string Name = string.Empty;
}
It seems like your
ConfigurationManager.AppSettings["DLL_File_Path"]
Is pointing to the same dll in other location, so the .Net runtime tries to load a dll already loaded, when you try to use it, the Type System blows up because of the types being repeated between the 2 dll's...
According to the error, these are the locations of the two dll's
- C:\currentProject\bin\debug\A.dll
- C:\projectA\bin\debug\A.dll
If the reference is explicitly set on the solution, you can try to set copy local to False so when the solution generates, don't be copying the result of the dependencies
If your dll is already in the automatically searched path (current-directory, bin etc), and loaded because you referenced something in that assembly, your LoadFile (you might want to use LoadFrom instead aswell) will load the same DLL into another context (LoadFrom-context) instead of default.
You should either consider to load the DLL only once, or try to fetch your assembly from your default context first in order to avoid the conflict, by looking in AppDomain.CurrentDomain.GetAssemblies().
You can even attach an AssemblyResolve-eventhandler to AppDomain.CurrentDomain as a fallback if you're trying to access an assembly that isn't in the Default context.
http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx
AppDomain.CurrentDomain.GetAssemblies() will contain assemblies from all contexts, even the ones that you've loaded using LoadFrom/LoadFile.
When you load an assembly there are different contexts that you can load the types into. This is why you can't cast from one type to the other. You could work around this using the the AssemblyResolve event (as jishi suggests) or there is also a TypeResolve event that you can use to return the correct type when you use it, but I think that you just really need to align the assembly to the right context and understand when and how it is loaded and what is right for you.
These articles from Suzanne Cook's blog really helped me understand the different contexts and how things are loaded into them and why.
http://blogs.msdn.com/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx
Also, if you want to look at how and why they are loaded you should look at the Fusion logs with them set to dump all the information out and it will show you the load information for the assembly.