.Net Core 2.2.300 Roslyn CodeAnalysis - CSharpCompilation не позволяет мне удалить файл, который используется для хранения кода на c # - PullRequest
0 голосов
/ 30 октября 2019

ОС: Windows 10 |.Net Core - 2.2.300 |Редактор кода Visual Studio Исходный код: https://github.com/CoviloMilos/CodeChallenge/tree/master/CodeCompiler/CodeCompilerAPI

Идея этого API заключается в получении запроса post с телом, в котором в качестве строки указан код c #. После этого я передаю этот строковый код c # классу Services / CompileService.cs, который выдает CSharpCompilation. Кроме того, если EmitResult успешен, я выполняю переданный код (точнее переданный метод) с различными типами входных параметров для этого метода. Все работает нормально, но проблема в том, что файл dll, созданный для хранения переданного кода C # и для CSharpCompilation, не может быть удален, потому что этот файл все еще используется. Я получаю сообщение об ошибке: (UnauthorizedAccessException: доступ к пути запрещен) . Моя идея заключается в том, что после компиляции и проверки кода внутри DLL-файла его следует удалить. Кроме того, я хотел создать сценарий, который принудительно удалял бы этот файл, но не пытался это сделать, потому что принудительное удаление с помощью PowerShell и CMD не работает.

using System.Collections;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using CodeCompilerAPI.Interfaces;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;


namespace CodeCompilerAPI.Services
{
    public class CompileService : ICompileService
    {

        //example of request body
        /*public bool checkNum(int n) { if (n%2==0) { return true; }else { return false; }}*/

        public Models.Task CompileCode(string inputCode, Models.Task task)
        {

            string code = @"    using System;
                                namespace CodeCompilerAPI
                                {
                                    public class Compile
                                    {";
            code += inputCode + "}}";

            //Produces a syntax tree by parsing the source text.
            var tree = SyntaxFactory.ParseSyntaxTree(code);

            //name of file which will be used to store code
            string fileName = "Temp" + DateTime.Now.ToString("dddd-dd-MMMM-yyyy-HH-mm-ss") + ".dll";
            string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);

            var systemRefLocation = typeof(object).GetTypeInfo().Assembly.Location;
            var systemReference =  MetadataReference.CreateFromFile(systemRefLocation);

            var compilation = CSharpCompilation.Create(fileName)
                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                .AddReferences(systemReference)
                .AddSyntaxTrees(tree);


            EmitResult compilationResult = compilation.Emit(path);


            if(compilationResult.Success)
            {
                Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);

                foreach (var testCase in task.Cases)
                {
                    try{

                        System.Object result = executeCode(asm, testCase.FirstInputParameter, testCase.SecondInputParameter, task.MethodName, task.ReturnDataType, task.FirstInputParameterDataType, task.SecondInputParameterDataType);

                        switch (task.ReturnDataType)
                        {
                            case "bool":
                                if (bool.Parse(testCase.ValidReturnValue)  == (bool) result) 
                                    testCase.CaseResult = true;
                                else
                                    testCase.CaseResult = false;
                                break;

                            case "array":
                                var tempValidReturnValue = testCase.ValidReturnValue.Split(",").Select(x => Int32.Parse(x)).ToArray();
                                if (tempValidReturnValue.SequenceEqual(((IEnumerable)result).Cast<int>()) == true)
                                    testCase.CaseResult = true; 
                                break;

                            case "string":
                                var caseResult = string.Compare(testCase.ValidReturnValue, result.ToString());
                                testCase.CaseResult = caseResult == 0 ? true : false;
                                break;

                        }


                        string info = $"Test case num: {testCase.CaseNum.ToString()} \nTest case valid result: {testCase.ValidReturnValue.ToString()} \nActual test case result: {result.ToString()}";
                        Console.WriteLine(info);


                    }catch( Exception e) {
                        var helpLinks = e.HelpLink;
                        string issue = $"Message: {e.GetBaseException().ToString()}, \nHelp link: {e.HelpLink}";
                        Console.WriteLine(issue);
                    }
                }
                DeleteFile(fileName);
                return task;
            }
            else
            {
                foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
                {
                    string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}, Location: {codeIssue.Location.GetLineSpan()}, Severity: {codeIssue.Severity}";
                    Console.WriteLine(issue);
                }
                DeleteFile(fileName);
                return null;
            }
        }

        private void DeleteFile(string fileName) {

            var dir = new DirectoryInfo(Directory.GetCurrentDirectory().ToString());

            foreach (var file in dir.EnumerateFiles("Temp*.dll")) {

                if (file.Name != fileName)
                    file.Delete();
            }
        }

        private System.Object executeCode(Assembly asm, string firstInputPara, string secondInputPara, string methodname, string returnDataType, string firstInputParaDataType, string secondInputParaDataType) 
        {

            if (secondInputPara == null) 
                return asm.GetType("CodeCompilerAPI.Compile").GetMethod(methodname).Invoke(null, new object[] { parseInputParameter(firstInputParaDataType, firstInputPara) });
            else if (secondInputPara != null)
                return asm.GetType("CodeCompilerAPI.Compile").GetMethod(methodname).Invoke(null, new object[] { parseInputParameter(firstInputParaDataType, firstInputPara), parseInputParameter(secondInputParaDataType, secondInputPara)});

            return null;
        }

        private System.Object parseInputParameter(string dataType, string inputParameter)
        {
            Object tempObj = new Object();
            switch (dataType)
            {
                case "int":
                    tempObj = Int32.Parse(inputParameter);
                    break;
                case "array":
                    tempObj = new int[inputParameter.Split(",").Length];
                    tempObj = Array.ConvertAll(inputParameter.Split(","), int.Parse);
                    break;
                case "bool":
                    tempObj = Boolean.Parse(inputParameter);
                    break;
                case "string":
                    tempObj = (String) inputParameter;
                    break;
                default:
                    tempObj = null;
                    break;
            }

            return tempObj;
        }
    }
} ```


Can someone help me how to solve this problem? Is there any way to abort CSharpCompilation or force delete
...