.NET Core 3.1 Sdk.Worker with Sdk.Web Razor templates? (What is Sdk.Web doing to allow for Razor Templates?)

0

I'm attempting to take this example that shows how to render Razor templates to string so that i can use them for rich email templates.

https://github.com/aspnet/Entropy/tree/master/samples/Mvc.RenderViewToString

I've updated code based on this repo that describes how to update the example to .NET Core 3.1

https://github.com/scottsauber/RazorHtmlEmails

I've altered it slightly because i'm trying to use a BackgroundService Worker in order to perform the render and transmission based on database changes.

Based on the answer in this question (Can a Worker Service be called and/or used inside an existing ASPNET.Core web project?) multiple process can be running at the same time. So i setup my hostbuilder like so.

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddDbContext<MyDataContext>(dbOptions =>
            {
                dbOptions.UseSqlServer(
                    hostContext.Configuration.GetConnectionString("db"),
                    sqlOptions => sqlOptions.EnableRetryOnFailure()
                );
            });
            // This is our main worker that will loop
            services.AddHostedService<Worker>();
            // Add NLog as the logging handler
            services.AddLogging(loggingBuilder =>
            {
                loggingBuilder.ClearProviders();
                loggingBuilder.SetMinimumLevel(LogLevel.Trace);
                loggingBuilder.AddNLog(hostContext.Configuration);
            });
        })
        // Add our template renderer
        .ConfigureWebHostDefaults(builder =>
        {
            builder.UseStartup<RazorStartup>();
        });
}

and my RazorBuilder like so..

public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            var factory = app.ApplicationServices.GetService<IServiceScopeFactory>();
            using (var scope = factory.CreateScope())
            {
                var confirmAccountModel = new ConfirmAccountEmailViewModel($"NOOOOO");
                var service = scope.ServiceProvider.GetService<IRazorViewToStringRenderer>();
                var result = service.RenderViewToStringAsync("/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml", confirmAccountModel).Result;
                Console.WriteLine(result);
            }

        }

And it's all.... "working"... as long as i change the target SDK from <Project Sdk="Microsoft.NET.Sdk.Worker"> to <Project Sdk="Microsoft.NET.Sdk.Web">

But when i do that my ILogger for the worker loses the ability to write to console. So obviously the Sdk.Worker is doing some setup that the Sdk.Web is not doing.

When i use Sdk.Worker i get errors about not being able to find the view. This means that Sdk.Web is obviously doing something that Sdk.Worker is not.

System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred. (Unable to find view '/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml'. The following locations were searched:
/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml)
  Source=System.Private.CoreLib
  StackTrace:
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at CompoundAlertProcessor.RazorStartup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in C:\Users\c.rice\Documents\Solutions\GIT\CompoundAlertProcessor\CompoundAlertProcessor\RazorStartup.cs:line 46
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.<UseStartup>b__2(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.<StartAsync>d__31.MoveNext()

  This exception was originally thrown at this call stack:
    CompoundAlertProcessor.Razor.Services.RazorViewToStringRenderer.FindView(Microsoft.AspNetCore.Mvc.ActionContext, string) in RazorViewToStringRenderer.cs
    CompoundAlertProcessor.Razor.Services.RazorViewToStringRenderer.RenderViewToStringAsync<TModel>(string, TModel) in RazorViewToStringRenderer.cs

Inner Exception 1:
InvalidOperationException: Unable to find view '/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml'. The following locations were searched:
/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml

I've been playing with packages and settings trying to figure out what magic Sdk.Web is doing that Sdk.Worker is not doing but i can't figure it out. I understand that the SDK loads and orchestrates a whole lot of things behinds the scenes to make your needed outcome easier to achieve, but i can't find a single thing that states what it is actually doing.

I'm worried that trying to use a worker with a base Sdk.Web will have other changes that i can't foresee right now. Is it safe to use it like this? Is there any way for me to just take the pieces i need from Sdk.Web and inject them into Sdk.Worker?

Should i just abandon using full Razor and instead try to use something like RazorLite like described here Razor email templates in .net standard 2.1 / Core 3.1 ? I'm always wary of projects that haven't been updated in multiple years.

c#
asp.net-mvc
asp.net-core
asked on Stack Overflow Oct 2, 2020 by Chris Rice

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0