別のスクリプト以外のRoslynコンパイルでスクリプトを動的アセンブリとして再利用したいのですが、その仕事をするRoslynスクリプト提出を他のRoslynコンパイルのアセンブリとして使用する方法
例えば、私は通常の方法でスクリプトを作成し、その後のようなもの使用して、バイトストリームにアセンブリとしてスクリプトを発する言う:
今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());
}
を、のは、私はにそのアセンブリを供給したいとしましょう他の非スクリプティングコンパイルを参考にしてください。 MetadataReference.CreateFrom...()
メソッドのどれも実際のAssembly
インスタンスを渡すことをサポートしていないので、私はassembly
を使うことはできません。ダイナミックアセンブリとしては場所がないので、MetadataReference.CreateFromFile()
は使用できません。
これまで成功したこのタイプのものにMetadataReference.CreateFromStream()
を使用しましたが、アセンブリがスクリプトの提出を表すときには動作していないようです(理由はわかりません)。
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.
私は対を評価する際には提出アセンブリが異なるコンテキストにあるとは何かを持って推測している:できるだけ早くあなたがのようなエラーを取得し提出からタイプを使用しようとすると、コンパイルが進むが、バイト配列としてロードされます。私は、後のスクリプト以外のコンパイルでスクリプト提出で定義されたオブジェクトとメソッドを使用する最良の方法についての洞察や指導が大好きです。
更新7/29
私は、問題を示し、最小限のREPROを得ることができました。それはhttps://github.com/daveaglick/ScriptingAssemblyReuseで見つけることができます。
この問題の重要な要素は、スクリプトがクラスのうちの1つのクラスのType
を呼び出しコードに渡してから、呼び出しコードがType
のインスタンスをインスタンス化することです。オブジェクトを作成し、インスタンスをスクリプトアセンブリを参照するコンパイルに渡します。この不一致は、ホストアプリケーションによって作成された型のインスタンスから参照元のコンパイル内の型にキャストするときに発生します。私はそれが非常に混乱して聞こえることを再読んで、次のコードは、それを明確にすることを願っています。
namespace ScriptingAssemblyReuse
{
public class Globals
{
public IFactory Factory { get; set; }
}
public interface IFactory
{
object Get();
}
public class Factory<T> : IFactory where T : new()
{
public object Get() => 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<object> script = CSharpScript.Create(@"
public class Foo { }
Factory = new ScriptingAssemblyReuse.Factory<Foo>();
", 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 => 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 && args.Name == _scriptAssembly.FullName)
{
// Return the dynamically compiled script assembly if given it's name
return _scriptAssembly;
}
return null;
}
}
}
スタックトレース全体を共有できますか?実際にそれを引き起こしたコードは?あなたが説明しているシナリオはうまくいきます。また、どのようにスクリプトから型を参照していますか?それらは不正な識別子( '#')を持っています。 –
@EliArbelアプリケーションはかなり複雑なので、私はそれをより単純なreproに分解しようとしています。私は上記の正確なInvalidCastExceptionを取得するのが難しいですし、その代りに消費型コンパイルで送信からタイプを使用しようとすると、TypeLoadExceptionを取得しています。私はこれが興味深く、おそらく関連していると感じます。私がこれまで持っていた再現策はここにあります:https://github.com/daveaglick/ScriptingAssemblyReuse – daveaglick
@EliArbelリンクされたGitHubリポジトリと上の両方にサンプルコードを追加しました – daveaglick