Компиляция страницы ASPX в отдельную программу

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

Я пытаюсь скомпилировать страницу 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 объявление:


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

page экземпляраControls коллекция пуста и, вероятно, не должнат быть

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

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

 Chris Ballance02 нояб. 2009 г., 15:58
Вау, никогда не думал, что выберу такой подход. повеселись
 SLaks02 нояб. 2009 г., 15:28
Теперь это победилодаже компилироватьThere is a circular dependency in the target dependency graph involving target "Build".

Ответы на вопрос(7)

Предупреждение

В итоге я скопировал файлы в выходную папку и инициализировал 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();
        }
    }
</tpage></string,>

InitDomain должен быть вызван прямо при запуске программы; в противном случае выдается исключение о том, что система конфигурации уже инициализирована.

Без вызоваProcessRequestстраницыControls Коллекция пуста.

ОБНОВИТЬ: Страница отображается во время звонкаProcessRequest, так что это должно быть сделано после манипулированияPage пример.

Этот код не будет работать, если программа имеет.config файл; Я сделал метод, чтобы установить версию компилятора C # по умолчанию без.config файл с использованием отражения.

 Leon Bambrick06 нояб. 2009 г., 01:10
Спасибо SLaks, оба комментария были полезны. Нужно ли где-нибудь добавить Global.asax? Я'Теперь у нас есть исключение NullRef в BuildManager.CreateInstanceFromVirtualPath - внутреннее исключение приводит меня к: System.Web.Compilation.ApplicationBuildProvider.GetGlobalAsaxBuildResult (Boolean isPrecompiledApp), поэтому я думаю, что я должен прекратить это беспокоиться.
 SLaks05 нояб. 2009 г., 15:56
virtualPath это абсолютный путь относительноPageDirectory, Например, еслиSomePage.aspx находится прямо вPageDirectory, так должно быть/SomePage.aspx, Если оно'S в подпапке, виртуальный путь будет./SubFolder/SomePage.aspx
 Leon Bambrick05 нояб. 2009 г., 08:28
Это отличная идея ... яЯ пытаюсь заставить то же самое работать здесь. Когда я звонюvar page = PageBuilder.CreatePage <MyPageName>(SomeVirtualPath);» Я нене знаю, какое значение использовать дляsomeVirtualPath», Я полагаю, это будет связано с именем моего файла aspx, но я могуполучить значение, которое работает.
 SLaks05 нояб. 2009 г., 15:57
Кстати, при отладке этого обязательно включите Break on All Exceptions (Debug, Exceptions), иначе вы можете столкнуться с загадочными проблемами. (HostingEnvironment.Initialize глотает исключения)
 SLaks06 нояб. 2009 г., 14:57
У меня была такая же проблема, и я сдался. Основная проблема заключается в том, чтоAppDomain.CurrentDomain.DynamicDirectory является нулевым, вызываяhttpRuntime.SetUpCodegenDirectory бросить исключение во времяInitialize который проглотилhostingInit, Из-за этого исключения среда выполнения ASP.Net неt полностью инициализируется, вызывая одно из свойств, используемыхGetGlobalAsaxBuildResult бытьnull, Чтобы увидеть это, включите Break для всех исключений и посмотрите на источникSetUpCodegenDirectory, Для альтернативного решения см. Мой второй ответ.

ET в вашем приложении?

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

Вотодин.

 SLaks02 нояб. 2009 г., 15:31
Как я объяснил в связанном вопросе, яЯ на самом деле не делаю веб-сервер. Также я'Я хотел бы избежать размещения отдельного домена приложений.

ClienBuildManager класс для компиляции ASPX-файлов.

 SLaks02 нояб. 2009 г., 15:37
AFAIK, который требует отдельного домена приложений, который яЯ хотел бы избежать.
 Pete03 нояб. 2009 г., 08:00
Я не эксперт, но, насколько мне известно, он генерирует новый домен приложений при компиляции страниц ASPX, но он возвращает объект типа, который вы можете создать в своем собственном домене приложений.
 SLaks05 нояб. 2009 г., 16:00
Да, но для того, чтобы отобразить страницу, она должна быть запущена через HttpRuntime.ProcessRequest, который требует инициализации ASP.Net tp в AppDomain, чтобы она 'S запустить

SimpleWorkerRequest.

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

Решение Вопроса

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

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

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

НОТА: WinForms ' Функция настроек не будет работать в домене приложений ASP.Net.

Есть ли способ обработать представление 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");

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

 SLaks02 нояб. 2009 г., 15:35
Я использую сгенерированный класс, в автоматически сгенерированномASP Пространство имен. (Я проверил с помощью Reflector) Я положил некоторые свойства в
 SLaks02 нояб. 2009 г., 15:37
(TheClassName приписывать@Page контролирует имя сгенерированного класса)

Ваш ответ на вопрос