views:

3404

answers:

6

I have an app (let's just call it MyApp) that dynamically creates source code for a class and then compiles it. When it compiles the source code I also reference another DLL (that is the base class for this newly created class) that already exists in another folder. I do the following to compile and output the DLL:

//Create a C# code provider 
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

//Set the complier parameters
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = false;
cp.TreatWarningsAsErrors = false;
cp.WarningLevel = 3;
cp.OutputAssembly = "SomeOutputPathForDLL";

// Include referenced assemblies
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.ReferencedAssemblies.Add("System.Data.dll");
cp.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
cp.ReferencedAssemblies.Add("System.Xml.dll");
cp.ReferencedAssemblies.Add("System.Xml.Linq.dll");
cp.ReferencedAssemblies.Add("MyApp.exe");
cp.ReferencedAssemblies.Add("SomeFolder\SomeAdditionalReferencedDLL.dll");

// Set the compiler options
cp.CompilerOptions = "/target:library /optimize";
CompilerResults cr = provider.CompileAssemblyFromFile(cp, "PathToSourceCodeFile");

Later on in my app (or next time the app runs) I try to create an instance of the class. I know where both the DLL for the newly created class (let's call it Blah) and the base class is. I use the following code to try to create an instance of the new class:

Assembly assembly = Assembly.LoadFile("PathToNewClassDLL");
Blah newBlah = assembly.CreateInstance("MyApp.BlahNamespace.Blah") as Blah;

When I call Assembly.CreateInstance like I do above I get an error saying it cannot create the instance. When I check assembly.GetReferencedAssemblies() it has the standard references and the reference for my app (MyApp.exe) but it doesn't have the reference for the dependent base class that I used when compiling the class originally (SomeAdditionalReferencedDLL.dll).

I know that I have to add the base class reference somehow in order to create the instance but I am not sure how to do this. How do I create an instance of a class from an assembly when I have the assembly and all of it dependecies?

Thanks

A: 

Are you sure that when it was compiling, it referenced the instance of the DLL you think it is referencing? It might be resolving the path to somewhere other than where you think such that when instantiating your class, it can't find that DLL anymore. I recommend getting a Fusion log of both the compilation process and the type creation to see how the types are being resolved.

Jeff Yates
A: 

I think .Net is trying to find the "SomeAdditionalReferencedDLL.dll" in your bin or GAC. Have you tried to do an Assembly.Load for the "SomeAdditionalReferencedDLL.dll" before creating a new Blah?

Min
A: 

This sounds like the assembly you're referencing in the dynamically generated assembly isn't in one of the standard probing paths.

Where does it reside relative to everything else?

You should fire up the fusion log viewer to see where things are going awry.

Kev
+2  A: 

If you manually load an external DLL (Assembly) IT WILL NOT AUTOMATICALLY LOAD WHAT YOU REFERENCED.

So you will have to create a AssemblyLoader. A code that checks the Referenced assemblies for your assembly and load them yourself.

For complications regarding the references assemblies residing in odd folders on your computer and not together with your compiled DLL, check out the AppDomain.CurrentDomain.AssemblyResolve event. (You use it to fool .NET to accept your assembly being loaded even though its not in the GAC or with your compiled DLL)

After you have loaded your referenced DLL manually with code, the CreateInstance will work.

Wolf5
A: 

first of all i think you have circular dependency .. your last paragraph sums it up. You need to rethink your application and decide if you have responsibilities setup correctly.

Why circular dependency:

For the new dll to be generated it requires MyApp.exe;

MyApp.exe cannot be used without the new dll.

maybe post what your goal is and we can help structure your app correctly.

With proper responsibility delegated MyApp.exe should be making the new generated assembly do work not require MyApp.exe to use objects from the new dll.

Example you should only have Execute on the new generated assembly .....

    public static void RenderTemplate(String templatepath, System.IO.Stream outstream, XElement xml, Dictionary<String, object> otherdata)
    {
        var templateFile = System.IO.File.ReadAllText(templatepath);

        var interpreter = new Interpreter();
        interpreter.ReferencedAssemblies.Add("System.Core.dll"); // linq extentions
        interpreter.ReferencedAssemblies.Add("System.Xml.dll");
        interpreter.ReferencedAssemblies.Add("System.Xml.Linq.dll");

        interpreter.UsingReferences.Add("System.Linq");
        interpreter.UsingReferences.Add("System.Xml");
        interpreter.UsingReferences.Add("System.Xml.Linq");
        interpreter.UsingReferences.Add("System.Collections.Generic");
        interpreter.Execute(templateFile, outstream, xml, otherdata);
    }
A: 
//Constructor
static MyClass()
    {
        //Provoque l'événement quand .Net ne sait pas retrouver un Assembly référencé
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

    }
    /// <summary>
    /// Mémorise les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV
    /// </summary>
    static Dictionary<string, string> _AssembliesPath;
    /// <summary>
    /// .Net ne sait pas retrouver un Assembly référencé
    /// Cherche et charge d'après les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV        
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (_AssembliesPath != null && _AssembliesPath.ContainsKey(args.Name))
        {
            Assembly lAssembly = Assembly.LoadFile(_AssembliesPath[args.Name]);
            AddAssemblyReferencedAssemblies(lAssembly, System.IO.Path.GetDirectoryName(lAssembly.Location));
            return lAssembly;
        }
        Error = string.Format("L'assembly {0} n'a pu être chargé", args.Name);
        return null;
    }
    /// <summary>
    /// Mémorise les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV        
    /// </summary>
    /// <param name="pAssembly"></param>
    /// <param name="psRootPath"></param>
    static void AddAssemblyReferencedAssemblies(Assembly pAssembly, string psRootPath)
    {
        if (_AssembliesPath == null) _AssembliesPath = new Dictionary<string, string>();
        foreach (AssemblyName lRefedAss in pAssembly.GetReferencedAssemblies())
            if (!_AssembliesPath.ContainsKey(lRefedAss.FullName))
            {
                string lsRoot = psRootPath + "\\" + lRefedAss.Name + ".";
                string lsExt;
                if (System.IO.File.Exists(lsRoot + (lsExt = "dll")) || System.IO.File.Exists(lsRoot + (lsExt = "exe")))
                {
                    _AssembliesPath.Add(lRefedAss.FullName, lsRoot + lsExt);
                }
            }
    }

//Function call Assembly lAssembly = Assembly.LoadFile(lsExternalAssemblyPath); AddAssemblyReferencedAssemblies(lAssembly, System.IO.Path.GetDirectoryName(lsExternalAssemblyPath));

edid