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

Blazor单例类中使用DbContextFactory的实现疑问

问题描述

我仍在尝试理解如何在普通.cs类/单例中使用DbContextFactory,需求是用单例存储缓存的Users列表,通过服务类获取。我知道如何在Razor页面注入DbContext,但不清楚如何在UsersScopedService中使用,也不确定连接字符串的传递是否正确。现有代码如下:

Program.cs

var connectionString = builder.Configuration.GetConnectionString("Main");
builder.Services.AddDbContextFactory<MyDBContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddSingleton<MySingletonService>();

MyDBContext.cs

public class MyDBContext : DbContext
{
    public DbSet<Models.Users> Users { get; set; }
    public DbSet<Models.Orders> Orders { get; set; }
    
    public MyDBContext(DbContextOptions<MyDBContext> options) : base(options)
    {
        _options = options;
    }
}

UsersSingletonService.cs

public class UsersSingletonService
{
    internal List<Users?> allUsers;

    internal void SetDataUsers(List<Users> value)
        => allUsers = value;
}

UsersScopedService.cs

public class UsersScopedService
{
    private UsersSingletonService _usersSingletonService;
    //define the DBContextFactory here?
    //private IDbContextFactory<MyDBContext> _dbFactory;
  
    public UsersScopedService(UsersSingletonService usersSingletonService)
    {
        //somehow get the DBContextFactory here?
        _usersSingletonService = usersSingletonService;
    }

     public async ValueTask<List<Users>?> GetUsersDataAsync()
     {
         if (_usersSingletonService.allUsers is null)
         {
             //how do we get DB context here?
             var myDBContext = _dbFactory.CreateDbContext();
             var allUsers = await myDBContext.Users.ToListAsync();
             _usersSingletonService.SetDataUsers(allUsers);
         }
         return _usersSingletonService.allUsers;
     }
}
解决方案

1. 注入DbContextFactory到UsersScopedService

直接通过构造函数注入IDbContextFactory<MyDBContext>,DI容器会自动传入已配置好连接字符串的实例。修改后的UsersScopedService代码:

public class UsersScopedService
{
    private readonly UsersSingletonService _usersSingletonService;
    private readonly IDbContextFactory<MyDBContext> _dbFactory;
  
    // 通过构造函数注入两个依赖
    public UsersScopedService(UsersSingletonService usersSingletonService, IDbContextFactory<MyDBContext> dbFactory)
    {
        _usersSingletonService = usersSingletonService;
        _dbFactory = dbFactory;
    }

     public async ValueTask<List<Users>?> GetUsersDataAsync()
     {
         if (_usersSingletonService.allUsers is null)
         {
             // 使用using确保DbContext使用后及时释放资源
             using var myDBContext = _dbFactory.CreateDbContext();
             var allUsers = await myDBContext.Users.ToListAsync();
             _usersSingletonService.SetDataUsers(allUsers);
         }
         return _usersSingletonService.allUsers;
     }
}

2. 注册UsersScopedService到DI容器

在Program.cs中添加Scoped服务的注册:

var connectionString = builder.Configuration.GetConnectionString("Main");
builder.Services.AddDbContextFactory<MyDBContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddSingleton<MySingletonService>();
// 添加Scoped服务注册
builder.Services.AddScoped<UsersScopedService>();

3. 简化MyDBContext代码

原代码中_options字段未实际使用,可直接删除:

public class MyDBContext : DbContext
{
    public DbSet<Models.Users> Users { get; set; }
    public DbSet<Models.Orders> Orders { get; set; }
    
    public MyDBContext(DbContextOptions<MyDBContext> options) : base(options)
    {
    }
}

4. 优化单例缓存的线程安全

原单例的allUsers字段在高并发场景下可能出现重复加载问题,建议添加锁机制:

public class UsersSingletonService
{
    private List<Users?> _allUsers;
    private readonly object _lockObj = new object();

    internal List<Users?> AllUsers => _allUsers;

    internal void SetDataUsers(List<Users> value)
    {
        lock (_lockObj)
        {
            _allUsers = value;
        }
    }
}

同时修改UsersScopedService的查询逻辑,结合双重检查锁避免重复查询:

public async ValueTask<List<Users>?> GetUsersDataAsync()
{
    if (_usersSingletonService.AllUsers is null)
    {
        lock (_usersSingletonService._lockObj)
        {
            // 双重检查确保仅加载一次
            if (_usersSingletonService.AllUsers is null)
            {
                using var myDBContext = _dbFactory.CreateDbContext();
                var allUsers = await myDBContext.Users.ToListAsync();
                _usersSingletonService.SetDataUsers(allUsers);
            }
        }
    }
    return _usersSingletonService.AllUsers;
}

内容的提问来源于stack exchange,提问作者Joe Defill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 21:05:11