将ASPX页面编译为独立程序

时间:2009-11-02 14:22:51

标签: c# .net asp.net

正如我提到here,我正在尝试从WinForms中的ASPX页面生成HTML。

我正在尝试将ASPX页面直接编译到EXE中;我希望能够写出这样的东西:

var page = new ASP.MyPageName();
var stringWriter = new StringWriter();
using(var htmlWriter = new HtmlTextWriter(stringWriter))
    page.RenderControl(htmlWriter);

我添加了一个ASPX页面,将Build Action设置为Compile,并输入以下Page声明:

<%@ Page Language="C#" ClassName="MyPageName" %>

代码编译,我在ASPX中定义的属性可以从调用代码中使用,但StringWriter仍然为空。我试着打电话给htmlWriter.Flush,但没有帮助。

page实例的Controls集合为空,可能不应该是 我查看了Reflector中的EXE,我无法在任何地方找到页面内容。因此,我假设页面未正确编译。

这样做的正确方法是什么?

7 个答案:

答案 0 :(得分:3)

我最终使用ApplicationHost.CreateApplicationHost在ASP.Net AppDomain中运行整个应用程序。这比我伪造ASP.Net AppDomain更简单,更可靠。

注意:为此,您必须在ASP.Net文件夹的Bin目录中放置EXE文件的副本(或包含传递给CreateApplicationHost的类型的任何程序集)。这可以在构建后步骤中完成。然后,您可以处理AssemblyResolve以在原始目录中找到其他程序集。

或者,您可以将程序本身和所有DLL放在ASP.Net的Bin目录中。

注意:WinForms的“设置”功能在ASP.Net AppDomain中不起作用。

答案 1 :(得分:2)

我相信你想要使用的是SimpleWorkerRequest

不幸的是,它需要资源(我相信)存在于磁盘上。从您的描述中听起来您更喜欢整个应用程序驻留在您的DLL中。如果是这种情况,您很可能需要实施自己的HttpWorkerRequest

答案 2 :(得分:2)

警告

这不能可靠地工作,我已经放弃了。


我最终将文件复制到输出文件夹并在同一个AppDomain中初始化ASP.Net,使用以下代码:(我测试了它;它有时可以工作)

static class PageBuilder {
    public static readonly string PageDirectory = Path.Combine(Path.GetDirectoryName(typeof(PageBuilder).Assembly.Location), "EmailPages");

    static bool inited;
    public static void InitDomain() {
        if (inited) return;
        var domain = AppDomain.CurrentDomain;

        domain.SetData(".appDomain", "*");
        domain.SetData(".appPath", PageDirectory);
        domain.SetData(".appVPath", "/");
        domain.SetData(".domainId", "MyProduct Domain");
        domain.SetData(".appId", "MyProduct App");
        domain.SetData(".hostingVirtualPath", "/");

        var hostEnv = new HostingEnvironment();//The ctor registers the instance

        //Ordinarily, the following method is called from app manager right after app domain (and hosting env) is created
        //Since CreateAppDomainWithHostingEnvironment is never called here, I need to call Initialize myself.
        //Here is the signaature of the method.
        //internal void Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters) { 

        var cmp = Activator.CreateInstance(typeof(HttpRuntime).Assembly.GetType("System.Web.Hosting.SimpleConfigMapPathFactory"));
        typeof(HostingEnvironment).GetMethod("Initialize", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(hostEnv, new[] { ApplicationManager.GetApplicationManager(), null, cmp, null });

        //This must be done after initializing the HostingEnvironment or it will initialize the config system.
        SetDefaultCompilerVersion("v3.5");

        inited = true;
    }

    static void SetDefaultCompilerVersion(string version) {
        var info = CodeDomProvider.GetCompilerInfo("c#");
        var options = (IDictionary<string, string>)typeof(CompilerInfo).GetProperty("ProviderOptions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(info, null);

        options["CompilerVersion"] = version;
    }


    public static TPage CreatePage<TPage>(string virtualPath) where TPage : Page {
        return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(TPage)) as TPage;
    }

//In a base class that inherits Page:

    internal string RenderPage() {
        var request = new SimpleWorkerRequest("", null, null);

        ProcessRequest(new HttpContext(request));
        using (var writer = new StringWriter(CultureInfo.InvariantCulture)) {
            using (var htmlWriter = new HtmlTextWriter(writer))
                RenderControl(htmlWriter);
            return writer.ToString();
        }
    }
程序启动时必须正确调用

InitDomain;否则,它会抛出有关已初始化的配置系统的异常。

如果不调用ProcessRequest,则网页的Controls集合为空。


更新:在调用ProcessRequest期间呈现页面,因此必须在操作Page实例后完成。

如果程序有.config文件,则此代码无效;我创建了一个方法来设置默认的C#编译器版本而没有使用反射的.config文件。

答案 3 :(得分:1)

为什么不考虑在应用程序中托管ASP.NET运行时?

网上有几个片段向您展示如何。

这是one

答案 4 :(得分:1)

您很可能使用了错误的网页类。你需要在代码背后使用不是实际的名字好的类。在编译期间,ASP.NET生成页面类,它继承自代码中定义的类,并且在此类中发生所有控件的初始化。因此,您应该使用生成的类(使用Reflector检查其名称)。

答案 5 :(得分:1)

如果您正在寻找此答案的MVC版本,请参阅: Is there a way to process an MVC view (aspx file) from a non-web application?

代码使用单独的AppDomain,但据我所知,这是必需的,因为从ASPX文件生成的所有代码都依赖于HttpContext和HostingEnvironment.VirtualPathProvider。

public class AspHost : MarshalByRefObject
{
    public string _VirtualDir;
    public string _PhysicalDir;

    public string AspxToString(string aspx)
    {
        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            using (HtmlTextWriter tw = new HtmlTextWriter(sw))
            {
                var workerRequest = new SimpleWorkerRequest(aspx, "", tw);
                HttpContext.Current = new HttpContext(workerRequest);

                object view = BuildManager.CreateInstanceFromVirtualPath(aspx, typeof(object));

                Page viewPage = view as Page;
                if (viewPage == null)
                {
                    UserControl viewUserControl = view as UserControl;
                    if (viewUserControl != null)
                    {
                        viewPage = new Page();
                        viewPage.Controls.Add(viewUserControl);
                    }
                }

                if (viewPage != null)
                {
                    HttpContext.Current.Server.Execute(viewPage, tw, true);

                    return sb.ToString();
                }

                throw new InvalidOperationException();
            }
        }
    }

    public static AspHost SetupFakeHttpContext(string physicalDir, string virtualDir)
    {
        return (AspHost)ApplicationHost.CreateApplicationHost(
            typeof(AspHost), virtualDir, physicalDir);
    }
}

然后,渲染文件:

var host = AspHost.SetupFakeHttpContext("Path/To/Your/AspNetApplication", "/");
String rendered = host.AspxToString("~/Views/MyView.aspx");

答案 6 :(得分:0)

您可以使用ClienBuildManager类来编译ASPX文件。