简体   繁体   中英

Roslyn has no reference to System.Runtime

I'm working on a project where we are using Roslyn to compile some templates for us. Now when I'm compiling the template I'm receiving multiple errors in the CompileResult.Diagnostics .

The errors are:

(21,6): error CS0012: The type 'System.Attribute' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
(21,6): error CS0012: The type 'System.Type' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Seeing these errors I assumed I didn't add a reference to the System.Runtime assembly correctly. However, after checking the loaded assemblies this appears to be in order.

private IEnumerable<MetadataReference> GetGlobalReferences()
{
    var assemblies = new [] 
        {
            typeof(System.Object).Assembly,                         //mscorlib
            typeof(System.Composition.ExportAttribute).Assembly,    //System.Composition (MEF)
            typeof(System.CodeDom.Compiler.CodeCompiler).Assembly,  //System.CodeDom.Compiler
        };

    var refs = from a in assemblies
                select new MetadataFileReference(a.Location);

    return refs.ToList();
}

And the compilation itself:

public void Compile(AssemblyFileInfo assemblyInfo)
{
    Parse(assemblyInfo.Files);

    var compilation = CSharpCompilation.Create(assemblyInfo.FilePath, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                        .AddReferences(GetGlobalReferences())
                        .AddSyntaxTrees(assemblyInfo.SourceCodeSyntaxTrees);

    assemblyInfo.CompileResult = compilation.Emit(assemblyInfo.FilePath);
}

Am I missing something obvious? It looks like all the prerequisites for a successfull compilation are satisfied, but apparently they aren't.

For reference, this is the (obfuscated) piece of code I'm trying to compile:

namespace Project.Rules.Generated
{
    using System;
    using System.Runtime;
    using System.Composition;
    using System.CodeDom.Compiler;

    [Export(typeof(IProject))]
    [GeneratedCode("Project Template Compiler", "1.0")]
    public sealed class ProcessPriorityValue : ProjectConcreteClass
    {
        public override void Execute(ProjectExecutionContext ctx)
        {
            CurrentContext = ctx;
        }
    }
}

edit I've advanced in my searchings a bit. The PublicKeyToken specified in the error messages match the PublicKeyToken of the System.Composition assemblies. My guess was adding all of the assemblies could possibly fix the issue. This is correct, or at least part of the solution. By using dotPeek I was able to check which objects exist in the different assemblies. With this knowledge I've changed the GetGlobalReferences() method to this:

private IEnumerable<MetadataReference> GetGlobalReferences()
{
    var assemblies = new [] 
        {
            typeof(System.Object).Assembly,                                     //mscorlib
            typeof(System.Composition.ExportAttribute).Assembly,                //System.Composition.AttributeModel
            typeof(System.Composition.Convention.ConventionBuilder).Assembly,   //System.Composition.Convention
            typeof(System.Composition.Hosting.CompositionHost).Assembly,        //System.Composition.Hosting
            typeof(System.Composition.CompositionContext).Assembly,             //System.Composition.Runtime
            typeof(System.Composition.CompositionContextExtensions).Assembly,   //System.Composition.TypedParts
            typeof(System.CodeDom.Compiler.CodeCompiler).Assembly,              //System.CodeDom.Compiler
        };

    var refs = from a in assemblies
                select new MetadataFileReference(a.Location);

    return refs.ToList();
}

As you can see, I'm adding all System.Composition assemblies now by specifying an object which exists in the assembly. By adding the System.Composition.Runtime , my compile errors were solve.

This did introduce a different error though. As you can check out yourself, there is a generic Export class in System.Composition.Runtime : public sealed class Export<T> : IDisposable Because of this I'm now getting this error:

(21,6): error CS0404: Cannot apply attribute class 'System.Composition.Export<T>' because it is generic

For some reason the code now wants to use System.Composition.Runtime.Export<T> and not the ExportAttribute, defined in System.Composition.AttributeModel assembly.

edit2

I can confirm the above code uses the System.Composition.Export<T> and not the ExportAttribute . I discovered this by changing the [Export(typeof(IProject)] to [ExportAttribute(typeof(IProject)] . Because of this change, the original errors returned. It looks like the assembly System.Composition.AttributeModel isn't loaded/referenced correctly, but after checking the compilation , I can see the assembly is referenced ok.

Referenced assemblies I've got at the moment are:

+       [0] mscorlib, Version=4.0.0.0, Culture=neutral, PublicKey=00000000000000000400000000000000  Microsoft.CodeAnalysis.AssemblyIdentity
+       [1] System.Composition.AttributedModel, Version=1.0.20.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293   Microsoft.CodeAnalysis.AssemblyIdentity
+       [2] System.Composition.Convention, Version=1.0.20.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293    Microsoft.CodeAnalysis.AssemblyIdentity
+       [3] System.Composition.Hosting, Version=1.0.20.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293   Microsoft.CodeAnalysis.AssemblyIdentity
+       [4] System.Composition.Runtime, Version=1.0.20.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293   Microsoft.CodeAnalysis.AssemblyIdentity
+       [5] System.Composition.TypedParts, Version=1.0.20.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293    Microsoft.CodeAnalysis.AssemblyIdentity
+       [6] Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293  Microsoft.CodeAnalysis.AssemblyIdentity
+       [7] System, Version=4.0.0.0, Culture=neutral, PublicKey=00000000000000000400000000000000    Microsoft.CodeAnalysis.AssemblyIdentity
+       [8] System.Core, Version=4.0.0.0, Culture=neutral, PublicKey=00000000000000000400000000000000   Microsoft.CodeAnalysis.AssemblyIdentity
+       [9] System.Data, Version=4.0.0.0, Culture=neutral, PublicKey=00000000000000000400000000000000   Microsoft.CodeAnalysis.AssemblyIdentity

By request of Dejan in the comment section, I'll post the answer (to my problem) as a real answer.


I've found out what the problem was! The compiler was correct all along. A blog post of smack0007 triggered me of trying something else. Instead of using the Facade dll, try referencing the necessary dll's manually. The GetGlobalReferences method now looks like this:

private IEnumerable<MetadataReference> GetGlobalReferences()
{
    var assemblies = new [] 
        {
            /*Making sure all MEF assemblies are loaded*/
            typeof(System.Composition.Convention.AttributedModelProvider).Assembly, //System.Composition.AttributeModel
            typeof(System.Composition.Convention.ConventionBuilder).Assembly,   //System.Composition.Convention
            typeof(System.Composition.Hosting.CompositionHost).Assembly,        //System.Composition.Hosting
            typeof(System.Composition.CompositionContext).Assembly,             //System.Composition.Runtime
            typeof(System.Composition.CompositionContextExtensions).Assembly,   //System.Composition.TypedParts

            /*Used for the GeneratedCode attribute*/
            typeof(System.CodeDom.Compiler.CodeCompiler).Assembly,              //System.CodeDom.Compiler
        };

    var refs = from a in assemblies 
                select new MetadataFileReference(a.Location);
    var returnList = refs.ToList();

    //The location of the .NET assemblies
    var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);

    /* 
        * Adding some necessary .NET assemblies
        * These assemblies couldn't be loaded correctly via the same construction as above,
        * in specific the System.Runtime.
        */
    returnList.Add(new MetadataFileReference(Path.Combine(assemblyPath, "mscorlib.dll")));
    returnList.Add(new MetadataFileReference(Path.Combine(assemblyPath, "System.dll")));
    returnList.Add(new MetadataFileReference(Path.Combine(assemblyPath, "System.Core.dll")));
    returnList.Add(new MetadataFileReference(Path.Combine(assemblyPath, "System.Runtime.dll")));

    return returnList;
}

When decompiling the System.Runtime.dll I also saw why it couldn't be referenced in any other way. The dll is empty, it only contains some references to other assemblies. Therefore one can't reference this assembly in any other way.

It appears that you are referencing a PortableClassLibrary. Portable Class Libraries pick up some of the basic types (like object/string/etc) from "System.Runtime.dll". However, in the desktop framework these come from mscorlib.dll. When you use typeof(object).Assembly , you get Roslyn's own version of object . Since Roslyn is not built as Portable Class Libraries, that is the one from mscorlib, and doesn't match the identity of your other references.

The predefined list of references was not working for me.

But getting them from the AppDomain helped:

public static readonly List<PortableExecutableReference> References = 
    AppDomain.CurrentDomain.GetAssemblies()
        .Where(_ => !_.IsDynamic && !string.IsNullOrWhiteSpace(_.Location))
        .Select(_ => MetadataReference.CreateFromFile(_.Location))
        .Concat(new[]
        {
            // add your app/lib specifics, e.g.:                      
            MetadataReference.CreateFromFile(typeof(MyType).Assembly.Location),
        })
        .ToList();

I found the answer in this helpful repo: https://github.com/andrewlock/NetEscapades.EnumGenerators

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM