.NET Core/Blazor架构中的服务层是否冗余?
问题:.NET Core服务应用是否冗余?
我现有以下项目结构:
- DAL:包含多个DbContext的C#类库
- DTOs:Blazor与服务应用共用的DTO类库
- Models:DbContext引用的模型类库
- Services.QualityAssurance:执行数据库CRUD的.NET Core应用,原本计划把数据库连接字符串放在这里
- Blazor Client:Blazor WASM前端
- Blazor Server:Blazor WASM后端,需要和Services.QualityAssurance的服务交互
当前所有功能能正常运行,可完成CRUD操作,但有以下疑问:
- 现在的.NET Core服务应用是否冗余?
- 原本想让服务层存连接字符串并在自己的Program.cs里注册DbContext,但发现必须在Blazor Server的Program.cs里注册UnitOfWork和DbContext才能把服务注入控制器,这样连接字符串就放在了Blazor端,没实现服务层作为Blazor和数据库之间抽象层的目标。
- 尝试用
IServiceScopeFactory在控制器里调用服务,移除Blazor Program.cs里的相关注册后,出现IQualityAssuranceService未注册的错误。
相关代码片段
DAL中的DbContext
public class QualityAssuranceDbContext : DbContext { public QualityAssuranceDbContext(DbContextOptions<QualityAssuranceDbContext> options) : base(options) { } public QualityAssuranceDbContext() { } public DbSet<Aspect> Aspects { get; set; } //其他实体已省略 }
.NET Core服务层的QualityAssuranceService
public class QualityAssuranceService : IQualityAssuranceService { private readonly IUnitOfWorkQA _unitOfWork; private static HttpContext _httpContext => new HttpContextAccessor().HttpContext; public QualityAssuranceService(IUnitOfWorkQA unitOfWork) { _unitOfWork = unitOfWork; } public async Task<IEnumerable<AspectDto>> GetAspects(int schoolId, bool includeDeleted = false) { var aspects = await _unitOfWork.Aspects.GetAll( expression: q => q.SchoolId == schoolId && q.Enabled == true, orderBy: o => o .OrderBy(x => x.Name)); var aspectsDto = (from aspect in aspects select new AspectDto() { Id = aspect.Id, Name = aspect.Name, SchoolId = aspect.SchoolId, }).ToList(); return aspectsDto; } //其他操作已省略 public void Dispose() { _unitOfWork.Dispose(); GC.SuppressFinalize(this); } }
Blazor Server的Program.cs配置
public partial class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); //无关代码已省略 var QualityAssuranceConnectionString = builder.Configuration.GetConnectionString("QualityAssuranceConnection"); builder.Services.AddDbContext<QualityAssuranceDbContext>(options => { options.UseSqlServer(QualityAssuranceConnectionString); options.EnableSensitiveDataLogging(true); }); builder.Services.AddTransient<IUnitOfWorkQA, UnitOfWorkQA>(); builder.Services.AddTransient<IQualityAssuranceService, QualityAssuranceService>(); var app = builder.Build(); //无关代码已省略 app.Run(); } }
控制器注入服务代码
public class QualityAssuranceController : ControllerBase { private readonly IQualityAssuranceService _qualityAssuranceService; public QualityAssuranceController(IQualityAssuranceService qualityAssuranceService) { _qualityAssuranceService = qualityAssuranceService; } [HttpGet] public async Task<IActionResult> GetAspects(bool includeDeleted = false) { var schoolId = GetSchoolId(); var aspectDtos = await _qualityAssuranceService.GetAspects(schoolId, includeDeleted); return Ok(aspectDtos); } }
解答
你的Services.QualityAssurance项目现在不是冗余的,但你用错了方式——当前是把它当成类库直接引用到Blazor Server里,而非让它作为独立的API服务对外提供接口。
正确的使用方式:
将Services.QualityAssurance做成独立Web API服务:
- 在这个API项目里注册DbContext、UnitOfWork、QualityAssuranceService,连接字符串放在该项目的配置文件中。
- 编写API控制器(逻辑可复用你现在Blazor Server里的控制器),对外暴露REST接口。
Blazor Server作为客户端调用该API:
- Blazor Server不再直接引用Services.QualityAssurance类库,而是通过HTTP请求调用API接口。
- 在Blazor Server中创建一个客户端服务类,封装对API的调用逻辑,再注入到组件或控制器中使用。
当前方式的问题:
- 直接引用服务层类库后,Blazor Server相当于直接操作数据库,服务层只是个中间类,完全没起到独立服务的作用,连接字符串自然只能放在Blazor端。
- 这种模式下,其他.NET Core应用复用服务逻辑时也得引用类库,一旦服务逻辑变更,所有引用项目都要重新编译部署,违背了独立服务的初衷。
修正步骤:
改造Services.QualityAssurance为Web API项目:
- 添加必要NuGet包(
Microsoft.AspNetCore.App、Microsoft.EntityFrameworkCore.SqlServer等)。 - 在其
Program.cs中注册依赖:var connectionString = builder.Configuration.GetConnectionString("QualityAssuranceConnection"); builder.Services.AddDbContext<QualityAssuranceDbContext>(options => options.UseSqlServer(connectionString)); builder.Services.AddTransient<IUnitOfWorkQA, UnitOfWorkQA>(); builder.Services.AddTransient<IQualityAssuranceService, QualityAssuranceService>(); builder.Services.AddControllers(); - 将Blazor Server中的
QualityAssuranceController迁移到该API项目中。
- 添加必要NuGet包(
调整Blazor Server端:
- 删除对Services.QualityAssurance类库的引用,移除Program.cs中相关的DbContext、Service注册代码。
- 创建API客户端类:
public class QualityAssuranceApiClient { private readonly HttpClient _httpClient; public QualityAssuranceApiClient(HttpClient httpClient) { _httpClient = httpClient; } public async Task<IEnumerable<AspectDto>> GetAspects(bool includeDeleted = false) { var schoolId = // 此处获取schoolId,可在Blazor Server中处理HttpContext逻辑 return await _httpClient.GetFromJsonAsync<IEnumerable<AspectDto>>( $"api/qualityassurance/getaspects?includeDeleted={includeDeleted}&schoolId={schoolId}"); } } - 在Blazor Server的Program.cs中注册该客户端:
builder.Services.AddHttpClient<QualityAssuranceApiClient>(client => { client.BaseAddress = new Uri("http://localhost:5000/"); // 替换为你的API服务地址 }); - 在控制器或组件中注入
QualityAssuranceApiClient来调用API。
调整后,Services.QualityAssurance成为独立服务,连接字符串保留在服务端,Blazor和其他.NET Core应用都通过HTTP调用它,既实现了抽象层,又能复用服务逻辑,服务变更仅需部署API项目即可。
内容的提问来源于stack exchange,提问作者Hannah Hayes
相关产品推荐
相关产品推荐

