adoconnection / RazorEngineCore

.NET6 Razor Template Engine
MIT License
585 stars 88 forks source link

RazorEngineCore.RazorEngine.Compile not work when publish to single file #38

Closed jddj007-hydra closed 3 years ago

jddj007-hydra commented 3 years ago

target framework is .net5
when i publish exe to single file ,compile a templeate ,get this error: System.ArgumentException: Empty path name is not legal. (Parameter 'path') at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) at System.IO.File.OpenRead(String path) at Roslyn.Utilities.FileUtilities.OpenFileStream(String path) at Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(String path, MetadataReferenceProperties properties, DocumentationProvider documentation) at RazorEngineCore.RazorEngine.<>c.b__4_1(Assembly ass) at System.Linq.Enumerable.SelectEnumerableIterator2.MoveNext() at System.Collections.Generic.List1.InsertRange(Int32 index, IEnumerable1 collection) at System.Collections.Generic.List1.AddRange(IEnumerable1 collection) at System.Linq.Enumerable.ConcatIterator1.ToList() at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at RazorEngineCore.RazorEngine.CreateAndCompileToStream(String templateSource, RazorEngineCompilationOptions options) at RazorEngineCore.RazorEngine.Compile(String content, Action1 builderAction)

if not single file , it ok

jddj007-hydra commented 3 years ago

i try to debug find this : ass name System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0\System.Private.CoreLib.dll ass name Microsoft.CSharp, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0\Microsoft.CSharp.dll ass name RazorEngineCore, Version=2020.10.1.0, Culture=neutral, PublicKeyToken=null when MetadataReference.CreateFromFile(ass.Location) ass is razorEngineCore it's Location is null

jddj007-hydra commented 3 years ago

looks like “MetadataReference.CreateFromFile” method not support Single-File Apps

jddj007-hydra commented 3 years ago

https://github.com/dotnet/runtime/issues/36590#issuecomment-689883856 referance this modify CreateAndCompileToStream

  CSharpCompilation compilation = CSharpCompilation.Create(
                fileName,
                new[]
                {
                    syntaxTree
                },
                options.ReferencedAssemblies
                    .Select(ass =>
                    { 
                        unsafe
                        {
                            ass.TryGetRawMetadata(out byte* blob, out int length);
                            var moduleMetadata = ModuleMetadata.CreateFromMetadata((IntPtr)blob, length);
                            var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
                            var metadataReference = assemblyMetadata.GetReference();
                            return metadataReference;
                        } 
                        // return  MetadataReference.CreateFromFile(ass.Location); 
                    })
                    .Concat(options.MetadataReferences)
                    .ToList(),
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

it's worked

adoconnection commented 3 years ago

Interesting! thanks for pointing out!

kobruleht commented 3 years ago

I got this error also in Linux. Will this change added to nuGet package ?

afucher commented 3 years ago

Still having this issue with the latest version.

@jddj007-hydra how did you make it works?

adoconnection commented 3 years ago

It’s not yet in release

afucher commented 3 years ago

@adoconnection any Idea when it will be?

adoconnection commented 3 years ago

@kobruleht @afucher @jddj007-hydra Can anyone help me reproduce this problem. I created console app (Core 3.1) with code from MetadataReference unit test, published it as self contained single file and it worked.

See TestCompileAndRun_MetadataReference https://github.com/adoconnection/RazorEngineCore/blob/7edfa1eff96dfe522d2dde074173e9ca1bb5727b/RazorEngineCore.Tests/TestCompileAndRun.cs#L503

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using RazorEngineCore;

namespace ConsoleApp44_razorMetadata
{
    class Program
    {
        static void Main(string[] args)
        {
            string greetingClass = @"
namespace TestAssembly
{
    public static class Greeting
    {
        public static string GetGreeting(string name)
        {
            return ""Hello, "" + name + ""!"";
        }
    }
}
";
            // This needs to be done in the builder to have access to all of the assemblies added through
            // the various AddAssemblyReference options
            CSharpCompilation compilation = CSharpCompilation.Create(
                "TestAssembly",
                new[]
                {
                        CSharpSyntaxTree.ParseText(greetingClass)
                },
                GetMetadataReferences(),
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            );

            MemoryStream memoryStream = new MemoryStream();
            EmitResult emitResult = compilation.Emit(memoryStream);

            memoryStream.Position = 0;

            // Add an assembly resolver so the assembly can be found
            AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) =>
                    new AssemblyName(eventArgs.Name ?? string.Empty).Name == "TestAssembly"
                            ? Assembly.Load(memoryStream.ToArray())
                            : null;

            RazorEngine razorEngine = new RazorEngine();
            IRazorEngineCompiledTemplate template = razorEngine.Compile(@"
@using TestAssembly
<p>@Greeting.GetGreeting(""Name"")</p>
", builder =>
            {
                builder.AddMetadataReference(MetadataReference.CreateFromStream(memoryStream));

            });

            string expected = @"
<p>Hello, Name!</p>
";
            string actual = template.Run();
            Console.WriteLine(actual);
            Console.ReadKey();
        }

        private static List<MetadataReference> GetMetadataReferences()
        {
            if (RuntimeInformation.FrameworkDescription.StartsWith(
                ".NET Framework",
                StringComparison.OrdinalIgnoreCase))
            {
                return new List<MetadataReference>()
                {
                        MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                        MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")).Location),
                        MetadataReference.CreateFromFile(Assembly.Load(
                            new AssemblyName(
                                "netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")).Location),
                        MetadataReference.CreateFromFile(typeof(System.Runtime.GCSettings).Assembly.Location)
                };
            }

            return new List<MetadataReference>()
            {
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                    MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
                    MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location),
                    MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location)
            };
        }
    }
}
kobruleht commented 3 years ago

Can you try it in .NET 5 by changing TargetFramework attribute in project file:

  <PropertyGroup>
    <!-- <TargetFramework>netcoreapp3.1</TargetFramework>-->
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
adoconnection commented 3 years ago

got the exception. apparently its a NET5 breaking change...

afucher commented 3 years ago

Hey @adoconnection do you have any update on this? Can I help you in any way?

adoconnection commented 3 years ago

@jddj007-hydra @afucher @kobruleht package 2021.3.1 published! https://www.nuget.org/packages/RazorEngineCore/2021.3.1

afucher commented 3 years ago

I made a quick test here, and it gives me error:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HM6T394B63R1", Request id "0HM6T394B63R1:00000007": An unhandled exception was thrown by the application.
      System.ArgumentNullException: Value cannot be null. (Parameter 'path1')
         at System.IO.Path.Combine(String path1, String path2)
         at PugPdf.Core.WkHtmlToPdfDriver.GetExecutablePath()
         at PugPdf.Core.WkHtmlToPdfDriver.GetPath()
         at PugPdf.Core.WkHtmlToPdfDriver.ConvertAsync(String html, String switches)
         at PugPdf.Core.HtmlToPdf.RenderHtmlAsPdfAsync(String html)
adoconnection commented 3 years ago

Is it RazorEngineCore exception? Any steps to reproduce would be helpful

afucher commented 3 years ago

Sorry, it is not. Is another library... my mistake 😞

adoconnection commented 3 years ago

Closing issue then :)