runtime: Cannot resolve assembly paths for reference compilation libraries

There is no implementation of ICompilationAssemblyResolver that will resolve paths for a CompilationLibrary with a Type of "reference", so when trying to use libraries like Microsoft.AspNetCore.Mvc that call ResolveReferencePaths, an exception is thrown:

System.InvalidOperationException: Can not find compilation library location for package 'MyLibrary'

This is a regression with 2.x, as the following used to work:

<Reference Include="MyLibrary">
    <HintPath>../../path/to/MyLibrary.dll</HintPath>
</Reference>

I have been reading everything I can find surrounding this issue and came across the following comment:

https://github.com/dotnet/sdk/issues/1213#issuecomment-302734715

However, this is not an applicable solution for projects that do in fact require PreserveCompilationContext to be true.

Here is what I came up with as a workaround, that lets us develop locally with MVC and also use the precompilation tool for deploys:

    public class MvcConfiguration : IDesignTimeMvcBuilderConfiguration
    {
        private class DirectReferenceAssemblyResolver : ICompilationAssemblyResolver
        {
            public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> assemblies)
            {
                if (!string.Equals(library.Type, "reference", StringComparison.OrdinalIgnoreCase))
                {
                    return false;
                }

                var paths = new List<string>();

                foreach (var assembly in library.Assemblies)
                {
                    var path = Path.Combine(ApplicationEnvironment.ApplicationBasePath, assembly);

                    if (!File.Exists(path))
                    {
                        return false;
                    }

                    paths.Add(path);
                }

                assemblies.AddRange(paths);

                return true;
            }
        }

        public void ConfigureMvc(IMvcBuilder builder)
        {
            // .NET Core SDK v1 does not pick up reference assemblies so
            // they have to be added for Razor manually. Resolved for
            // SDK v2 by https://github.com/dotnet/sdk/pull/876 OR SO WE THOUGHT
            /*builder.AddRazorOptions(razor =>
            {
                razor.AdditionalCompilationReferences.Add(
                    MetadataReference.CreateFromFile(
                        typeof(PdfHttpHandler).Assembly.Location));
            });*/

            // .NET Core SDK v2 does not resolve reference assemblies' paths
            // at all, so we have to hack around with reflection
            typeof(CompilationLibrary)
                .GetTypeInfo()
                .GetDeclaredField("<DefaultResolver>k__BackingField")
                .SetValue(null, new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[] 
                {
                    new DirectReferenceAssemblyResolver(),
                    new AppBaseCompilationAssemblyResolver(),
                    new ReferenceAssemblyPathResolver(),
                    new PackageCompilationAssemblyResolver(),
                }));
        }
    }

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 2
  • Comments: 50 (19 by maintainers)

Commits related to this issue

Most upvoted comments

Here’s a couple other workarounds for 2.0.0 until we have a resolution:

  • If your application does not have any views, add <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish> to disable Razor view compilation.
  • If your application does have views, you can replace the default MetadataReferenceFeatureProvider to not throw when it encounters dll references:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.DependencyModel;

namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
    public class ReferencesMetadataReferenceFeatureProvider : IApplicationFeatureProvider<MetadataReferenceFeature>
    {
        public void PopulateFeature(IEnumerable<ApplicationPart> parts, MetadataReferenceFeature feature)
        {
            var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var assemblyPart in parts.OfType<AssemblyPart>())
            {
                var dependencyContext = DependencyContext.Load(assemblyPart.Assembly);
                if (dependencyContext != null)
                {
                    foreach (var library in dependencyContext.CompileLibraries)
                    {
                        if (string.Equals("reference", library.Type, StringComparison.OrdinalIgnoreCase))
                        {
                            foreach (var libraryAssembly in library.Assemblies)
                            {
                              libraryPaths.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, libraryAssembly));
                            }
                        }
                        else
                        {
                            foreach (var path in library.ResolveReferencePaths())
                            {
                                libraryPaths.Add(path);
                            }
                        }
                    }
                }
                else
                {
                    libraryPaths.Add(assemblyPart.Assembly.Location);
                }
            }

            foreach (var path in libraryPaths)
            {
                feature.MetadataReferences.Add(CreateMetadataReference(path));
            }
        }

        private static MetadataReference CreateMetadataReference(string path)
        {
            using (var stream = File.OpenRead(path))
            {
                var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata);
                var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);

                return assemblyMetadata.GetReference(filePath: path);
            }
        }
    }
}

And in your Startup:

services.AddMvc()
    .ConfigureApplicationPartManager(manager =>
    {
        var oldMetadataReferenceFeatureProvider = manager.FeatureProviders.First(f => f is MetadataReferenceFeatureProvider);
        manager.FeatureProviders.Remove(oldMetadataReferenceFeatureProvider);
        manager.FeatureProviders.Add(new ReferencesMetadataReferenceFeatureProvider());
    });

In my case, Razor compilation was fixed by installing of "Microsoft.Extensions.DependencyModel" Version="2.0.3". Thanks.

This was merged and should be fixed in 2.0.1

I ran into this issue with .NET Core 2.1.1. I was using the RazorLight lib coupled with some cshtml files for FluentEmail. Having the cshtml email templates in my project triggered error “Cannot find compilation library location for package” after deployment. I truly did not need the Razor compilation, since it was being handled by RazorLight at runtime, so adding <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish> to the csproj resolved the issue

confirm it’s fixed with 2.0.3 November release, thanks

@ganeshmuthuvelu - I assume when you reference 2.0.2 above, you are talking about the .NET Core SDK version, correct?

Unfortunately, a new .NET Core SDK doesn’t fix this issue. The issue is in the Microsoft.Extensions.DependencyModel library, which ships when a new .NET Core Runtime ships, which should be coming soon.

Is this still a known issue and being worked on for a proper core update release?

Yes, the fix is in the release/2.0.0 servicing branch now, but we haven’t shipped a release since the fix was merged. The next 2.0.x release will have this fixed.

@tuespetre Thank you very much, it’s worked.

@tuespetre what you mean " put new MvcConfiguration().ConfigureMvc(mvcBuilder); in your ConfigureServices."? where is mvcBuilder from?

Is there a way to tell Razor compilation where to look for assemblies, or to ignore certain assemblies?

After upgrading to 2.0 i suffered for a long day until this hack by @pranavkm “worked the issue around” for me. My first shock was that PreserveCompilationContext came enabled by default, it caused severe problems with non-nuget assemblies that were added as path reference and Cefsharp.Core (most aren’t even used by cshtml views). I read somewhere about razor reference paths, but was unable to correctly configure that. When I disabled PreserveCompilationContext, then it started asking for manual input of the referenced assemblies, which for me would be okay, i was able to add assemblies with IRazorOptions.CompilationCallback, except for netstandard 2.0.0, which I couldn’t find a way to fetch with reflection (my project uses net462, Razor insisted that class “Object” is in netstandard, not mscorlib. I could get mscorlib file location at runtime, but not netstandard) By returning PreserveCompilationContext to true and using @pranavkm’s hack, it worked. Maybe razor compilation shouldn’t be so alarmful by default, maybe expose more ways to configure razor compilation: A way to tell razor to ignore dependency errors, a way to tell razor to ignore assemblies, a way to tell razor where to look for assemblies.

Possible fix for 2.01 is to add new resolver to Microsoft.Extensions.DependencyModel\Resolution that understands CompilationLibrary

Assigning to @eerhardt ; he will be able to evaluate this next week