views:

124

answers:

2

I'm trying to figure out if it's possible when you are dynamically generating assemblies, to reference a type in a previously dynamically generated assembly.

For example:

using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;

CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();

parameters.GenerateInMemory = true;

CompilerResults results = provider.CompileAssemblyFromSource(parameters, @"
namespace Dynamic
{
    public class A
    {
    }
}
");

Assembly assem = results.CompiledAssembly;

CodeDomProvider provider2 = new CSharpCodeProvider();
CompilerParameters parameters2 = new CompilerParameters();

parameters2.ReferencedAssemblies.Add(assem.FullName);
parameters2.GenerateInMemory = true;

CompilerResults results2 = provider2.CompileAssemblyFromSource(parameters2, @"
namespace Dynamic
{
    public class B : A
    {
    }
}
");

if (results2.Errors.HasErrors)
{
    foreach (CompilerError error in results2.Errors)
    {
        Console.WriteLine(error.ErrorText);
    }
}
else
{
    Assembly assem2 = results2.CompiledAssembly;
}

This code prints the following on the console: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?)

I've tried it lots of different ways, but nothing seems to be working. Am I missing something? Is this even possible?

EDIT: Fixing the bug in the code provides this error instead: Metadata file 'l0livsmn, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' could not be found

EDIT2: Bit of a side note, but changing GenerateInMemory to false, and doing parameters2.ReferencedAssemblies.Add(assem.Location); will cause it to compile correctly, but I'd greatly prefer to reference the assembly that is directly in memory rather than outputting temporary files.

+2  A: 

I think that in

CompilerResults results2 = provider2.CompileAssemblyFromSource(parameters, @"
namespace Dynamic
{
    public class B : A
    {
    }
}
");

You want to pass parameters2, not parameters.

I found the way to do it, you need NOT to compile the first one in memory, if you don't do that, it will create a dll for this assembly in your temp directory, plus, in your call to

ReferencedAssemblies.Add() 

you dont pass the assembly name, you pass the assembly path, take a look at this code, it should work flawlessly :

        CodeDomProvider provider = new CSharpCodeProvider();
        CompilerParameters parameters = new CompilerParameters();            

        CompilerResults results = provider.CompileAssemblyFromSource(parameters, @"
            namespace Dynamic
            {
                public class A
                {
                }
            }
            ");

        Assembly assem = results.CompiledAssembly;

        CodeDomProvider provider2 = new CSharpCodeProvider();
        CompilerParameters parameters2 = new CompilerParameters();

        parameters2.ReferencedAssemblies.Add(assem.Location);
        parameters2.GenerateInMemory = true;

        CompilerResults results2 = provider2.CompileAssemblyFromSource(parameters2, @"
            namespace Dynamic
            {
                public class B : A
                {
                }
            }
            ");

        if (results2.Errors.HasErrors)
        {
            foreach (CompilerError error in results2.Errors)
            {
                Console.WriteLine(error.ErrorText);
            }
        }
        else
        {
            Assembly assem2 = results2.CompiledAssembly;
        }
Francisco Soto
Ah, shoot. Thanks! I fixed it. See the edit.
Ashley
See my second edit. I think we figured that out at about the same time. I would still like to be able to reference the in-memory assembly, if possible.
Ashley
+1  A: 

MSDN says you can:

Restrictions on Type References

Assemblies can reference types defined in another assembly. A transient dynamic assembly can safely reference types defined in another transient dynamic assembly, a persistable dynamic assembly, or a static assembly. However, the common language runtime does not allow a persistable dynamic module to reference a type defined in a transient dynamic module. This is because when the persisted dynamic module is loaded after being saved to disk, the runtime cannot resolve the references to types defined in the transient dynamic module.

Paul Williams
Well, I suppose the question now is, "How do I reference the transient dynamic assembly in memory?"
Ashley
I wonder: the assembly is created in memory, but is it added to the ApplicationDomain by default? Or would you load it yourself? Or call `AppDomain.DefineDynamicAssembly`?
Paul Williams
Hmm, that's a good question. I tried playing with it a bit, and the dynamically generated assembly definitely seems to be loaded.
Ashley