You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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操作,但有以下疑问:

  1. 现在的.NET Core服务应用是否冗余?
  2. 原本想让服务层存连接字符串并在自己的Program.cs里注册DbContext,但发现必须在Blazor Server的Program.cs里注册UnitOfWork和DbContext才能把服务注入控制器,这样连接字符串就放在了Blazor端,没实现服务层作为Blazor和数据库之间抽象层的目标。
  3. 尝试用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服务对外提供接口。

正确的使用方式:

  1. 将Services.QualityAssurance做成独立Web API服务:

    • 在这个API项目里注册DbContext、UnitOfWork、QualityAssuranceService,连接字符串放在该项目的配置文件中。
    • 编写API控制器(逻辑可复用你现在Blazor Server里的控制器),对外暴露REST接口。
  2. Blazor Server作为客户端调用该API:

    • Blazor Server不再直接引用Services.QualityAssurance类库,而是通过HTTP请求调用API接口。
    • 在Blazor Server中创建一个客户端服务类,封装对API的调用逻辑,再注入到组件或控制器中使用。

当前方式的问题:

  • 直接引用服务层类库后,Blazor Server相当于直接操作数据库,服务层只是个中间类,完全没起到独立服务的作用,连接字符串自然只能放在Blazor端。
  • 这种模式下,其他.NET Core应用复用服务逻辑时也得引用类库,一旦服务逻辑变更,所有引用项目都要重新编译部署,违背了独立服务的初衷。

修正步骤:

  1. 改造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项目中。
  2. 调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 14:30:04