Required dependencies for Resolver or ServiceProvi

2020-07-17 04:59发布

问题:

I am trying to use ICompositeViewEngine in ASP.NET Core MVC for substituting ViewEngine from System.Web.Mvc since it is no longer available in .NET Core. I am generally trying to migrate a webform from ASP.NET to ASP.NET Core in this project.

I have found the following solution: Where are the ControllerContext and ViewEngines properties in MVC 6 Controller? and I believe that this may resolve my issue. I have also found a similar engine creation with ServiceProvider in a github question: https://github.com/aspnet/Mvc/issues/3091

However, I am not sure about what dependencies or frameworks I may be missing as I am very new with .NET. I have the following namespaces:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.DependencyInjection;

That I believe might be related to my issue.

My original code is:

    public static string RenderPartialToString(Controller controller, string viewName, object model)
    {
        controller.ViewData.Model = model;

        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return "document.write('" + sw.GetStringBuilder().Replace('\n', ' ').Replace('\r', ' ').Replace("'","\\'").ToString() + "');";
        }
    }

And now I am trying to use either of the following:

 var engine = Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
 var engine2 = IServiceProvider.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;

Am I on the right track to fix this? Is there an easier way to replace System.Web.Mvc ViewEngines in .NET Core? What do I need to fix "does not exist in the current context" errors for Resolver and/or ServiceProvider?

Thanks. I hope I was able to follow the question guidelines.

Edit: Please let me know if I should include anything else from my code for this question. I am currently reading about Dependency Injection to understand the situation better.

回答1:

You're mostly on the right track. ASP.NET Core got rid of many static objects so you can't do things like Resolver.GetService. Resolver doesn't exist. Instead, use the dependency injection system.

If you just need to access ICompositeViewEngine from a controller, inject it in the constructor:

public MyController(ICompositeViewEngine viewEngine)
{
    // save a reference to viewEngine
}

If you want to have a discrete service that handles Razor-to-string rendering, you'll need to register it at startup:

public void ConfigureServices(IServiceCollection services)
{
    // (Other code...)

    services.AddTransient<IViewRenderingService, ViewRenderingService>();

    services.AddMvc();
}

The service itself would look like this:

using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;

public interface IViewRenderingService
{
    string RenderPartialView(ActionContext context, string name, object model = null);
}

public class ViewRenderingService : IViewRenderingService
{
    private readonly ICompositeViewEngine _viewEngine;
    private readonly ITempDataProvider _tempDataProvider;

    public ViewRenderingService(ICompositeViewEngine viewEngine, ITempDataProvider tempDataProvider)
    {
        _viewEngine = viewEngine;
        _tempDataProvider = tempDataProvider;
    }

    public string RenderPartialView(ActionContext context, string name, object model)
    {
        var viewEngineResult = _viewEngine.FindView(context, name, false);

        if (!viewEngineResult.Success)
        {
            throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", name));
        }

        var view = viewEngineResult.View;

        using (var output = new StringWriter())
        {
            var viewContext = new ViewContext(
                context,
                view,
                new ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = model
                },
                new TempDataDictionary(
                    context.HttpContext,
                    _tempDataProvider),
                output,
                new HtmlHelperOptions());

            view.RenderAsync(viewContext).GetAwaiter().GetResult();

            return output.ToString();
        }
    }
}

To use it from a controller, inject and call it:

public class HomeController : Controller
{
    private readonly IViewRenderingService _viewRenderingService;

    public HomeController(IViewRenderingService viewRenderingService)
    {
        _viewRenderingService = viewRenderingService;
    }

    public IActionResult Index()
    {
        var result = _viewRenderingService.RenderPartialView(ControllerContext, "PartialViewName", model: null);
        // do something with the string

        return View();
    }
}

If you want to use Razor outside of MVC entirely, see this answer.