I have an Assembly Library1.dll which contains some Interfaces, which were serialized as a byte array into the database. For some reasons we have to change the Interface properties and defintion. so now i am writing a migration utility. So i have 2 versions of Library1.dll , In my utility i have created a folder where i store the new version of Library1.dll . This utility in turn also references Library1.dll hence in bin folder contains Library1.dll but this dll is compiled on older version. My new version of Library1.dll is stored in a private path which i am passing to Assembly.ReflectionOnlyLoadFrom function to instantiate and hence GetTypes on the assembly loaded which further would enable me to do conversion of data.
But I always get ReflectionTypeLoadException when trying to load Library1.dll from private path.
Please help guys!!!. any help would be appreciated. I am really stuck.
Thanks,
AG
If your Library is referencing another dll, GetTypes will fail when it hits a type that uses an external type. Unlike normal assembly loading, ReflectionOnly Assembly loading will not resolve dependencies. You can either subscribe to AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve and load the dependencies as required, or you could pre-load them.
This is the code I use for this:
var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
foreach (var assemblyName in assembly.GetReferencedAssemblies()) {
try {
Assembly.ReflectionOnlyLoad(assemblyName.FullName);
} catch {
Assembly.ReflectionOnlyLoadFrom(Path.Combine(Path.GetDirectoryName(assemblyPath), assemblyName.Name + ".dll"));
}
}
This will try to load all dependencies of the reflection-only loaded assembly first by fullname, then by path (assuming that the dependency is in the same directory as the loaded assembly).
Related
I have a problem with MEF and using a plugins folder.
I have a main app that supports plugins via MEF. The main app does not reference the assemblies containing the .NET Task type for multithreading but one or more of the plugins do.
The plugins are located in a Plugins folder and I'm using a DirectoryCatalog.
I keep getting ReflectionTypeLoadException being thrown by MEF on
Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information.
The LoaderExceptions property contains a FileNotFoundException
"Could not load file or assembly 'System.Threading.Tasks,
Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or
one of its dependencies. The system cannot find the file
specified.":"System.Threading.Tasks, Version=1.5.11.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
The plugin is referencing System.Threading.Tasks via a Microsoft NuGet package reference.
This is my helper method:
public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
{
// setup composition container
var catalog = new AggregateCatalog();
// check if folders were specified
if (searchFolders != null)
{
// add search folders
foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
{
catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
}
}
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
// compose and create plug ins
var composer = new CompositionContainer(catalog);
composer.ComposeParts(parts);
}
public class MEFComposer
{
[ImportMany(typeof(IRepository))]
public List<IRepository> Repositories;
[ImportMany(typeof(ILogging))]
public List<ILogging> LoggingRepositories;
[ImportMany(typeof(IPlugin))]
public List<IPlugin> Plugins;
}
This is the code I'm using to invoke MEF and load the plugins.
public void Compose()
{
// try to connect with MEF types
try
{
var parts = new MEFComposer();
MEFHelpers.Compose(new[] { Path.Combine(Application.StartupPath, "Plugins") }, parts);
RepositoryFactory.Instance.Repository = parts.Repositories.FirstOrDefault();
Logging.Repositories.AddRange(parts.LoggingRepositories);
foreach (var plugin in parts.Plugins)
{
this.applicationApi.Plugins.Add(plugin);
plugin.Connect(this.applicationApi);
}
}
catch
{
// ERR: handle error
}
}
Why is MEF not able to load the plugins even though the Microsoft.Threading.Tasks.dll and related assembly files are present in the Plugins folder, but not the main application bin folder? And is there any way of telling MEF to search the Plugins folder for assembly dependencies?
Having a plugin model means I can't anticipate what assemblies a plugin may be referencing so I cannot include them in the main bin folder for the app, which is why I want all related plugins and plugins dependencies to be in the plugins folder.
You have run into an essential problem when supporting 3rd party plugins. Your problem is that when you are loading the plugin, the runtime will search for its references when needed only in the specified folders your AppDomain knowns of. That would be the WorkingDirectory of that process, then path etc.
Essentially, you are loading an Plugin that requires System.Threading.Tasks. That DLL lies within your /Plugin folder. As .net loads your plugin, it will search for that assembly but has no way in finding it as it is located within the /Plugin folder and fails.
There are a couple of solutions to that.
Do not use a Plugin folder
That would be the most simple solution, when all assemblies (including references of the 3rd party lib) are located in your WorkingDirectory, .net will have not trouble finding all references of that plugin.
Add the Plugin folder to your Probing path
This will extend the paths .net will search the 3rd party references:
https://learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies#locating-the-assembly-through-probing
Use AppDomains for each plugin.
An AppDomain is my choice of go here as it allows you to load the assembly not only in its "own" container, but also can simulate the Working directory only for the plugin. That can come handy if on of the Plugins uses the same framework as your application but in a different version for example.
Load the dependencies by yourself
That would be the "straight forward" method of solving this. You can load every assembly as ReflectionOnly, Determinate all dependencies and then load these. That will almost Garantie to work.
4.1. AssemblyResolve event
This is just another way how to "redirect" .net to load assemblies from the PluginFolder
https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.8
Edit:
There is also a certain problem with the AssemblyCatalog, it uses Assembly.Load instead of Assembly.LoadFrom to load the given assembly. That is an essential part of your problem, as LoadFrom would probe the path where the assembly originates from for its dependencies where Load does not.
https://github.com/JPVenson/MSEF/blob/master/JPB.Shell/JPB.Shell.MEF/Model/StrongNameCatalog.cs
You could use an Catalog like this one that is using LoadFrom instead.
Disclaimer: I am the creator of that project.
I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .
I have a method that performs a search in both the local assembly and in the current directory. It is looking for a class based on the name provided (reflection). However I now want to only load the classes/dlls that I am looking for into memory as opposed to loading them all and selecting the one that I want from it.
I have been told that marshalbyrefobject could be used to do this. [Below is the code I am currently using]
The solution would be to create 2 app domains and have one load all the assembiles and do the checks then unload on of the app domains, though im not sure how to go about doing that.
To my limited knowledge, you can avoid loading the assemblies by querying them beforehand using the Assembly class.
A full working model would look like this:
1 - Collect information through the Assembly class
Assembly File:
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(fileName);
Local assembly:
Assembly myAssembly = Assembly.GetExecutingAssembly();
2 - Iterate through the classes present on each Assembly reference
Assembly mscorlib = typeof(string).Assembly;
foreach (Type type in mscorlib.GetTypes())
{
Console.WriteLine(type.FullName);
}
3 - After choosing which assembly to use, Load it into your Application Domain
Assembly assembly = Assembly.Load(fullAssemblyName);
or
Assembly assembly = Assembly.LoadFrom(fileName);
Examples copied from the following post, so feel free to upvote the responses there if you feel they contributed to solve your issue!
C#: List All Classes in Assembly
I am trying to develop a plugin architecture in .Net. The application will be a .Net application. There will be directories which holds the plug-ins. Each directory will represent a plugin. Each plugin directory will also contain all the dependency dlls as well. The plugins need to be stored in separate AppDomain as the plugins may use the same assemblies, but different versions.
As it iterates through the foreach loop in Init(), I get a
System.IO.FileNotFoundException : Could not load file or assembly '[Assembly Name], Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
in _apDomain.Load() for assemblies that are not in the main project.
Code:
readonly private AppDomain _appDomain;
internal ItemModuleAppDomain()
{
AppDomainSetup info = AppDomain.CurrentDomain.SetupInformation;
_appDomain = AppDomain.CreateDomain("ChildDomain");
}
public void Init(string dllDir)
{
string[] dlls = Directory.GetFiles(dllDir, "*.dll", SearchOption.TopDirectoryOnly);
foreach(string dll in dlls)
{
_appDomain.Load(AssemblyName.GetAssemblyName(dll));
}
Any idea why? I tried several methods such as reading the assembly as an array of bytes to load.
I believe AppDomain.Load may be a wrong method for your purpose - it loads the assembly into both current app domain as well as target domain. And you get an error because your assembly is located in a sub folder and .NET does not know about it. You have to give private paths in configuration (http://msdn.microsoft.com/en-us/library/15hyw9x3.aspx). You can also use AssemblyResolve or TypeResolve event of AppDomain for resolution.
You should also look at AppDomain.CreateInstanceFrom method to load your main plugin type (and containing assembly).
How do I obtain an instance of a Type class for a type not defined in the currently executing assembly or in mscorlib.dll?
a) Namely, I've defined a class type someType in assembly CSharpSnapIn.dll, located at E:\CSharpSnapIn.dll, but for some reason when I try to specify an absolute path to this assembly, I get an exception:
Type t = Type.GetType("someType, E:\\CSharpSnapIn.dll"); // exeception
b) I've also tried by putting CSharpSnapIn.dll into \bin\debug directory of a currently running application, but I still get an exception:
Type t = Type.GetType("someType, CSharpSnapIn.dll"); // exeception
thanx
EDIT:
1) I've declared another class type someType2 ( inside CsharpSnapIn.dll )and this time it worked:
Type.GetType("someType2, CSharpSnapIn");
Difference between someType and someType2 is that someType implements an interface declared in external assembly asmIn, but this shouldn't cause an exception, since CsharpSnapIn.dll does have a reference to asmIn?!
2)
Note that the assembly doesn't need to
be loaded first, so long as the
assembly resolver can find it
In other words, calling Type.GetType() first loads an assembly and then creates a Type instance?
3)
The assembly has to be found by
probing, so it would have to be in the
bin directory as per your second
example. If it's an assembly with a
strong name, you have to give all the
details.
So you're saying we can't specify an absolute path ( to an assembly ) using Type.GetType(), but instead assembly needs to reside inside a bin directory?
You will need to first load the assembly:
Type t = Assembly
.LoadFrom(#"e:\CSharpSnapIn.dll")
.GetType("SomeNs.SomeType", true);
You need to give the assembly name - not the file which contains it.
For example:
Type t = Type.GetType("someType, CSharpSnapIn");
The assembly has to be found by probing, so it would have to be in the bin directory as per your second example. If it's an assembly with a strong name, you have to give all the details. Note that someType here also has to be fully qualified in terms of the namespace.
Note that the assembly doesn't need to be loaded first, so long as the assembly resolver can find it. For example, if the assembly is in the same directory as the currently executing assembly, that will be fine in most cases.
As Darin says, an alternative is to load the assembly directly - although in my experience there are quite a few "gotchas" in loading assemblies explicitly, particularly if you've got two assemblies in different locations which both rely on a third assembly. Making sure you only get that third assembly loaded once can be tricky.
You need to choose between LoadFile and LoadFrom etc. Here are some remarks from MSDN:
Use the LoadFile method to load and
examine assemblies that have the same
identity, but are located in different
paths. LoadFile does not load files
into the LoadFrom context, and does
not resolve dependencies using the
load path, as the LoadFrom method
does. LoadFile is useful in this
limited scenario because LoadFrom
cannot be used to load assemblies that
have the same identities but different
paths; it will load only the first
such assembly.