Компиляция страницы ASPX в отдельную программу - PullRequest
3 голосов
/ 02 ноября 2009

Как я уже упоминал здесь , я пытаюсь сгенерировать HTML со страницы ASPX внутри WinForms.

Я пытаюсь скомпилировать страницу ASPX непосредственно в EXE; Я хотел бы иметь возможность написать что-то вроде этого:

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

Я добавил страницу ASPX, установил для параметра «Действие при компиляции» значение «Page»:

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

Код компилируется, а свойства, которые я определил в ASPX, можно использовать из вызывающего кода, но StringWriter остается пустым. Я пытался позвонить htmlWriter.Flush, и это не помогло.

Коллекция Controls экземпляра page пуста и, вероятно, не должна быть.
Я посмотрел на EXE в Reflector и нигде не смог найти содержимое страницы. Поэтому я предполагаю, что страница не компилируется должным образом.

Как правильно это сделать?

Ответы [ 7 ]

3 голосов
/ 06 ноября 2009

В итоге я использовал ApplicationHost.CreateApplicationHost для запуска всего приложения в ASP.Net AppDomain. Это гораздо проще и надежнее, чем моя попытка подделать домен приложений ASP.Net.

Примечание. Чтобы сделать это, вы должны поместить копию вашего EXE-файла (или любой сборки, содержащей тип, переданный в CreateApplicationHost) в каталог Bin вашей папки ASP.Net. Это можно сделать на этапе после сборки. Затем вы можете обработать AssemblyResolve, чтобы найти другие сборки в исходном каталоге.

Кроме того, вы можете поместить саму программу и все библиотеки DLL в каталог Bin ASP.Net.

ПРИМЕЧАНИЕ : функция настроек WinForms не будет работать в домене приложений ASP.Net.

2 голосов
/ 04 ноября 2009

Внимание

Это не работает надежно, и я разочаровался в этом.


В итоге я скопировал файлы в выходную папку и инициализировал ASP.Net в том же AppDomain, используя следующий код: (Я проверял это; иногда это работает)

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, используя отражение.

2 голосов
/ 02 ноября 2009

Я считаю, что вы хотите использовать SimpleWorkerRequest .

К сожалению, однако, он требует, чтобы ресурс (как мне кажется) находился на диске. Из вашего описания звучит так, как будто вы предпочитаете, чтобы все приложение находилось в вашей DLL. В этом случае вам, скорее всего, потребуется реализовать собственный HttpWorkerRequest .

1 голос
/ 22 сентября 2010

Если вы ищете MVC-версию этого ответа, см .: Есть ли способ обработать представление MVC (файл aspx) из не веб-приложения?

Код использует отдельный домен 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");
1 голос
/ 02 ноября 2009

Скорее всего, вы используете неправильный класс страницы. Вы должны использовать не фактический класс с хорошим именем в коде позади. Во время компиляции ASP.NET генерирует класс страницы, который наследуется от класса, определенного в коде, и внутри этого класса происходит инициализация всех элементов управления. Поэтому вы должны использовать сгенерированный класс (проверьте его имя с помощью Reflector).

1 голос
/ 02 ноября 2009

Почему бы вам не взглянуть на хостинг среды исполнения ASP.NET в вашем приложении?

В Интернете есть несколько фрагментов, чтобы показать вам, как это сделать.

Вот один .

0 голосов
/ 02 ноября 2009

вы можете использовать класс ClienBuildManager для компиляции файлов ASPX.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...