Render Razor View as String

The following demonstrates rendering a Razor view in a Hosted Background service of an ASP .Net 8 API using the Default HttpContext. Even though it's an API project, the Background service still doesn't have "access" to a live request, so there's no HttpContext available.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace API.Services
    public class ViewRenderService
        private readonly IRazorViewEngine razorViewEngine;
        private readonly ITempDataProvider tempDataProvider;
        private readonly IServiceProvider serviceProvider;

        public ViewRenderService(IRazorViewEngine razorViewEngine,
                                ITempDataProvider tempDataProvider,
                                IServiceProvider serviceProvider)
            this.razorViewEngine = razorViewEngine;
            this.tempDataProvider = tempDataProvider;
            this.serviceProvider = serviceProvider;

        public async Task RenderToStringAsync(string viewName, object model)
            return await RenderToStringAsync(viewName, model, new ModelStateDictionary());

        public async Task RenderToStringAsync(string viewName, object model, ModelStateDictionary modelState)
            var httpContext = new DefaultHttpContext();
            httpContext.RequestServices = serviceProvider;

            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

            using (var sw = new StringWriter())
                var viewResult = FindView(actionContext, viewName);

                if (viewResult == null)
                    throw new ArgumentNullException($"View {viewName} not found.");

                var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                    Model = model

                var viewContext = new ViewContext(
                    new TempDataDictionary(actionContext.HttpContext, tempDataProvider),
                    new HtmlHelperOptions()

                await viewResult.RenderAsync(viewContext);
                return sw.ToString();

        private IView FindView(ActionContext actionContext, string viewName)
            var getViewResult = razorViewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: false);
            if (getViewResult.Success)
                return getViewResult.View;

            var findViewResult = razorViewEngine.FindView(actionContext, viewName, isMainPage: false);
            if (findViewResult.Success)
                return findViewResult.View;

            var searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations);
            var errorMessage = string.Join(
                new[] { $"Unable to find view '{viewName}'. The following locations were searched:" }.Concat(searchedLocations));

            throw new InvalidOperationException(errorMessage);