Ale*_*jic 2 c# entity-framework dependency-injection asp.net-core
我正在使用.NET Core 3.1。我想运行一些后台处理,而用户不必等待它完成(大约需要 1 分钟)。因此,我Task.Run这样使用:
public class MyController : Controller
{
private readonly IMyService _myService;
public MyController(IMyService myService)
{
_myService = myService;
}
public async Task<IActionResult> Create(...)
{
await _myService.CreatePostAsync(...);
return View();
}
}
public class MyService : IMyService
{
private readonly MyDbContext _dbContext;
private readonly IServiceScopeFactory _scopeFactory;
public MyService(MyDbContext dbContext, IServiceScopeFactory scopeFactory)
{
_dbContext = dbContext;
_scopeFactory = scopeFactory;
}
public async Task CreatePostAsync(Post post)
{
...
string username = GetUsername();
DbContextOptions<MyDbContext> dbOptions = GetDbOptions();
Task.Run(() => SaveFiles(username, dbOptions, _scopeFactory));
}
private void SaveFiles(string username, DbContextOptions<MyDbContext> dbOptions, IServiceScopeFactory scopeFactory)
{
using (var scope = scopeFactory.CreateScope())
{
var otherService = scope.ServiceProvider.GetRequiredService<IOtherService>();
var cntxt = new MyDbContext(dbOptions, username);
Post post = new Post("abc", username);
cntxt.Post.Add(post); <----- EXCEPTION
cntxt.SaveChanges();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在标记行中收到以下异常:
System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'IServiceProvider'.'
Run Code Online (Sandbox Code Playgroud)
为什么会出现这种情况?我对 MyDbContext 使用了自定义构造函数(而不是scope.ServiceProvider.GetRequiredService<MyDbContext>()),因为我需要保存一项附加属性(用户名)以供以后在重写方法中使用。
public partial class MyDbContext
{
private string _username;
private readonly DbContextOptions<MyDbContext> _options;
public DbContextOptions<MyDbContext> DbOptions { get { return _options; } }
public MyDbContext(DbContextOptions<MyDbContext> options, string username) : base(options)
{
_username = username;
_options = options;
}
... other overriden methods
}
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
首先,不要在服务中隐藏线程池操作;让调用代码决定是否在线程池上运行操作:
当您使用依赖注入时,框架会DbContext在 HTTP 请求结束时处理您的内容。
您需要将服务范围工厂注入控制器,并从那里请求服务:
public class MyController : Controller
{
private readonly IMyService _myService;
private readonly IServiceScopeFactory _scopeFactory;
public MyController(IMyService myService, IServiceScopeFactory scopeFactory)
{
_myService = myService;
_scopeFactory = scopeFactory;
}
public async Task<IActionResult> Create(...)
{
HostingEnvironment.QueueBackgroundWorkItem(SaveInBackground);
return View();
}
private async Task SaveInBackground(CancellationToken ct)
{
using (var scope = scopeFactory.CreateScope())
{
var scopedService = scope.ServiceProvider.GetRequiredService<IMyService>();
await scopedService.CreatePostAsync(...);
}
}
}
Run Code Online (Sandbox Code Playgroud)
HostingEnvironment.QueueBackgroundWorkItem工作方式与 类似Task.Run,只不过它确保应用程序在所有后台工作项完成之前不会关闭。
您的服务需要是这样的:
public class MyService : IMyService
{
private readonly MyDbContext _dbContext;
public MyService(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task CreatePostAsync(Post post)
{
_dbContext.Post.Add(post);
await _dbContext.SaveChangesAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
更新
要将附加参数传递给SaveInBackground:
private async Task SaveInBackground(YourParam param)
Run Code Online (Sandbox Code Playgroud)
然后调用如下:
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => SaveInBackground(yourParam));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5403 次 |
| 最近记录: |