标准化Blazor启动流程:类库中使用Application Builder的疑问
我们用Blazor开发软件系统,同时想复用通用组件和页面开发桌面应用。目前Web服务器启动代码都是复制粘贴的,很不规范。我尝试用Application Builder标准化启动流程,核心通过继承实现通用Builder的定制来满足客户化需求,示例代码如下:
// Common implementation class CommonAppBuilder { public virtual Application CreateApplication() { var builder = MauiAppBuilder.CreateBuilder(); // Or some other builder for Blazor Server for example... // Configuration of something like a MauiAppBuilder or IConfiguration or our IServiceCollection is done in other methods of this builder that will be invoked at some point in here } // More stuff ... } // Application A implementation (application specific, still a standard application) class ApplicationAAppBuilder : CommonAppBuilder { public override Application CreateApplication() { var builder = base.CreateApplication(); // Here I want to be able to add or intercept (simply override and differ from) the common, standard implementation of configuring and settings up our startup } // Maybe more overrides if need be... } // Customized application A implementation (application and customer specific) class CustomApplicationAAppBuilder : ApplicationAAppBuilder { public override Application CreateApplication() { var builder = base.CreateApplication(); // Same story, I want to be able to inherit and override the respective standard, so I can replace standard parts with parts that need to be customized or simply add more... } } // Main(string[]) class Program { public static void Main(string[] args) { // Everything about the applications configuration takes place in my application builder, so it can be standartized and override if need be new CustomApplicationAAppBuilder(args) .CreateApplication() .Run(); } }
现在已经实现了这个Builder,但项目结构冗余:要分别维护仅含Builder的MAUI Blazor项目、Blazor Server项目,存放通用扩展方法的常规C#项目,还有实际的Blazor Server和MAUI Blazor Desktop项目。现提出三个疑问:
- 是否可通过NuGet包将MauiAppBuilder和WebAppBuilder添加到常规Razor类库,合并三个项目?
- 这种Application Builder是否会引发问题?
- 是否有更优方案实现需求?
1. 能否通过NuGet合并项目?
可以。常规Razor类库可以通过引用对应的NuGet包引入MauiAppBuilder和WebAppBuilder:
- 引入
Microsoft.AspNetCore.Components.Web包可获取Blazor Server相关的WebApplicationBuilder(需.NET 6+的WebApplication模型) - 引入
Microsoft.Maui和Microsoft.Maui.Controls包可获取MauiAppBuilder
注意事项:
- 配置类库的多目标框架,确保兼容
net6.0/net7.0/net8.0及对应的MAUI目标框架(如net6.0-android、net6.0-ios等) - 仅引入启动相关的基础包,避免不必要的UI组件依赖,减少类库体积
这样就能把通用Builder、扩展方法统一放到同一个Razor类库中,消除项目冗余。
2. 这种Application Builder会引发问题吗?
基于继承的Builder模式本身可行,但要警惕几个潜在风险:
- 继承层级过深:如果后续定制层级持续增加,会导致逻辑追踪困难,调试和维护成本上升
- 基类变更影响范围广:
CommonAppBuilder的核心逻辑修改会波及所有子类,容易引发意外问题 - 职责混杂:如果Builder中同时包含配置、服务注册、中间件设置等多种逻辑,会违反单一职责原则,导致类过于臃肿
只要控制继承层级(最多2-3层),并将不同职责拆分到独立的虚方法(如ConfigureServices、ConfigureMiddleware)而非仅重载CreateApplication,就能规避大部分问题。
3. 更优方案有哪些?
方案一:基于组合的Builder模式
放弃继承,改用组合方式封装通用配置,通过扩展方法或委托注入定制逻辑:
public class CommonAppBuilder { private readonly List<Action<IServiceCollection>> _serviceConfigurators = new(); private readonly List<Action<IApplicationBuilder>> _middlewareConfigurators = new(); public CommonAppBuilder AddServices(Action<IServiceCollection> configure) { _serviceConfigurators.Add(configure); return this; } public CommonAppBuilder AddMiddleware(Action<IApplicationBuilder> configure) { _middlewareConfigurators.Add(configure); return this; } public Application CreateApplication() { var builder = MauiAppBuilder.CreateBuilder(); // 通用服务配置 builder.Services.AddCommonServices(); // 执行自定义服务配置 foreach (var configure in _serviceConfigurators) { configure(builder.Services); } // 通用中间件配置 var app = builder.Build(); app.UseCommonMiddleware(); // 执行自定义中间件配置 foreach (var configure in _middlewareConfigurators) { configure(app); } return app; } } // 使用方式 new CommonAppBuilder() .AddServices(services => services.AddApplicationASpecificServices()) .AddMiddleware(app => app.UseApplicationASpecificMiddleware()) .CreateApplication() .Run();
这种方式更灵活,避免了继承的层级问题,定制逻辑更清晰。
方案二:宿主构建器扩展方法
直接针对WebApplicationBuilder和MauiAppBuilder编写通用扩展方法,封装通用配置:
public static class AppBuilderExtensions { public static WebApplicationBuilder ConfigureCommonBlazorServer(this WebApplicationBuilder builder) { // 通用服务配置 builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddCommonServices(); return builder; } public static MauiAppBuilder ConfigureCommonMauiBlazor(this MauiAppBuilder builder) { builder.Services.AddMauiBlazorWebView(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); #endif builder.Services.AddCommonServices(); return builder; } public static IServiceCollection AddCommonServices(this IServiceCollection services) { // 通用服务注册 services.AddSingleton<IMyService, MyService>(); return services; } }
项目中直接使用:
// Blazor Server项目 var builder = WebApplication.CreateBuilder(args); builder.ConfigureCommonBlazorServer(); // 项目特定配置 builder.Services.AddApplicationAServices(); var app = builder.Build(); app.UseCommonMiddleware(); // 项目特定中间件 app.Run(); // MAUI Blazor项目 var builder = MauiApp.CreateBuilder(args); builder.ConfigureCommonMauiBlazor(); // 项目特定配置 builder.Services.AddApplicationAServices(); var app = builder.Build(); app.Run();
这种方式轻量化,直接复用官方Builder模型,没有额外抽象层,学习和维护成本低。
方案三:自定义项目模板
用.NET模板引擎创建团队内部的自定义项目模板,把通用启动代码、项目结构封装进去,新项目通过dotnet new命令快速生成,彻底避免复制粘贴代码的问题。
内容的提问来源于stack exchange,提问作者Nico1395

