如何根据路由从多个Razor Class Library DLL中选择Razor Pages?
Great question! Your setup makes total sense—separating RCLs for UI variants while keeping PageModels shared is a clean approach. To route to the correct RCL's views based on your URL pattern, you'll need to customize Razor's view location logic using an IViewLocationExpander—here's how to pull it off:
Step 1: Set Up Your Shared PageModel RCL
First, confirm your shared PageModel library (let's call it SharedPageModels) is referenced by all three MyFeatureX.dll RCLs. Each .cshtml file in your feature RCLs should inherit from the corresponding shared PageModel, e.g.:
@model SharedPageModels.Feature1PageModel @{ ViewData["Title"] = "Feature 1 - California"; }
Make sure each feature RCL's .csproj includes this setting to enable Razor MVC support:
<PropertyGroup> <AddRazorSupportForMvc>true</AddRazorSupportForMvc> </PropertyGroup>
Step 2: Implement a Custom IViewLocationExpander
This interface lets you modify Razor's view search paths dynamically based on route data. We'll use it to inject the correct feature RCL's assembly name into the view lookup:
using Microsoft.AspNetCore.Mvc.Razor; public class FeatureViewLocationExpander : IViewLocationExpander { private const string FeatureRouteKey = "feature"; // Add custom view locations pointing to the target RCL public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { if (!context.ActionContext.RouteData.Values.TryGetValue(FeatureRouteKey, out var featureValue) || featureValue is not string featureName) { return viewLocations; } // Map route "feature1" → "MyFeature1" assembly var targetAssembly = $"MyFeature{featureName.Replace("feature", "")}"; // Define view paths that prioritize the target RCL var featureSpecificLocations = new[] { $"/Pages/{{1}}/{{0}}.cshtml, {targetAssembly}", $"/Pages/{{0}}.cshtml, {targetAssembly}", $"/Views/Shared/{{0}}.cshtml, {targetAssembly}" }; // Add custom paths BEFORE default locations to prioritize them return featureSpecificLocations.Concat(viewLocations); } // Store route data for consistent lookup across requests public void PopulateValues(ViewLocationExpanderContext context) { if (context.ActionContext.RouteData.Values.TryGetValue(FeatureRouteKey, out var featureValue)) { context.Values[FeatureRouteKey] = featureValue.ToString(); } } }
Step 3: Register the Expander and Configure Routes
In your main web project's Program.cs, register the expander and set up routes that pass the feature parameter to the expander:
var builder = WebApplication.CreateBuilder(args); // Add services to the container builder.Services.AddRazorPages() .AddRazorPagesOptions(options => { // Register the custom view location expander options.ViewLocationExpanders.Add(new FeatureViewLocationExpander()); // Map your URL patterns to the corresponding feature pages options.Conventions.MapPageRoute( name: "CaliforniaFeature1", template: "/california/feature1/{page?}", page: "/Pages/Feature1/Index" // Matches the page structure in your RCLs ); options.Conventions.MapPageRoute( name: "CaliforniaFeature2", template: "/california/feature2/{page?}", page: "/Pages/Feature2/Index" ); options.Conventions.MapPageRoute( name: "CaliforniaFeature3", template: "/california/feature3/{page?}", page: "/Pages/Feature3/Index" ); }); var app = builder.Build(); // Configure middleware if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
How It Works
When a request comes in for /california/feature1/, the route system extracts feature1 as the route parameter. The FeatureViewLocationExpander then adds view paths pointing to MyFeature1.dll to the top of Razor's search list. Razor will first look for the view in MyFeature1.dll before falling back to default locations—ensuring you get the correct UI variant.
Alternative: Dynamic Route Matching
If you want to avoid hardcoding each feature route, you can use a dynamic route template like:
options.Conventions.MapPageRoute( template: "/california/{feature}/{page?}", page: "/Pages/{feature}/Index" );
This will automatically match any /california/featureX/ URL to the corresponding page in MyFeatureX.dll, as long as your RCLs' page structures mirror the route pattern.
内容的提问来源于stack exchange,提问作者wonderful world

