Azure / Azure-Functions

1.11k stars 194 forks source link

Can't use Razor as a template engine in Azure Functions v2 (.NET Core) #1138

Open lurumad opened 5 years ago

lurumad commented 5 years ago

Hi folks,

I've uploaded two samples: a .NET Core 2.2 Console App and Azure Functions v2 (.NET Core). Both examples using Razor as a template engine. Console applications works fine:

image

Azure Functions v2 doesn't work:

image

I've tried many things but I'm not be able to find the problem. Any ideas?

Projects.zip

ColbyTresness commented 5 years ago

@fabiocav

Doddler commented 5 years ago

I've been struggling with this issue for a bit, and as best as I can tell it's the result of bugs with the build process. I was unable to make runtime compiled views work, but I was able to get pre-compiled views from a class library working. Here's the steps I went through to get it to work:

  1. I created a class library project for the razor views to live in. I updated the csproj file to include nuget packages for Microsoft.AspNetCore.All, Microsoft.AspNetCore.Mvc, and Microsoft.AspNetCore.Mvc.Razor.ViewCompilation, set the project line to use the razor sdk with <Project Sdk="Microsoft.NET.Sdk.Razor">, and also include <PreserveCompilationContext>true</PreserveCompilationContext> (not sure if this one is necessary on the class library). This should be enough to get the class library to pre-compile any .cshtml files in the Views folder.
  2. Add the class library as a reference within your azure functions project. You'll also need <PreserveCompilationContext>true</PreserveCompilationContext> to be set within your main azure functions project.
  3. Bug 1: Your azure functions project will not find any of your views because all of the assemblies are built into a folder called bin, but the ClassLibraryName.views.dll file does not get moved to the correct location. Bug 2: Same general problem, but PreserveCompilationContext option does not give the desired results because the generated FunctionsProjectName.deps.json also does not get moved to the bin folder. To work around these two problems, I added this to my functions project (make sure you change the ClassLibraryName and FunctionsProjectName to match your project):

    <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <ItemGroup>
      <OutputFiles Include="$(OutDir)ClassLibraryName.Views.*"></OutputFiles>
      <OutputFiles Include="$(OutDir)FunctionsProjectName.deps.json"></OutputFiles>
    </ItemGroup>
    
    <Move SourceFiles="@(OutputFiles)" DestinationFolder="$(OutDir)bin\" OverwriteReadOnlyFiles="true"></Move>
    </Target>

With that, you should at least be able to work around the issue.

As for why you can't use runtime compiled views, I'm not too sure yet. The fact that FunctionsProjectName.deps.json doesn't make it into the bin folder is 100% the reason you get that confusing stream of errors out of the compiling process. However, even manually resolving that I still got a different error (Cannot find compilation library location for package) and was unable to figure out how to solve it.

lurumad commented 5 years ago

Hi @Doddler

Thank you for taking your time with this issue.

I need to use runtime compiled views because customers can create their own templates. I'm going to move this logic outside Azure Functions.

Regards!

elmo61 commented 5 years ago

Any updates on this?

yoandyglez1989 commented 5 years ago

I've been struggling with this issue for a bit, and as best as I can tell it's the result of bugs with the build process. I was unable to make runtime compiled views work, but I was able to get pre-compiled views from a class library working. Here's the steps I went through to get it to work:

  1. I created a class library project for the razor views to live in. I updated the csproj file to include nuget packages for Microsoft.AspNetCore.All, Microsoft.AspNetCore.Mvc, and Microsoft.AspNetCore.Mvc.Razor.ViewCompilation, set the project line to use the razor sdk with <Project Sdk="Microsoft.NET.Sdk.Razor">, and also include <PreserveCompilationContext>true</PreserveCompilationContext> (not sure if this one is necessary on the class library). This should be enough to get the class library to pre-compile any .cshtml files in the Views folder.
  2. Add the class library as a reference within your azure functions project. You'll also need <PreserveCompilationContext>true</PreserveCompilationContext> to be set within your main azure functions project.
  3. Bug 1: Your azure functions project will not find any of your views because all of the assemblies are built into a folder called bin, but the ClassLibraryName.views.dll file does not get moved to the correct location. Bug 2: Same general problem, but PreserveCompilationContext option does not give the desired results because the generated FunctionsProjectName.deps.json also does not get moved to the bin folder. To work around these two problems, I added this to my functions project (make sure you change the ClassLibraryName and FunctionsProjectName to match your project):
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <ItemGroup>
      <OutputFiles Include="$(OutDir)ClassLibraryName.Views.*"></OutputFiles>
      <OutputFiles Include="$(OutDir)FunctionsProjectName.deps.json"></OutputFiles>
    </ItemGroup>

    <Move SourceFiles="@(OutputFiles)" DestinationFolder="$(OutDir)bin\" OverwriteReadOnlyFiles="true"></Move>
  </Target>

