title: How to use a Roslyn scripting submission as an assembly in other Roslyn compilations
body: <p>I'd like to reuse a script as a dynamic assembly in another non-scripting Roslyn compilation, but I can't for the life of me figure out how to make that work.</p>
  
  <p>For example, say a I create a script the normal way and then emit the script as an assembly to a byte stream using something like:</p>
  
  <pre><code>var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
  var compilation = script.GetCompilation().WithOptions(compilationOptions);
  using (var ms = new MemoryStream())
  {
      EmitResult result = compilation.Emit(ms);
      ms.Seek(0, SeekOrigin.Begin);
      assembly = Assembly.Load(ms.ToArray());
  }
  </code></pre>
  
  <p>Now, let's say I want to feed that assembly into another non-scripting compilation as a reference. I can't just use the <code>assembly</code> since none of the <code>MetadataReference.CreateFrom...()</code> methods support passing an actual <code>Assembly</code> instance. As a dynamic assembly it doesn't have a location so I can't use <code>MetadataReference.CreateFromFile()</code>.</p>
  
  <p>In the past I've used <code>MetadataReference.CreateFromStream()</code> for this type of thing with success, but that doesn't seem to work when the assembly represents a script submission (I have no idea why). The compilation proceeds, but as soon as you attempt to use a type from the submission you get errors like:</p>
  
  <pre><code>System.InvalidCastException: [A]Foo cannot be cast to [B]Foo. Type A originates from 'R*19cecf20-a48e-4a31-9b65-4c0163eba857#1-0, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' in a byte array. Type B originates from 'R*19cecf20-a48e-4a31-9b65-4c0163eba857#1-0, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' in a byte array.
  </code></pre>
  
  <p>I'm guessing it has something to do with the submission assembly being in a different context when evaluating vs. loaded as a byte array. I'd love any insight or guidance into the best way to use objects and methods defined in a script submission in later non-script compilations.</p>
  
  <p><strong>Update 7/29</strong></p>
  
  <p>I was able to get a minimal repro that demonstrates the problem. It can be found at <a href="https://github.com/daveaglick/ScriptingAssemblyReuse" rel="noreferrer">https://github.com/daveaglick/ScriptingAssemblyReuse</a>.</p>
  
  <p>In producing the repro, it became clear that an important component of this problem is that the script passes the <code>Type</code> of one of it's classes out to the calling code, the calling code then uses that <code>Type</code> to instantiate an instance of the object, and then passes the instance into the compilation that references the script assembly. The mismatch happens when casting from the instance of the type created by the host application to the type from within the referencing compilation. As I re-read that it sounds very confusing, so hopefully the code below will make it clearer.</p>
  
  <p>Here's all the code to trigger this problem:</p>
  
  <pre><code>namespace ScriptingAssemblyReuse
  {
      public class Globals
      {
          public IFactory Factory { get; set; }    
      }
  
      public interface IFactory
      {
          object Get();
      }
  
      public class Factory&lt;T&gt; : IFactory where T : new()
      {
          public object Get() =&gt; new T();
      }
  
      public class Program
      {
          public static void Main(string[] args)
          {
              new Program().Run();
          }
  
          private Assembly _scriptAssembly;
  
          public void Run()
          {
              AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
  
              // Create the script
              Script&lt;object&gt; script = CSharpScript.Create(@"
                  public class Foo { }
                  Factory = new ScriptingAssemblyReuse.Factory&lt;Foo&gt;();
                  ", ScriptOptions.Default.WithReferences(MetadataReference.CreateFromFile(typeof(IFactory).Assembly.Location)), typeof(Globals));
  
              // Create a compilation and get the dynamic assembly
              CSharpCompilationOptions scriptCompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
              Compilation scriptCompilation = script.GetCompilation().WithOptions(scriptCompilationOptions);
              byte[] scriptAssemblyBytes;
              using (MemoryStream ms = new MemoryStream())
              {
                  EmitResult result = scriptCompilation.Emit(ms);
                  ms.Seek(0, SeekOrigin.Begin);
                  scriptAssemblyBytes = ms.ToArray();
              }
              _scriptAssembly = Assembly.Load(scriptAssemblyBytes);
  
              // Evaluate the script
              Globals globals = new Globals();
              script.RunAsync(globals).Wait();
  
              // Create the consuming compilation
              string assemblyName = Path.GetRandomFileName();
              CSharpParseOptions parseOptions = new CSharpParseOptions();
              SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
                  public class Bar
                  {
                      public void Baz(object obj)
                      {
                          Script.Foo foo = (Script.Foo)obj;  // This is the line that triggers the exception 
                      }
                  }", parseOptions, assemblyName);
              CSharpCompilationOptions compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
              string assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
              CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] {syntaxTree},
                  new[]
                  {
                      MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
                      MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
                      MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
                      MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll"))
                  }, compilationOptions);
              using (MemoryStream ms = new MemoryStream(scriptAssemblyBytes))
              {
                  compilation = compilation.AddReferences(MetadataReference.CreateFromStream(ms));
              }
  
              // Get the consuming assembly
              Assembly assembly;
              using (MemoryStream ms = new MemoryStream())
              {
                  EmitResult result = compilation.Emit(ms);
                  ms.Seek(0, SeekOrigin.Begin);
                  byte[] assemblyBytes = ms.ToArray();
                  assembly = Assembly.Load(assemblyBytes);
              }
  
              // Call the consuming assembly
              Type barType = assembly.GetExportedTypes().First(t =&gt; t.Name.StartsWith("Bar", StringComparison.Ordinal));
              MethodInfo bazMethod = barType.GetMethod("Baz");
              object bar = Activator.CreateInstance(barType);
              object obj = globals.Factory.Get();
              bazMethod.Invoke(bar, new []{ obj });  // The exception bubbles up and gets thrown here
          }
  
          private Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
          {
              if (_scriptAssembly != null &amp;&amp; args.Name == _scriptAssembly.FullName)
              {
                  // Return the dynamically compiled script assembly if given it's name
                  return _scriptAssembly;
              }
              return null;
          }
      }
  }
  </code></pre>