Compilare una pagina ASPX in un programma standalone

Come ho menzionato qui , sto provando a generare HTML da una pagina ASPX all’interno di un WinForms.

Sto cercando di compilare la pagina ASPX direttamente nel file EXE; Mi piacerebbe essere in grado di scrivere qualcosa del genere:

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

Ho aggiunto una pagina ASPX, impostato l’azione Build per compilare e inserito la seguente dichiarazione di Page :

  

Il codice viene compilato e le proprietà che ho definito nell’ASPX sono utilizzabili dal codice chiamante, ma StringWriter rimane vuoto. Ho provato a chiamare htmlWriter.Flush e non è stato d’aiuto.

La raccolta Controls dell’istanza della page è vuota e probabilmente non dovrebbe esserlo.
Ho guardato l’EXE in Reflector e non sono riuscito a trovare il contenuto della pagina da nessuna parte. Suppongo quindi che la pagina non venga compilata correttamente.

Qual è il modo corretto per farlo?

Ho finito per utilizzare ApplicationHost.CreateApplicationHost per eseguire l’intera applicazione nell’AppDomain di ASP.Net. Questo è molto più semplice e più affidabile del mio tentativo di simulare AppDomain ASP.Net.

Nota: per fare ciò, è necessario inserire una copia del file EXE (o qualunque assembly contenga il tipo passato a CreateApplicationHost) nella directory Bin della cartella ASP.Net. Questo può essere fatto in una fase post-costruzione. È quindi ansible gestire AssemblyResolve per individuare altri assembly nella directory originale.

In alternativa, è ansible posizionare il programma stesso e tutte le DLL nella directory Bin di ASP.Net.

NOTA : la funzione Impostazioni di WinForms non funzionerà in un AppDomain ASP.Net.

Credo che quello che vuoi usare sia SimpleWorkerRequest .

Sfortunatamente, tuttavia, richiede che la risorsa (credo) viva su disco. Dalla tua descrizione sembra che tu preferisca che l’intera app risieda nella tua DLL. Se questo è il caso, è molto probabile che tu debba implementare la tua HttpWorkerRequest .

avvertimento

Questo non funziona in modo affidabile, e ho rinunciato a questo.


Ho finito per copiare i file nella cartella di output e inizializzare ASP.Net nello stesso AppDomain, usando il seguente codice: (l’ho testato, a volte funziona)

 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)typeof(CompilerInfo).GetProperty("ProviderOptions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(info, null); options["CompilerVersion"] = version; } public static TPage CreatePage(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 deve essere chiamato a destra all’avvio del programma; in caso contrario, genera un’eccezione sul sistema di configurazione già inizializzato.

Senza la chiamata a ProcessRequest , l’insieme di Controls della pagina è vuoto.


AGGIORNAMENTO : la pagina viene visualizzata durante la chiamata a ProcessRequest , quindi deve essere eseguita dopo aver modificato l’istanza di Page .

Questo codice non funzionerà se il programma ha un file .config ; Ho creato un metodo per impostare la versione del compilatore C # predefinita senza un file .config usando reflection.

Perché non guardi solo ad ospitare il runtime ASP.NET nella tua app?

Ci sono diversi frammenti online per mostrarti come.

Eccone uno

Molto probabilmente stai usando una class di pagine sbagliata. È necessario utilizzare non la vera class di codice nel codice dietro. Durante la compilazione ASP.NET genera una class di pagina, che eredita dalla class definita nel codice sottostante e all’interno di questa class avviene l’inizializzazione di tutti i controlli. Quindi dovresti usare la class generata (controlla il suo nome usando Reflector).

Se stai cercando la versione MVC di questa risposta, vedi: C’è un modo per elaborare una vista MVC (file aspx) da un’applicazione non web?

Il codice utilizza un AppDomain separato, ma per quanto posso dire, è necessario poiché tutto il codice generato da un file ASPX dipende da HttpContext e 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); } } 

Quindi, per eseguire il rendering di un file:

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

è ansible utilizzare la class ClienBuildManager per compilare i file ASPX.