With that, you should at least be able to work around the issue.

As for why you can't use runtime compiled views, I'm not too sure yet. The fact that FunctionsProjectName.deps.json doesn't make it into the bin folder is 100% the reason you get that confusing stream of errors out of the compiling process. However, even manually resolving that I still got a different error (Cannot find compilation library location for package) and was unable to figure out how to solve it.

Hi @Doddler, I trying to get my azure function works with pre-compiled views from a Razor class library in the same way you mention here but I haven't succeeded. I'm also using the RazorViewEngine "GetView" and "FindView" APIs. Could you please share some sample code ?

onionhammer commented 4 years ago

Wish Microsoft would spend a tiny bit of time supporting this common scenario; html emails are super common and it’s so hard to generate html with razor outside of an Mvc context

bragma commented 4 years ago

Hi, I have the same problem here (views not found). I am moving from a webjob and I'd like to use precompiled views. Can anyone share a repo with a solution including a function and a razor class library to show how to make them work?

Thanks!

bragma commented 4 years ago

After some work I've been able to use an RCL from a function. I have not had enough time to try with compiled views, but I think they should work too. The cause of the problem was an incorrect setting of ApplicationName in the HostingEnvironment: most of the sample code do something like this:

                services.AddSingleton<IHostingEnvironment>(new HostingEnvironment
                {
                    ApplicationName = Assembly.GetEntryAssembly().GetName().Name,
                    WebRootFileProvider = fileProvider,
                });

The problem is that in a function, Assembly.GetEntryAssembly().GetName().Name evaluates to the string "func", which is obviously incorrect. Setting the value manually with the correct class name, everything worked out of the box.

onionhammer commented 4 years ago

@bragma interesting. Would you be able to share some sample?

When i tried using services.AddSingleton it freaked because functions already register that behind the scenes somehow

JontyMC commented 4 years ago

@bragma if you could share a working sample, that would be awesome

bragma commented 4 years ago

@bragma interesting. Would you be able to share some sample?

I'll put a repo on github as soon as I finish to fight with localization using resources in the RCL. It's a nightmare too...

bragma commented 4 years ago

Hello, I am completely stalled here. I have the function working perfectly locally, but it fails to start when deployed. The problems seems to be in the "AddMvcCore" call in the Startup function.

[assembly: FunctionsStartup(typeof(TestFunction.Startup))]

namespace TestFunction
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddMvcCore();
        }
    }
}

This is my startup, but I've also tried with "AddLogging" etc. Still it fails to start with "The function runtime is unable to start."

I am banging my head here... I don't know how to look for some debugging information.

sadranyi commented 4 years ago

is it .Net Core 3 ? looks like support just went into preview

bragma commented 4 years ago

is it .Net Core 3 ? looks like support just went into preview

Not sure if you are asking me, but I am using .Net core 2.2 with functions SDK 1.0.29.

sadranyi commented 4 years ago

is, it possible to share the code.

On Tue, Nov 5, 2019 at 11:46 AM Bragma notifications@github.com wrote:

is it .Net Core 3 ? looks like support just went into preview

Not sure if you are asking me, but I am using .Net core 2.2 with functions SDK 1.0.29.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Azure/Azure-Functions/issues/1138?email_source=notifications&email_token=AAEJ3XG5JYJF2WFNAYZRZJDQSFMJBA5CNFSM4G3LVUVKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDCR5NQ#issuecomment-549789366, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEJ3XGOSAXA7TGQUIGH5BDQSFMJBANCNFSM4G3LVUVA .

