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
相关产品推荐
相关产品推荐

