Dynamically load ASP.NET Page from DLL

2019-07-15 09:53发布

问题:

I want to create a modular ASP.NET application. Something like, I have a main application that is just some kind of module loader. It only have the one "Default.aspx" page. And, based on the loaded modules, this page will create a menu and links to the pages found in the modules.

I want the modules to be ASP.NET projects packed into dll. So, I want to drop that dll into the "Modules" folder of my main application, it will identify the module, and use reflection to load the modules, inspect them to find the pages, and build a menu from that.

What I've done so far:

In my solution, I have a "DummyModule" project. This project have only 3 pages. Nothing special about it.

And I have another project called "MainApp". Here is the "big deal".

In this project I have a "ModuleLoader" class. When the "LoadModules" method is called, it search for "dll" files in the "Modules" folders of my application. And, using reflection, load these modules. Foreach of these modules, still using reflection, it searches all "Page" types, and stores the names into a list.

On the "Page_Load" method of the "Default.aspx" page, it call de "ModuleLoader" class, gets all modules names and all pages names for each module, and build a menu from that. I created a hyperlink pattern, that have all the information I need to load the right page. That is : "/ModuleName/PageName". I'm not using the "aspx" extension. OK, so far, so good.

Here is the tricky part.

I've created a HTTPModule called "PageLoaderModule". This modules intercepts all requests, so I can read the URL to identify wich page from wich module I have to load.

And that's exactly what I cannot do and I have no idea how to solve this.

What I'm doing:

public class PageLoaderModule : IHttpModule
{
    #region IHttpModule Members

    public void Dispose()
    {
        //clean-up code here.
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += context_BeginRequest;
    }

    private void context_BeginRequest(object sender, EventArgs e)
    {
        var application = (HttpApplication)sender;

        if (Regex.IsMatch(application.Request.RawUrl, @"/.+/.+"))
        {
            var parts = application.Request.RawUrl.Split('/').Where(u => !string.IsNullOrWhiteSpace(u)).ToList();

            IHttpHandler page = ModuleManager.GetPage(parts[0], parts[1]);

            page.ProcessRequest(application.Context);
        }
    }

    #endregion IHttpModule Members
}

The "GetPage" method, find the correct "Page" type in the specified assembly, create an instance and return the that Page instance.

But when I call the "ProcessRequest" method of the IHTTPHandler interface, it doesn't load the page.

It's possible to do that? Any thoughts?

Edit:

I've tried @Slavo suggestion.

While searching for an anwser, I've found and tried a similar solution, implementing my own VirtualPathProvider and VirtualFile. It almost worked. The virtual path handle and load the correct page but, when the page is loaded, I got the following error in my browser:

Parser Error Message: Could not load type 'DummyModule.Pages.DummyPage3'.
Source Error: 
Line 1: <% @ Page Language="C#" AutoEventWireup="true" CodeBehind="DummyPage3.aspx.cs" Inherits="DummyModule.Pages.DummyPage3" %>

So, I don't know if I've done something wrong, or this isn't the solution I'm looking for. So, I tried other option.

I correctly marked the "Build Action" of the ".aspx" file as "Embedded Resource", so it can be accessible as a virtual path. But I still got the error above.

回答1:

This looks like a case where you would want to write a VirtualPathProvider. This class lets you control the logic, which provides components to the compilation system.

When ASP.NET compiles a page to handle the request, by default it only uses the ASPX file and the code-behind. If you write a custom VirtualPathProvider, you will be able to tell it to do otherwise. So whenever ASP.NET needs to compile a page for a particular path to handle the request, your provider can extract it from an assembly.

Here is a helpful article: http://support.microsoft.com/kb/910441



回答2:

You should handle the PostMapRequestHandler event in your module and set a custom IHttpHandler to the application.Current.Handler property. Here is an example.