I'm having a very hard time trying to access a custom configuration section in my config file.
The config file is being read from a .dll that is loaded as a plug-in. I created the Configuration and necessary code using the Configuration Section Designer VS addin.
The namespace is 'ImportConfiguration'. The ConfigurationSection class is 'ImportWorkflows'. The assembly is ImportEPDMAddin.
The xml:
<configSections>
<section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/>
</configSections>
Whenever I try to read in the config, I get the error:
An error occurred creating the configuration section handler for importWorkflows: Could not load file or assembly 'ImportEPDMAddin.dll' or one of its dependencies. The system cannot find the file specified.
The dll will not reside in the same directory as the executable as the software that loads the plugin places the dll and it's dependencies in it's own directory. (I can't control that.)
I edited the code for the singleton instance to the following:
string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase;
path = path.Replace("file:///", "");
System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(path);
return configuration.GetSection(ImportWorkflowsSectionName) as ImportConfiguration.ImportWorkflows;
I have also tried using a simple NameValueFileSectionHandler as well, but I get an exception saying that it can't load file or assembly 'System'.
I have read numerous blog posts and articles and it sounds like it is possible to read a config file in for a dll, but I just can't get it to work. Any ideas? Thanks.
Unfortunately, you will need to either have the ImportEPDMAddin assembly residing in the same folder as your executable, residing in the .Net framework folder related to the .Net framework you are using (i.e., C:\Windows\Microsoft.NET\Framework\v2.0.50727), or registered in the Global Assembly Cache.
The only other option is, if you know the path to the assembly that contains the configuration handler's defining class, you can load it without a reference with something like this:
//Class global
private Assembly configurationDefiningAssembly;
protected TConfig GetCustomConfig<TConfig>(string configDefiningAssemblyPath,
string configFilePath, string sectionName) where TConfig : ConfigurationSection
{
AppDomain.CurrentDomain.AssemblyResolve += new
ResolveEventHandler(ConfigResolveEventHandler);
configurationDefiningAssembly = Assembly.LoadFrom(configDefiningAssemblyPath);
var exeFileMap = new ExeConfigurationFileMap();
exeFileMap.ExeConfigFilename = configFilePath;
var customConfig = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap,
ConfigurationUserLevel.None);
var returnConfig = customConfig.GetSection(sectionName) as TConfig;
AppDomain.CurrentDomain.AssemblyResolve -= ConfigResolveEventHandler;
return returnConfig;
}
protected Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
{
return configurationDefiningAssembly;
}
Make sure you handle the AssemblyResolve event, as this will throw an exception without it.
In your main applications config file, add the following (where plugins is the folder for your assembly to load from. You can use multiple paths semi-colon separated.
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath=".;.\Plugins"/>
</assemblyBinding>
</runtime>
Taken from http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx
To expand on AJ's excellent answer, here is a custom class to assist with the overhead of registering and removing the global event.
public sealed class AddinCustomConfigResolveHelper : IDisposable
{
public AddinCustomConfigResolveHelper(
Assembly addinAssemblyContainingConfigSectionDefinition)
{
Contract.Assert(addinAssemblyContainingConfigSectionDefinition != null);
this.AddinAssemblyContainingConfigSectionDefinition =
addinAssemblyContainingConfigSectionDefinition;
AppDomain.CurrentDomain.AssemblyResolve +=
this.ConfigResolveEventHandler;
}
~AddinCustomConfigResolveHelper()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposing)
{
AppDomain.CurrentDomain.AssemblyResolve -= this.ConfigResolveEventHandler;
}
private Assembly AddinAssemblyContainingConfigSectionDefinition { get; set; }
private Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
{
// often the name provided is partial...this will match full or partial naming
if (this.AddinAssemblyContainingConfigSectionDefinition.FullName.Contains(args.Name))
{
return this.AddinAssemblyContainingConfigSectionDefinition;
}
return null;
}
}
I would suggest creating an instance in a using statement, like so:
// you'll need to populate these two variables
var configuration = GetConfiguration();
var assembly = GetAssemblyContainingConfig();
using(new AddinCustomConfigResolveHelper(assembly))
{
return (MyConfigSection)configuration.GetSection("myConfigSection");
}
Have you made sure that DLL is loaded first? Perhaps with Assembly.LoadFile("PATH")?
If you can't get the classes in System.Configuration to work properly, you can always fall back on using XmlDocument to manually parse the configuration file. Use XPaths to make getting the data easier. For example (assuming your path variable above):
var document = new XmlDocument();
document.Load(path);
var node = document.SelectSingleNode("configuration/importWorkflows/add[#name='KEY']");
// Do whatever with node
Could you verify that the probing paths are setup correctly in your Host application's config file? It is possible that a needed reference is not being loaded in your current application domain.
Assembly Binding ->Probing
Had to use the fully qualified type string of my module/plugin assembly, which is in a probing directory, so it could be located. Using EntityFramework as an example...
Incorrect:
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"
Correct
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
I tried AJ's answer, with rileywhite's supplement but I found that did not work for me.
In my scenario, the custom ConfigurationSection class was already in the currently-executing assembly, and trying to load it causes a stack overflow. I also did not want to put it in GAC even though it did resolve the problem as reported by the OP.
In end, I found this to work well enough for my purpose. Maybe others will find it useful:
public class CustomConfigurationSection : ConfigurationSection {
public CustomConfigurationSection()
{
var reader = XmlReader.Create(<path to my dll.config>);
reader.ReadToDescendant("CustomConfigurationSection");
base.DeserializeElement(reader,false);
}
// <rest of code>
}
Related
== compile command ==
csc -r:"../Newtonsoft.Json.dll" test.cs
== exec command ==
mono test.exe
== exec result : dependency error ==
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
"Newtonsoft.Json.dll" this file is located in parent path. so I added a reference about dll and compile succeeded, but when I executed the exe file, it failed to get dll reference I added.
And when I put cs file and dll file together in the same directory, it worked very well, but that's not what I wanted.
Is there a solution to add a reference from dll file which is located in parent path using command line interface?
I used csc for compiler and mono for execution.
Thanks.
References are pathless. What that means is that wherever the assembly resides, all your program knows is that it has a reference to Newtonsoft.Json, Version=x.x.x.x, Culture=... and so on. You can do some things with the application configuration (application.config or myprogram.exe.config) to organize things into subfolders (using the probing setting) or specify a URL location for the file (using the codebase setting). You can set up the environment to change the search path and so on.
Or you can add some runtime code that allows you to override the default behavior and the call Assembly.LoadFrom to provide a full path to the file. You can either do it as part of your initialization or in a handler for the AppDomain.AssemblyResolve event - which is generally better method since it will only be called when the assembly is actually needed.
For example:
using System.IO;
using System.Reflection;
static class ParentPathResolver
{
public static void Init()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolve);
}
private static Assembly? Resolve(object? sender, ResolveEventArgs args)
{
var filename = new AssemblyName(args.Name).Name + ".dll";
var fullname = Path.GetFullPath(Path.Combine("..", filename));
if (File.Exists(fullname))
return Assembly.LoadFrom(fullname);
return null;
}
}
Of course you can add your own code to the Resolve method to search pretty much anywhere, just as long as you don't get caught in a resolution loop. I've used the AssemblyResolve event to do fun things like loading assemblies from compressed resources.
I am building a simple MEF application. What I want to achieve is building one plugin, which can be registered multiple times in the same composing application. The registration of the plugin should be dependent on a setting from the configfile of the plugin, but I am not able to do this.
[edit]
My server, which has the CompositionContainer, needs to communicate with 6 different targets (ie Traffic Light Controllers). For every target, I want to add a plugin. The plugin logic is the same, so I want to maintain only 1 plugin. Every target has its own webaddress to communicate (and some other configuration items), I want those to be in (separate) configfiles.
What I tried is putting the plugins in subdirectories and going recursively through those directories to add the plugins in the catalog. This doesn't work however. The second plugin found in the subdirs will be imported, but this one is targeting the first plugin. When looping through the container FASTAdapters, all parts seem to equal to the first.
private void Compose()
{
var catalog = new AggregateCatalog();
string sDir = AppSettingsUtil.GetString("FASTAdaptersLocation", #"./Plugins");
foreach (string d in Directory.GetDirectories(sDir))
{
catalog.Catalogs.Add(new DirectoryCatalog(d));
}
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
I don't know if I can also use the ExportMetadata attribute. It seems that ExportMetadata attributes have to be hardcoded, but I want the attribute being read from the config file if possible.
[/edit]
My goal is to have 6 ControllerAdapters, each targeting a different controller (read: communicating with a different webserver). The logic in the 6 ControllerAdapters is equal.
I thought copying the ClassLibrary (for example to 1.dll, 2.dll and so on) and adding the configfiles (1.dll.config and so on) should do the trick, but no.
When composing, I get multiple instances typeof(FAST.DevIS.ControllerAdapter) in the container, but I don't know how to get further.
Do I need to do something with MetaData in the export?
The importing server
[ImportMany]
public IEnumerable<IFASTAdapter> FASTAdapters { get; set; }
private void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(AppSettingsUtil.GetString("FASTAdaptersLocation", Path.GetDirectoryName(Assembly.GetAssembly(typeof(ControllerServer)).Location))));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
The plugin
namespace FAST.DevIS.ControllerAdapter
{
[Export (typeof(IFASTAdapter))]
public class ControllerAdapter : IFASTAdapter
{
...
}
}
The interface
namespace FAST.Common.FastAdapter
{
public interface IFASTAdapter
{
/// Parse plan parameters
///
//Activator
bool ParsePlan(PlansContainer plan);
bool ActivatePlan();
void Configure(string config);
}
}
This may be more a problem with how you are using assemblies than with the MEF solution.
You say:
The logic in the 6 ControllerAdapters is equal.
So is it the same DLL just copied 6 times to different plugin directories? If yes, then this is the problem.
I modeled your approach and ran the some tests to prove what I thought. The code is effectively the same as yours and reads plugins from the subdirectories of bin/plugin directory of the server.
Simple test using NUnit to exercise the server class library:
[Test]
public void Compose()
{
var server = new Server();
server.Compose();
Console.WriteLine("Plugins found: " + server.FASTAdapters.Count());
Console.WriteLine();
foreach (var adapter in server.FASTAdapters)
{
Console.WriteLine(adapter.GetType());
Console.WriteLine(adapter.GetType().Assembly.FullName);
Console.WriteLine(adapter.GetType().Assembly.CodeBase);
Console.WriteLine();
}
Assert.Pass();
}
Test results for one plugin in place:
Plugins found: 1
AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL
Test result for two plugins in place, using the same plugin assembly copied over to two distinct plugin directories (probably your case):
Plugins found: 2
AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL
AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL
You also get the exact same result if you give distinct names to those DLLs because effectively it is still the same assembly inside.
Now I add the third plugin, but this time it is a different plugin assembly:
Plugins found: 3
AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL
AdapterPlugin.ControllerAdapter
AdapterPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER1/ADAPTERPLUGIN.DLL
AdapterPlugin2.ControllerAdapter
AdapterPlugin2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
file:///C:/USERS/GARKIN/DOCUMENTS/VISUAL STUDIO 2012/PROJECTS/MEFADAPTERS/ADAPTERSERVER/BIN/DEBUG/PLUGINS/ADAPTER3/ADAPTERPLUGIN2.DLL
The different assembly is of course found and identified correctly.
So this all boils down to how the .NET runtime handles assembly loading, which is a complex and strictly defined process and works differently for strongly and weakly named assemblies. I recommend this article for a good explanation of the process: Assembly Load Contexts Subtleties.
In this case, the same process is followed behind the scenes when using MEF:
The .NET runtime finds the first weakly typed plugin assembly and loads it from that location and MEF does it's export processing.
Then MEF tries to process the next plugin assembly it founds using the catalog, but the runtime sees the assembly with the same metadata already loaded. So it uses the already loaded one to look for exports and ends up instantiating the same type again. It does not touch the second DLL at all.
There is no way the same assembly can be loaded more than once by the runtime. Which makes perfect sense when you think of it. Assembly is just bunch of types with their metadata and once loaded the types are available, no need to load them again.
This may not be completely correct, but I hope it helps to explain where the problem lies and it should be clear that duplicating DLLs for this purpose is useless.
Now regarding what you want to achieve. It seems that all you need is just to get multiple instances of the SAME adapter plugin to use them for different purposes, which has nothing to do with multiplying DLLs.
To get multiple adapter instances you can define multiple imports with RequiredCreationPolicy set to CreationPolicy.NonShared in your server which MEF will accordingly instantiate for you:
public class Server
{
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
public IFASTAdapter FirstAdapter { get; set; }
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
public IFASTAdapter SecondAdapter { get; set; }
// Other adapters ...
public void Compose()
{
var catalog = new AggregateCatalog();
var pluginsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
foreach (string d in Directory.GetDirectories(pluginsDir))
{
catalog.Catalogs.Add(new DirectoryCatalog(d));
}
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
Corresponding NUnit test to check that the adapters are instantiated and that they are different instances:
[Test]
public void Compose_MultipleAdapters_NonShared()
{
var server = new Server();
server.Compose();
Assert.That(server.FirstAdapter, Is.Not.Null);
Assert.That(server.SecondAdapter, Is.Not.Null);
Assert.That(server.FirstAdapter, Is.Not.SameAs(server.SecondAdapter));
}
If all of this will help you to some extent, we can also take look at how you want to configure what and how to instantiate using the app.config.
I'm trying to put together a small helper tool to encrypt web.config files.
Linqpad example:
var path = #"F:\project\";
var wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/",new VirtualDirectoryMapping(path,true));
var config = Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm,"/");
var c = config.Sections["customGroup"];
c.SectionInformation.ProtectSection(null);
config.Save();
This throws the exception:
Could not load file or assembly 'customGroupAssembly' or one of its dependencies.
The system cannot find the file specified
Adding the assembly to LinqPad fixes the errpr. I would assume this is because it's now a compile time reference.
Moving the code to a winforms application, reintroduces the issue.
I'm trying to load the needed assembly at runtime with this:
if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
byte[] assemblyBuffer = File.ReadAllBytes(this.openFileDialog1.FileName);
AppDomain.CurrentDomain.Load(assemblyBuffer);
MessageBox.Show("Assembly loaded!");
}
However it still doesn't seem to find the file.
Is there a way to load the custom assembly into the runtime app domain so that the web config can be loaded properly?
Try attaching assembly resolve listener, I've had such a problem where runtime-loaded assembly was not resolved properly and needed to be manually resolved:
AppDomain.CurrentDomain.AssemblyResolve += (s,arg)=>{ ... }
with your code at the (...) returning the assembly you want resolved based the name you got in arg
I was developing an application. I want unity to resolve my types WITHOUT having to reference the assemblies in the main project. I tought it loaded assemblies automatically by configuring the type registration by using but doesn't seem to work unless I add a reference to the assembly containing dependencies.
Is there a wat to load types from the assemblies in the current directory?
Thanks!
Sounds like you want MEF and not Unity. MEF is designed for dynamic discovery.
Read the answer to this question: What is different between and purpose of MEF and Unity?
I know this was asked a while ago, but for anyone looking for an answer, you have to make sure that Unity can locate the assemblies at run-time. So you either need to put them in the GAC or drop your assembly dll in the same directory as your executable. If you are using a different directory for dependencies and have a web app, then you have to set the probing element in your web.config:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dependencies" />
</assemblyBinding>
</runtime>
And for anyone out there looking for a piece of code on how to solve this, the following code will look for all assemblies on your defined directory and register them with Unity:
string DependenciesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Dependencies");
string[] Dependencies = Directory.GetFiles(DependenciesPath, DLL_PATTERN);
Dictionary<Type, Type> pluginMappings = new Dictionary<Type, Type>();
//Load Dependency Assemblies
foreach (string fileName in Dependencies)
{
string assemblyName = Path.GetFileNameWithoutExtension(fileName);
if (assemblyName != null)
{
Assembly pluginAssembly = Assembly.Load(assemblyName);
foreach (Type pluginType in pluginAssembly.GetTypes())
{
if (pluginType.IsPublic) //Only look at public types
{
if (!pluginType.IsAbstract) //Only look at non-abstract types
{
//Gets a type object of the interface we need the plugins to match
Type[] typeInterfaces = pluginType.GetInterfaces();
foreach (Type typeInterface in typeInterfaces)
{
if (pluginMappings.ContainsKey(typeInterface))
{
throw new DuplicateTypeMappingException(typeInterface.Name, typeInterface, pluginMappings[typeInterface], pluginType);
}
pluginMappings.Add(typeInterface, pluginType);
}
}
}
}
}
}
//Web API resolve dependencies with Unity
IUnityContainer container = new UnityContainer();
foreach (var mapping in pluginMappings)
{
container.RegisterType(mapping.Key, mapping.Value);
}
It might be a bit late to help, but Unity can dynamically load assemblies if you use the XML configuration approach, register each assembly, and then register the types accordingly. I've been using this process for minor extensions to an otherwise very DI-heavy framework for awhile now.
If you're encountering an issue where Unity fails to resolve a type that's registered in the main application but defined in another, unreferenced assembly and referencing said assembly resolves the issue, that most likely means that it just wasn't copied to the application's output directory. By default, only assemblies that are directly referenced are automatically copied. If they're copied manually or by a post-build event, or if you redirect your build paths so that the unreferenced assemblies build to the application's output directory, it should work.
Interfaces (In the assembly named "Interfaces". In project :- Interfaces)
namespace Interfaces
{
public interface IDoSomeWork1
{
string DoSomeWork1();
}
}
namespace Interfaces
{
public interface IDoSomeWork2
{
string DoSomeWork2();
}
}
Dependencies (In the assembly named "Entities". In project :- Entities)
namespace Entities
{
public class ClassB : IDoSomeWork1
{
public string DoSomeWork1()
{
return this.ToString();
}
}
}
namespace Entities
{
public class ClassC : IDoSomeWork2
{
public string DoSomeWork2()
{
return this.ToString();
}
}
}
Class (In project :- UsingUnity)
public class ClassA
{
[Dependency]
public IDoSomeWork1 DoSomeWork1 { get; set; }
[Dependency]
public IDoSomeWork2 DoSomeWork2 { get; set; }
public void SomeMethodInClassA()
{
Console.WriteLine(DoSomeWork1.DoSomeWork1());
Console.WriteLine(DoSomeWork2.DoSomeWork2());
}
}
App.Config (In a console application project :- ConsoleUsingUnity)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity>
<containers>
<container>
<types>
<type type="Interfaces.IDoSomeWork1, Interfaces"
mapTo="Entities.ClassB, Entities" />
<type type="Interfaces.IDoSomeWork2, Interfaces"
mapTo="Entities.ClassC, Entities" />
</types>
</container>
</containers>
</unity>
</configuration>
The client (In a console application project :- ConsoleUsingUnity)
public class Class1
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
// Load from config file
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(container);
ClassA classA = container.Resolve<ClassA>();
classA.SomeMethodInClassA();
}
}
And when I run the client, I get the following error at section.Configure(container);:-
The given assembly name or codebase
was invalid. (Exception from HRESULT:
0x80131047)
I am not sure If there is anything wrong with the config or the type. Could anyone please point out the mistake here?
In case anyone else ever has the same problem - I was also getting this error but had a slightly different problem. I was trying to load an assembly that clearly existed like the following:
Assembly.Load("C:\\Program Files\\MyProgram\\MyAssembly.dll");
After lots of trial and error I figured out that you aren't supposed to pass the path and you certainly aren't supposed to include .dll extension. The following fixed my issue:
Assembly.Load("MyAssembly");
Hopefully that helps someone else sooner or later!
Before I answer my question, I must state that the code posted above didn't give me any problem (build error etc.). It just gave me the error I stated in my question. The problem with Unity at this point of time is that It does not provide which assembly or a which types in the assembly could not be loaded. This is a requested feature.
In my case It was a missing assembly problem. I didn't reference Entities assembly in to the client application project. It seems that that "Entities" assembly could be resolved only at the run-time (since it didn't give me any compile time error). However, the run-time error was also not useful at all.
I had a look a Fusion Log viewer (It should be in the .NET SDK folder). What a gem of an utility It is. It can log all kind of assembly bindings (all or only failures) and It give a very neat description of which assembly could not load. Very helpful!
So, next time, you get this "The given assembly name or codebase was invalid" error, try Fusion Log Viewer. It wont help you in finding which types couldn't be loaded. However,at least you will be sure all your assemblies are getting loaded correctly.
If you connect up the domain AssemblyResolve event you can get the assembly that has failed to bind.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
There is a better way now!
Unity has a new version (currently 2.1.505.2) which clearly reports the details and lets you get to the bottom of it immediately.
You can find binaries and source here: http://unity.codeplex.com/releases
I found that the least time consuming method of finding which Type exactly failed to bind is the following:
Go to Sources section of Unity page at codeplex http://unity.codeplex.com/SourceControl/list/changesets
Select a version and download the sources
Build DEBUG version of Unity and Unity.Configuration (if your project uses more of unity assemblies, build them as well)
Remove references to unity from your project and add references to assemblies from step 3
In Visual Studio go to Debug > Exceptions and make sure Common Language Runtime Exceptions has a checkbox in the Thrown column.
Now go crash that thing. Execution will stop in Unitys' TypeResolverImpl.SearchAssemblies method and typeNameOrAlias parameter will hold the answer!
works like this
Dim dllData = System.IO.File.ReadAllBytes(dllFullPath)
Dim asb As System.Reflection.Assembly
asb = System.Reflection.Assembly.Load(dllData)
Dim cls As Object = asb.CreateInstance("namespace.class")
Please make sure, that you have added assembly references of the missing assembly in the project where your web.config file exists.
I was missing this. I already have added those assembly references in the project which was using Unity to resolve the class, but missed to add it in the parent project where configuration file was located. This has resolved my problem.