bragma commented 4 years ago

Here is the repository with the problem:

https://github.com/bragma/TestFunction/tree/master/TestFunction

Notice this is not the original functions, but a simplified one showing my problem with AddMvcCore in startup.

sadranyi commented 4 years ago

Checking it out

On Tue, Nov 5, 2019 at 12:08 PM Bragma notifications@github.com wrote:

Here is the repository with the problem:

https://github.com/bragma/TestFunction/tree/master/TestFunction

Notice this is not the original functions, but a simplified one showing my problem with AddMvcCore in startup.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Azure/Azure-Functions/issues/1138?email_source=notifications&email_token=AAEJ3XCAENLCYCSBOCEERATQSFO5PA5CNFSM4G3LVUVKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDCTT6Q#issuecomment-549796346, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEJ3XFIJQWQ3OAXYTCTSTLQSFO5PANCNFSM4G3LVUVA .

bragma commented 4 years ago

Checking it out

Thanks. Once I solve thins I should be able to provide a complete function with working templates.

lurumad commented 4 years ago

Hi @bragma

I've uploaded a small PoC with AddMvcCore and it's working:

https://functionrazor.azurewebsites.net/api/Function1?name=Luis

[assembly: FunctionsStartup(typeof(FunctionRazor.Startup))]
namespace FunctionRazor
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddMvcCore();
        }
    }
}
bragma commented 4 years ago

Hi @bragma

I've uploaded a small PoC with AddMvcCore and it's working:

Hi, it seems very similar to my repo, moving AddMvcCore to main Configure instead of separate function. There must be something different...

Can you confirm my original repo does not work? Did you only change line 20 from:

        ConfigureTest1(builder);

to

builder.Services.AddMvcCore();

Can you make a PR to my repo or share yours?

Thanks!

lurumad commented 4 years ago

Hi @bragma

I'm going to clone your repo and deploy to Azure ;)

lurumad commented 4 years ago

Hi @bragma

It's working

https://functionrazor.azurewebsites.net/api/test

Regards!

bragma commented 4 years ago

Hi @bragma

It's working

https://functionrazor.azurewebsites.net/api/test

Regards!

Ok, so it seems my repo works for you but not in my function app. This makes things even more tricky. What can be different?

sadranyi commented 4 years ago

Got mine working to.

On Tue, Nov 5, 2019, 3:14 PM Bragma notifications@github.com wrote:

Hi @bragma https://github.com/bragma

It's working

https://functionrazor.azurewebsites.net/api/test

Regards!

Ok, so it seems my repo works for you but not in my function app. This makes things even more tricky. What can be different?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Azure/Azure-Functions/issues/1138?email_source=notifications&email_token=AAEJ3XDPVS6OFYDXRK53RHTQSGETXA5CNFSM4G3LVUVKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDDAQ4Y#issuecomment-549849203, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEJ3XHQUTXQUVMORKRJRRDQSGETXANCNFSM4G3LVUVA .

bragma commented 4 years ago

Hi, I am glad to hear it is working for you. :) I'd like to understand why it is not working for me. There are many variables here but I think I'll need some help from someone more into function hosting environment than me.

The only thing I can say is that I am hosting my function in west Europe with runtime 2.0.12858.0

I've also managed to make it work by creating a separate ServiceCollection, but it is a bad trick.

lurumad commented 4 years ago

In my case I'm still having the same problem with compilation:

[11/5/2019 4:19:01 PM] Executed 'Function1' (Failed, Id=07b9902a-b3b3-4b51-820f-0c59c245d78b) [11/5/2019 4:19:01 PM] System.Private.CoreLib: Exception while executing function: Function1. Microsoft.AspNetCore.Mvc.Razor: One or more compilation failures occurred: [11/5/2019 4:19:01 PM] bnw0sviu.ej1(4,20): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)

Kind regards!

bragma commented 4 years ago

In my case I'm still having the same problem with compilation:

[11/5/2019 4:19:01 PM] Executed 'Function1' (Failed, Id=07b9902a-b3b3-4b51-820f-0c59c245d78b) [11/5/2019 4:19:01 PM] System.Private.CoreLib: Exception while executing function: Function1. Microsoft.AspNetCore.Mvc.Razor: One or more compilation failures occurred: [11/5/2019 4:19:01 PM] bnw0sviu.ej1(4,20): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)

