Blazor中通过LazyAssemblyLoader动态注册路由替代@page指令
问题描述
目前我在OnNavigateAsync事件中加载包含带@page指令(如@page "/extrapage")的Razor页面程序集,运行正常。我希望以编程方式声明页面路由,不使用@page指令,而是通过Dictionary<string, string>(Key为页面完整路由,Value为页面类的全名)来注册。需求是支持动态加载页面,可通过数据库配置添加额外页面。
现有App.razor代码如下:
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@LazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync"> <Found Context="routeData"> <RouteView RouteData="@routeData" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> @code { private Dictionary<string, (string AssemblyName, string ClassName)> AssemblyNameAncClassNameByRoute = new Dictionary<string, (string AssemblyName, string ClassName)>(); private List<Assembly> LazyLoadedAssemblies = new List<Assembly>(); private async Task OnNavigateAsync(NavigationContext args) { try { if (AssemblyNameAncClassNameByRoute.TryGetValue(args.Path, out (string AssemblyName, string ClassName) tuple)) { IEnumerable<Assembly> assemblies = await AssemblyLoader.LoadAssembliesAsync(new[] { tuple.AssemblyName }); LazyLoadedAssemblies.AddRange(assemblies); RegisterRoute(args.Path, Type.GetType(tuple.ClassName)); } } catch (Exception ex) { Logger.LogError("Error: {Message}", ex.Message); } } public void RegisterRoute(string pathRoute, Type pageType) { //需要实现的代码 } }
解决方案
要实现动态注册路由,需要操作Blazor内部的路由表。以下是具体实现步骤:
首先在App.razor的顶部注入路由服务:
@inject IEnumerable<IRouter> Routers
然后实现RegisterRoute方法:
private readonly object _routeLock = new object(); public void RegisterRoute(string pathRoute, Type pageType) { // 验证页面类型是否为合法的Blazor组件 if (!typeof(ComponentBase).IsAssignableFrom(pageType)) { throw new ArgumentException("指定类型不是有效的Blazor组件", nameof(pageType)); } lock (_routeLock) { // 获取Blazor内置的RouteDataRouter实例 var routeDataRouter = Routers.OfType<RouteDataRouter>().FirstOrDefault(); if (routeDataRouter == null) { throw new InvalidOperationException("无法获取路由处理器实例"); } // 通过反射访问内部路由表 var routeTableProperty = routeDataRouter.GetType().GetProperty("RouteTable", BindingFlags.Instance | BindingFlags.NonPublic); if (routeTableProperty == null) { throw new InvalidOperationException("无法访问路由表属性"); } var routeTable = routeTableProperty.GetValue(routeDataRouter) as RouteTable; if (routeTable == null) { throw new InvalidOperationException("路由表实例为空"); } // 创建并添加新路由条目 var routeEntry = new RouteEntry( routeTemplate: pathRoute, handlerType: pageType, parameters: Array.Empty<RouteParameter>() ); routeTable.Entries.Add(routeEntry); } }
关键说明
- 线程安全:用
lock包裹路由表操作,避免并发导航时出现修改冲突。 - 类型校验:确保传入的页面类型是
ComponentBase的子类,保证是合法的Blazor页面组件。 - 反射操作:由于Blazor的路由管理组件属于内部实现,通过反射来访问路由表并添加自定义路由。
- 路由条目构造:
RouteEntry包含路由路径、页面类型和参数列表,这里参数列表为空,适配固定路径的路由场景。
注意事项
- 该实现依赖Blazor的内部细节,后续框架版本更新可能需要调整代码。
- 若需要持久化动态路由,可在应用启动时从数据库加载配置并批量注册。
内容的提问来源于stack exchange,提问作者Asier Cayón Francisco
相关产品推荐
相关产品推荐

