I'm doing some exploration into trying to render html from a console application WITHOUT relying on tools like RazorEngine. I want to see if it's possible using straight mvc code; at the same time learn more about how MVC view renders work in general.
I've been following the source code here: https://github.com/mono/aspnetwebstack
Going from RazorView and working my way down the rabbit holes it seems like the buck stops at "public override void ExecutePageHierarchy()"
I'm a little confused because it seems to hinge on the abstract "Execute" function which isn't defined anywhere in the solution.
This given me nothing as to how the view data gets mixed in with the html. I was expecting to see some sort of regex that looks for @Model.Prop and does a replace with the given view data.
Looking at the UTs in the solution aren't much help either. They seem to be just defining the Write functions and checking if the render slaps it together in the right order:
[Fact]
public void RenderPageNestedSubPageAnonymousTypeTest()
{
// Test that PageData for each level of nesting returns the values as specified in the
// previous calling page.
//
// ~/index.cshtml does the following:
// @(PageData["foo"] ?? "null")
// @RenderPage("subpage.cshtml", new { foo = 1 , bar = "hello" })
//
// ~/subpage1.cshtml does the following:
// @(PageData["foo"] ?? "sub1nullfoo")
// @(PageData["bar"] ?? "sub1nullbar")
// @(PageData["x"] ?? "sub1nullx")
// @(PageData["y"] ?? "sub1nully")
// @RenderPage("subpage2.cshtml", new { bar = "world", x = "good", y = "bye"})
//
// ~/subpage2.cshtml does the following:
// @(PageData["foo"] ?? "sub2nullfoo")
// @(PageData["bar"] ?? "sub2nullbar")
// @(PageData["x"] ?? "sub2nullx")
// @(PageData["y"] ?? "sub2nully")
//
// Expected result: null 1 hello sub1nullx sub1nully sub2nullfoo world good bye
var page = Utils.CreatePage(
p =>
{
p.Write(p.PageData["foo"] ?? "null ");
p.Write(p.RenderPage("subpage1.cshtml", new { foo = 1, bar = "hello" }));
});
var subpage1Path = "~/subpage1.cshtml";
var subpage1 = Utils.CreatePage(
p =>
{
p.Write(p.PageData["foo"] ?? "sub1nullfoo");
p.Write(" ");
p.Write(p.PageData["bar"] ?? "sub1nullbar");
p.Write(" ");
p.Write(p.PageData["x"] ?? "sub1nullx");
p.Write(" ");
p.Write(p.PageData["y"] ?? "sub1nully");
p.Write(" ");
p.Write(p.RenderPage("subpage2.cshtml", new { bar = "world", x = "good", y = "bye" }));
}, subpage1Path);
var subpage2Path = "~/subpage2.cshtml";
var subpage2 = Utils.CreatePage(
p =>
{
p.Write(p.PageData["foo"] ?? "sub2nullfoo");
p.Write(" ");
p.Write(p.PageData["bar"] ?? "sub2nullbar");
p.Write(" ");
p.Write(p.PageData["x"] ?? "sub2nullx");
p.Write(" ");
p.Write(p.PageData["y"] ?? "sub2nully");
}, subpage2Path);
Utils.AssignObjectFactoriesAndDisplayModeProvider(subpage1, subpage2, page);
var result = Utils.RenderWebPage(page);
Assert.Equal("null 1 hello sub1nullx sub1nully sub2nullfoo world good bye", result);
}
Am I missing something here? Or is the model to cshtml resolution handled externally? In any case where would I be able to find that code?
What I tried initially was SUPER hacky. Since I had nothing setup in the way of HostingEnvironment or HttpRuntime (statics used by view rendering process) I was tried to manually set this up by looking at what the source code wanted and providing it with what I can. Try not to laugh to hard
System.Threading.Thread.GetDomain().SetData(".appDomain", "*");
System.Threading.Thread.GetDomain().SetData(".appVPath", "/");
FieldInfo info = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static);
object value = info.GetValue(null);
MethodInfo dynMethod = typeof(HttpRuntime).GetMethod("Init", BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(value, null);
FieldInfo info2 = typeof(HostingEnvironment).GetField("_theHostingEnvironment", BindingFlags.NonPublic | BindingFlags.Static);
info2.SetValue(null, new HostingEnvironment());
object value2 = info2.GetValue(null);
MethodInfo dynMethod2 = value2.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(x => x.GetParameters().Count() == 6 && x.Name == "Initialize");
dynMethod2.Invoke(value2, new object[] { ApplicationManager.GetApplicationManager(), null, (new ConfigMapPathFactoryMock()) as IConfigMapPathFactory, null, null, null });
var t = HostingEnvironment.VirtualPathProvider;
//HostingEnvironment.RegisterVirtualPathProvider(new DbPathProvider());
var path = @"C:\<path to mvc proj>\MyMVcProj\Views\Report\SomeReport.cshtml";
HostingEnvironment.MapPath(path);
var compType = BuildManager.GetCompiledType(path);
I ended up hitting a brick wall with the "Initialize" invoke, it gave an error saying:
System.Reflection.TargetInvocationException
HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=mscorlib
StackTrace:
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at TestProg.Program.Main(String[] args) in C:\<path to console app>\MyConsoleApp\Program.cs:line 165
Inner Exception 1:
InvalidOperationException: The configuration system has already been initialized.
Unable to pinpoint that error message in the source code, I concluded it was being thrown by some other external tool. With there being no meaningful stack trace for me go on, it could be one or many things. So I starting shifting my focus more on the nitty gritty of the
Problem I am trying to solve: I have a worker (something that executes some given task) infrastructure deployed as window services in my application that is used to handle large tasks. In my case I am trying to generate a large report and cast that html into a pdf using a tool. It would be ideal if I could somehow render the html from there, but it's challenging as it has no HttpRuntime or HostingEnvironment setup as you would have if you ran from say a web api. I'd like to try to avoid using tools like RazorEngine as this adds way too many restrictions.
Before MVC there were many ways of creating HTML, and they still exist.
For example the HtmlTable class generates HTML for <table>
elements. That documentation has links to many similar classes, so you can explore.
This question shows an example of using HtmlTable.
It's not as easy to use as Razor, but you wanted it the hard way :-)
User contributions licensed under CC BY-SA 3.0