我正在尝试使用“伪控制器上下文”将MVC局部视图(包含Kendo Grid)渲染为html字符串..
我想知道是否有办法为此创建合适的假控制器..所以我可以从静态方法 访问MVC局部视图(包含Kendo网格)。
对此有任何帮助表示赞赏。在此先感谢!!
我一直收到此错误:
值不能为空。 参数名称:controllerContext 描述:执行当前Web请求期间发生未处理的异常。请查看堆栈跟踪了解更多信息 有关错误的信息以及它在代码中的起源。
Exception Details: System.ArgumentNullException: Value cannot be null. Parameter name: controllerContext Source Error: Line 8: @(Html.Kendo().Grid<SimpleKendoModelData>()
堆栈追踪:
[ArgumentNullException: Value cannot be null.
Parameter name: controllerContext]
System.Web.Mvc.ChildActionValueProviderFactory.GetValueProvider(ControllerContext controllerContext) +137
System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) +80
System.Web.Mvc.ControllerBase.get_ValueProvider() +39
Kendo.Mvc.UI.Grid`1.ProcessDataSource() +254
Kendo.Mvc.UI.Grid`1.WriteHtml(HtmlTextWriter writer) +818
Kendo.Mvc.UI.WidgetBase.ToHtmlString() +102
Kendo.Mvc.UI.Fluent.WidgetBuilderBase`2.ToHtmlString() +15
System.Web.WebPages.WebPageBase.Write(Object value) +103
ASP._Page_Views_Reporting_ReportElement_SimpleKendoGrid_cshtml.Execute() in c:\Users\xxx\Documents\Visual Studio 2015\Projects\xxx\Views\Reporting\ReportElement_SimpleKendoGrid.cshtml:8
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +253
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +148
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +122
xxx.Api.Ui.Helpers.ViewRenderer.RenderViewToStringInternal(String viewPath, Object model, Boolean partial) in C:\Users\xxx\Documents\Visual Studio 2015\Projects\xxx\Helpers\ViewRenderer.cs:218
xxx.Api.Ui.Helpers.ViewRenderer.RenderPartialView(String viewPath, Object model) in C:\Users\xxx\Documents\Visual Studio 2015\Projects\xxx\Helpers\ViewRenderer.cs:81
xxx.Api.Ui.Helpers.ViewRenderer.RenderPartialView(String viewPath, Object model, ControllerContext controllerContext) in C:\Users\xxx\Documents\Visual Studio 2015\Projects\xxx\Helpers\ViewRenderer.cs:149
xxx.Api.Ui.Controllers.ReportingController.Test() in C:\Users\xxx\Documents\Visual Studio 2015\Projects\xxx\Controllers\ReportingController.cs:86
lambda_method(Closure , ControllerBase , Object[] ) +87
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +229
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +35
System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +39
System.Web.Mvc.Async.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) +67
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +72
System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +386
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +38
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +186
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +38
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +65
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +36
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +38
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +44
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +65
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +399
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +157
这是我在控制器中的测试方法:
[AllowAnonymous]
public ActionResult Test()
{
var model = new SimpleKendoModel();
for (int x = 0; x < 10; x++)
{
var result = new SimpleKendoModelData();
result.Data = x;
model.Results.Add(result);
}
var currentContext = ControllerExtensions.GetFakeControllerContext();
//This commented out code works
//var html = ViewRenderer.RenderPartialView("~/Views/Reporting/ReportElement_SimpleKendoGrid.cshtml", model, ControllerContext);
var html = ViewRenderer.RenderPartialView("~/Views/Reporting/ReportElement_SimpleKendoGrid.cshtml", model, currentContext);
return View();
}
ControllerExtensions类:
public static class ControllerExtensions
{
public static ControllerContext GetFakeControllerContext()
{
var currentContext = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
var st = new StringWriter();
var context = new HttpContextWrapper(currentContext);
var routeData = new RouteData();
routeData.Values.Add("controller", "someValue");
var controllerContext = new ControllerContext(new RequestContext(context, routeData), new FakeController());
return controllerContext;
}
}
查看渲染器类:
// Read: https://weblog.west-wind.com/posts/2012/may/30/rendering-aspnet-mvc-views-to-string
/// <summary>
/// Class that renders MVC views to a string using the
/// standard MVC View Engine to render the view.
/// </summary>
public class ViewRenderer
{
/// <summary>
/// Required Controller Context
/// </summary>
protected ControllerContext Context { get; set; }
/// <summary>
/// Initializes the ViewRenderer with a Context.
/// </summary>
/// <param name="controllerContext">
/// If you are running within the context of an ASP.NET MVC request pass in
/// the controller's context.
/// Only leave out the context if no context is otherwise available.
/// </param>
public ViewRenderer(ControllerContext controllerContext = null)
{
// Create a known controller from HttpContext if no context is passed
if (controllerContext == null)
{
if (HttpContext.Current != null)
{
var currentContext = HttpContext.Current;
var context = new HttpContextWrapper(currentContext);
var routeData = new RouteData();
controllerContext = new ControllerContext(new RequestContext(context, routeData), new FakeController());
}
else
throw new InvalidOperationException(
"ViewRenderer must run in the context of an ASP.NET " +
"Application and requires HttpContext.Current to be present.");
}
Context = controllerContext;
}
/// <summary>
/// Renders a full MVC view to a string. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to render the view with</param>
/// <returns>String of the rendered view or null on error</returns>
public string RenderView(string viewPath, object model)
{
return RenderViewToStringInternal(viewPath, model, false);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <returns>String of the rendered view or null on error</returns>
public string RenderPartialView(string viewPath, object model)
{
return RenderViewToStringInternal(viewPath, model, true);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active Controller context</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderView(string viewPath, object model,
ControllerContext controllerContext)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderView(viewPath, model);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active Controller context</param>
/// <param name="errorMessage">optional out parameter that captures an error message instead of throwing</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderView(string viewPath, object model,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderView(viewPath, model);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
return null;
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active controller context</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderPartialView(string viewPath, object model,
ControllerContext controllerContext)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderPartialView(viewPath, model);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active controller context</param>
/// <param name="errorMessage">optional output parameter to receive an error message on failure</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderPartialView(string viewPath, object model,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderPartialView(viewPath, model);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
return null;
}
/// <summary>
/// Internal method that handles rendering of either partial or
/// or full views.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">Model to render the view with</param>
/// <param name="partial">Determines whether to render a full or partial view</param>
/// <returns>String of the rendered view</returns>
protected string RenderViewToStringInternal(string viewPath, object model,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException("ViewCouldNotBeFound");
// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
/// <summary>
/// Creates an instance of an MVC controller from scratch
/// when no existing ControllerContext is present
/// </summary>
/// <typeparam name="T">Type of the controller to create</typeparam>
/// <returns></returns>
public static T CreateController<T>(RouteData routeData = null)
where T : Controller, new()
{
T controller = new T();
// Create an MVC Controller Context
HttpContextBase wrapper = null;
if (HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
//else
// wrapper = CreateHttpContextBase(writer);
if (routeData == null)
routeData = new RouteData();
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
}
〜/ Views / Reporting / ReportElement_SimpleKendoGrid.cshtml局部视图:
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_ReportTemplateLayout.cshtml";
}
@model SimpleKendoModel
@(Html.Kendo().Grid<SimpleKendoModelData>()
.Name("Results")
.BindTo(Model.Results)
.Columns(columns =>
{
columns.Bound(x => x.Data).Title("Data").Width(250);
})
.DataSource(datasource => datasource
.Ajax()
.PageSize(5000)
.ServerOperation(false)
)
)
〜/查看/共享/ _ReportTemplateLayout.cshtml:
@RenderBody()
MVC部分视图模型:
public class SimpleKendoModel
{
public SimpleKendoModel()
{
Results = new List<SimpleKendoModelData>();
}
public List<SimpleKendoModelData> Results { get; set;}
}
public class SimpleKendoModelData
{
public SimpleKendoModelData()
{
Data = 0;
}
public int Data { get; set; }
}
答案 0 :(得分:0)
感谢此帖:
Setting HttpContext.Current.Session in a unit test
我实际上已经解决了这个问题..
使用下面的类,我能够使用MVC Razor局部视图(包含Kendo网格)成功创建模板HTML以用于电子邮件模板..并允许我从静态方法调用它。
我创建了一个名为 ControllerExtensions
的类public static class ControllerExtensions
{
public static ControllerContext GetFakeControllerContext()
{
HttpContext.Current = HttpContext.Current ?? GetFakeHttpContext();
var currentContext = HttpContext.Current;
var controller = CreateController<FakeController>();
var st = new StringWriter();
var context = new HttpContextWrapper(currentContext);
var routeData = GetFakeRouteData(controller);
var controllerContext = new ControllerContext(new RequestContext(context, routeData), controller);
return controllerContext;
}
public static HttpContext GetFakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://stackoverflow/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
public static RouteData GetFakeRouteData(Controller controller)
{
var routeData = new RouteData();
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
return routeData;
}
public static T CreateController<T>(RouteData routeData = null)
where T : Controller, new()
{
T controller = new T();
// Create an MVC Controller Context
var wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
if (routeData == null)
routeData = new RouteData();
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
}
public class FakeController : Controller
{
}
并按以下方式使用:
public static string GetHtml()
{
var model = new SimpleKendoModel();
for (int x = 0; x < 10; x++)
{
var result = new SimpleKendoModelData();
result.Data = x;
model.Results.Add(result);
}
var currentContext = ControllerExtensions.GetFakeControllerContext();
var html = ViewRenderer.RenderPartialView("~/Views/Reporting/ReportElement_SimpleKendoGrid.cshtml", model, currentContext);
return html;
}