toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.51k stars 260 forks source link

Cannot render template in docker container #336

Open darind opened 4 years ago

darind commented 4 years ago

Describe the bug

I am trying to render a razor template in a docker container using .NET Core 3.1.

Getting the following error at runtime:

Unhandled exception. System.InvalidOperationException: Cannot find reference assembly 'Microsoft.AspNetCore.Antiforgery.dll' file for package Microsoft.AspNetCore.Antiforgery.Reference
   at Microsoft.Extensions.DependencyModel.Resolution.ReferenceAssemblyPathResolver.TryResolveAssemblyPaths(CompilationLibrary library, List`1 assemblies)
   at Microsoft.Extensions.DependencyModel.Resolution.CompositeCompilationAssemblyResolver.TryResolveAssemblyPaths(CompilationLibrary library, List`1 assemblies)
   at Microsoft.Extensions.DependencyModel.CompilationLibrary.ResolveReferencePaths(ICompilationAssemblyResolver resolver, List`1 assemblies)
   at Microsoft.Extensions.DependencyModel.CompilationLibrary.ResolveReferencePaths()
   at RazorLight.Compilation.DefaultMetadataReferenceManager.<>c.<Resolve>b__10_0(CompilationLibrary library)
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at RazorLight.Compilation.DefaultMetadataReferenceManager.Resolve(Assembly assembly, DependencyContext dependencyContext)
   at RazorLight.Compilation.DefaultMetadataReferenceManager.Resolve(Assembly assembly)
   at RazorLight.Compilation.RoslynCompilationService.EnsureOptions()
   at RazorLight.Compilation.RoslynCompilationService.get_ParseOptions()
   at RazorLight.Compilation.RoslynCompilationService.CreateSyntaxTree(SourceText sourceText)
   at RazorLight.Compilation.RoslynCompilationService.CreateCompilation(String compilationContent, String assemblyName)
   at RazorLight.Compilation.RoslynCompilationService.CompileAndEmit(IGeneratedRazorTemplate razorTemplate)
   at RazorLight.Compilation.RazorTemplateCompiler.CompileAndEmit(RazorLightProjectItem projectItem)
   at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey)
--- End of stack trace from previous location where exception was thrown ---
   at RazorLight.EngineHandler.CompileTemplateAsync(String key)
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag)
   at TestApp.Program.Main(String[] args)
   at TestApp.Program.<Main>(String[] args)

To Reproduce

Create a new .NET Core console application and add reference to the RazorLight NuGet. Also add the following to the corresponding .csproj:

<PropertyGroup>
    <PreserveCompilationReferences>true</PreserveCompilationReferences>
    <PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

Render a simple template in the app:

using System;
using System.Threading.Tasks;
using RazorLight;

namespace TestApp
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var engine = new RazorLightEngineBuilder()
                .UseEmbeddedResourcesProject(typeof(Program))
                .UseMemoryCachingProvider()
                .Build();

            var message = "Hello @Model.Name";
            var content = await engine.CompileRenderStringAsync(message, message, new
            {
                Name = "world",
            });

            Console.WriteLine(content);
        }
    }
}

To build the docker container use the following Dockerfile:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build
WORKDIR /source

# copy csproj and restore as distinct layers
COPY *.sln .
COPY TestApp/*.csproj ./TestApp/
RUN dotnet restore ./TestApp/TestApp.csproj -r alpine-x64

# copy everything else and build app
COPY TestApp/. ./TestApp/
WORKDIR /source/TestApp

RUN dotnet publish \
       -c Release \
       -o ./publish \
       -r alpine-x64 \
       --self-contained true \
       --no-restore \
       /p:PublishTrimmed=true \
       /p:PublishSingleFile=true \
       /p:UseAppHost=true

FROM alpine:3.9.4
# Add some libs required by .NET runtime
RUN apk add --no-cache libstdc++ libintl

# Copy
WORKDIR /app
COPY --from=build /source/TestApp/publish ./

ENTRYPOINT [ "./TestApp" ]

Then build the image:

docker build -t test-app .

And run:

docker run -e DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 test-app

Expected behavior

The application runs without errors and prints "Hello world".

Information (please complete the following information):

Additional context

If I remove the /p:PublishTrimmed=true and /p:PublishSingleFile=true parameters when compiling the app, then it works fine:

RUN dotnet publish \
       -c Release \
       -o ./publish \
       -r alpine-x64 \
       --self-contained true \
       --no-restore \
       /p:UseAppHost=true

So it looks like the IL linker is stripping some information that RazorLight depends on.

jzabroski commented 4 years ago

Thanks. This will help #299 a lot. A little off-topic for your issue but, since you're here: Is alpine-x64 capable of testing Unicode features? I thought it wasn't? And maybe that's why you're also passing DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1?

darind commented 4 years ago

@jzabroski, what are you referring to as Unicode features? I am using DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 because my app doesn't need to support any other cultures and I am using a slimmed down version of alpine-x64 which doesn't include the corresponding dependencies.

bvmeer commented 4 years ago

Any update on this issue? I have the same issue, when using the RazorLight package within a REST API to send a email based on razor templates. (.net core 3.1) The issue resolved when adding a reference to nuget package Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation