Azure / Azure/azure-functions-host
Could not load file or assembly 'Microsoft.CodeAnalysis'
- Dominant language
- C#
- Stars
- 2k
- Forks
- 482
- Avg merge
- 2d 12h
- Merged PRs (30d)
- 38
Description
So I built a simple script runner class to take an arbitrary block of C# code and run it by using Roslyns scripting API's.
This functionality works everywhere except in my azure functions app for some reason.
```
public static class Execute
{
[FunctionName("Execute")]
public static async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "Execute")]HttpRequestMessage req)
{
var json = await req.Content.ReadAsStringAsync();
var request = (json.Contains("$type")
? JsonConvert.DeserializeObject(json, ObjectExtensions.JSONSettings)
: JsonConvert.DeserializeObject>(json)) as ScriptRequest;
return await new ScriptRunner(Log).Run(request.code, request.Imports, request.args, Log);
}
void Log(LogLevel level, string message) => Console.WriteLine(message);
}
```
Looking at the logged output from the ctor in the following "ScriptRunner" class I see that it outputs ...
> Loaded: Microsoft.CodeAnalysis.CSharp.Scripting, Version=3.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
Then I get:
> Could not load file or assembly 'Microsoft.CodeAnalysis, Version=3.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
I'm using the latest version from nuget (without ticking the include previews option) of all dependencies.
So given that it can both see it but not see it at the same time and as a console app this runs fine I figure this must a functions runtime issue?
```
using Core.Objects;
using Core.Objects.Dtos.Workflow;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Workflow
{
internal class ScriptRunner : IScriptRunner
{
internal static Assembly[] references;
public ScriptRunner(LogEvent log)
{
try
{
if (references == null)
{
// some of the stack might not have been loaded, lets make sure we load everything so we can give a complete response
// first grab what's loaded
var loadedAlready = AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => !a.IsDynamic)
.ToList();
// then grab the bin directory
var thisAssembly = Assembly.GetExecutingAssembly();
var binDir = thisAssembly.Location
.Replace(thisAssembly.ManifestModule.Name, "");
log(WorkflowLogLevel.Debug, $"Bin Directory: {binDir}");
// from the bin, grab our core dll files
var stackDlls = Directory.GetFiles(binDir)
//.Select(i => i.ToLowerInvariant())
.Where(f => f.EndsWith("dll"))
.ToList();
loadedAlready.ForEach(a => log(WorkflowLogLevel.Info, $"Loaded: {a.FullName} "));
// load the missing ones
var toLoad = stackDlls
.Where(assemblyPath => loadedAlready.All(a => a.CodeBase.ToLowerInvariant() != assemblyPath.ToLowerInvariant()))
.Where(a => !a.Contains("api-ms-win"))
.ToArray();
foreach (var assemblyPath in toLoad)
{
try
{
var a = Assembly.LoadFile(assemblyPath);
loadedAlready.Add(a);
log(WorkflowLogLevel.Info, $"Loaded: {a.FullName} ");
}
catch (Exception ex)
{
log(WorkflowLogLevel.Warning, $"Unable to load assembly {assemblyPath} because: " + ex.Message);
}
}
references = loadedAlready.ToArray();
}
}
catch (Exception ex)
{
log(WorkflowLogLevel.Warning, "Script Runner may not have everything it needs, continuing anyway despite exception:\n" + ex.Message);
}
}
public async Task BuildScript(string code, string[] imports, Action log)
{
try
{
var referencesNeeded = references.Where(r => r.GetExportedTypes().Any(t => imports.Contains(t.Namespace)));
var options = ScriptOptions.Default
.AddReferences(referencesNeeded)
.WithImports(imports);
if (log != null)
{
var message = $"\nImports\n {string.Join("\n ", imports)}\n\nReferences Needed\n {string.Join("\n ", referencesNeeded.Select(r => r.FullName))}";
log(WorkflowLogLevel.Debug, message);
}
return await CSharpScript.EvaluateAsync(code, options);
}
catch (Exception ex)
{
log(WorkflowLogLevel.Error, "Script failed to compile.");
log(WorkflowLogLevel.Error, ex.Message);
if (ex is CompilationErrorException cEx)
log(WorkflowLogLevel.Error, $"Source of the problem:\n{cEx.Source}");
return default;
}
}
public async Task Run(string code, string[] imports, object args, Action log)
{
try
{
var referencesNeeded = references.Where(r => r.GetExportedTypes().Any(t => imports.Contains(t.Namespace)));
var options = ScriptOptions.Default
.AddReferences(referencesNeeded)
.WithImports(imports);
if (log != null)
{
var message = $"\nImports\n {string.Join("\n ", imports)}\n\nReferences Needed\n {string.Join("\n ", referencesNeeded.Select(r => r.FullName))}";
log(WorkflowLogLevel.Debug, message);
}
return await CSharpScript.EvaluateAsync(code, options, args, args.GetType());
}
catch (NullReferenceException ex)
{
var typeAndCall = $"(({ex.TargetSite.DeclaringType.Name})object).{ex.TargetSite.Name}";
var data = new List();
foreach (var k in ex.Data.Keys) data.Add($"{k}: {ex.Data[k]}");
throw new Exception(ex.Message + $"\nContext: {ex.Source}\nTarget: {typeAndCall}\n{string.Join("\n", data)}");
}
catch (CompilationErrorException ex)
{
throw new Exception($"Compilation failed:\n{ex.Message}\n{string.Join(Environment.NewLine, ex.Diagnostics)}");
}
}
public Task Run(string code, string[] imports, object args, Action log)
=> Run(code + ";return true;", imports, args, log);
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.