Kind regards!

Hi, can you share the code you use to create the razor engine?

lurumad commented 4 years ago

I've attached my example:

FunctionRazor.zip

sadranyi commented 4 years ago

Can you try hosting in a different region just to eliminate the possibility of a region Issue.

On Tue, Nov 5, 2019 at 4:18 PM Bragma notifications@github.com wrote:

Hi, I am glad to hear it is working for you. :) I'd like to understand why it is not working for me. There are many variables here but I think I'll need some help from someone more into function hosting environment than me.

The only thing I can say is that I am hosting my function in west Europe with runtime 2.0.12858.0

I've also managed to make it work by creating a separate ServiceCollection, but it is a bad trick.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Azure/Azure-Functions/issues/1138?email_source=notifications&email_token=AAEJ3XCM7AR7HM4QIDW6J5LQSGME7A5CNFSM4G3LVUVKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDDIV6A#issuecomment-549882616, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEJ3XAXNUID22FADGBXHKDQSGME7ANCNFSM4G3LVUVA .

bragma commented 4 years ago

Can you try hosting in a different region just to eliminate the possibility of a region Issue.

I just deployed in Central US and it works. This is really annoying.

bragma commented 4 years ago

@sadranyi and @lurumad can you please try to deploy the function in West Europe as a counter test? It seems to me something fishy is happening. Also please log here your runtime version. Thanks a lot!

lurumad commented 4 years ago

Hi @bragma

Running in West Europe

https://functionrazor.azurewebsites.net/api/test

Regards!

bragma commented 4 years ago

Ok now I don't understand. Same code, I deploy in Central US -> works. I deploy in West Europe -> Not starting. You deploy in West Europe -> it works. Last chance: is your runtime 2.0.12858.0?

bragma commented 4 years ago

I've attached my example:

FunctionRazor.zip

@lurumad I've checked your project but it seems to me you are not adding all required services.

This is the list of services I am adding:

            services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
            services.AddSingleton<DiagnosticSource>(new DiagnosticListener("Microsoft.AspNetCore"));

            var fileProvider = new PhysicalFileProvider(appDirectory);
            services.AddSingleton<IHostingEnvironment>(new HostingEnvironment
            {
                WebRootFileProvider = fileProvider,
                ApplicationName = appName
            });

            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.FileProviders.Clear();
                options.FileProviders.Add(fileProvider);
            });

            services.AddLogging();

#if !NATIVE_CONTAINER
            services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
#endif
            services.AddMvcCore().AddViewLocalization(options => options.ResourcesPath = "Resources");

The problems I had were related to the directory and app name settings. As an appName I use the name of the DLL containing the views. Some examples try to get the app name from Assembly.GetEntryAssembly().GetName().Name but this returns the string "func" when running in a function app enviroment. I prefer to set it explicitly with a constant string.

As a dll directory, I use this:

Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)

This is because local environment and function app environment have different process current directories.

lurumad commented 4 years ago

Hi @bragma

Can you share a small PoC of your solution?

Regards!

bragma commented 4 years ago

Hi @bragma

Can you share a small PoC of your solution?

Regards!

Hi, sorry for the late reply. I am very busy, I'll try to share a working sample once I have more time. Probably next week.

ganhammar commented 4 years ago

I managed to figure out how to host a MVC application with Razor views. I used a IdentityServer4 sample project to test the concept. Link to sample repo and to the hosted function can be found on the link below if anyone is interested.

https://www.ganhammar.se/2019-12-25/how-to-host-a-asp-net-core-3-mvc-application-as-a-azure-function

rainerllera commented 4 years ago

Any updates on this one guys? kinda having the same issue here

elmo61 commented 4 years ago

If it helps I have had success using https://github.com/fouadmess/RazorEngine in combination with a AzureFunction v2.

There are some limitations of the razor engine but the basics work and I've used it for developing email templates which are sent from the azure function.

gyankov commented 3 years ago

I am still facing the same issue with Azure Function v3. Did someone come up with a way to make runtime compiled views work?