Microsoft.EntityFrameworkCore.dll 中发生类型 'System.InvalidOperationException' 的异常,但未在用户代码中处理
An exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll but was not handled in user code
我正在尝试使用 Task.Run 从 IActionResult 调用一个方法。这是我得到的异常。
An exception of type 'System.InvalidOperationException' occurred in
Microsoft.EntityFrameworkCore.dll but was not handled in user code:
'An exception was thrown while attempting to evaluate a LINQ query
parameter expression. To show additional information call
EnableSensitiveDataLogging() when overriding DbContext.OnConfiguring.'
Inner exceptions found, see $exception in variables window for more
details. Innermost exception System.NullReferenceException : Object
reference not set to an instance of an object.
EntityContext 实例的初始化:
private readonly EntityContext _context;
public ApiController (
UserManager<User> userManager,
SignInManager<User> signInManager,
ILoggerFactory loggerFactory,
IMemoryCache memoryCache,
EntityContext context,
IRepository repository,
Context session,
IEmailService emailService,
IHostingEnvironment environment,
IHttpContextAccessor contextAccessor, ViewRender view, IStringLocalizer<SharedResources> localizer) : base (userManager, signInManager, loggerFactory, memoryCache, context, repository, session, contextAccessor) {
//_view = view;
_emailService = emailService;
this.environment = environment;
_localizer = localizer;
this._context = context;
}
Startup.cs
services.AddEntityFrameworkNpgsql ()
.AddDbContext<EntityContext> (
options => options.UseNpgsql (connectionString)
);
从控制器调用方法:
if(updated){
Task t1 = Task.Run(()=>SendEmailAsync(entity,true,responsible,_context));
}else{
Task t1 = Task.Run(()=>SendEmailAsync(entity,false,responsible,_context));
}
我调用的方法:
public void SendEmailAsync (Activity entity, bool updated, User responsible, EntityContext ctx) {
List<string> emailList = new List<string> ();
var mail = new MailComposer (_emailService, environment, _localizer);
if (responsible.IsSubscriber) {
emailList.Add (responsible.Email);
}
if (entity.Participants.Count > 0) {
foreach (var item in entity.Participants) {
var p = ctx.Users.Where(c=>c.Id==item.Participant.Id).FirstOrDefault(); //This is where I am getting an exception.
if (p.IsSubscriber) {
emailList.Add (p.Email);
}
}
}
if (emailList.Count != 0) {
var emailArray = emailList.ToArray ();
if (updated) {
mail.SendActivityUpdate (entity, emailArray);
} else {
mail.SendActivityCreated (entity, emailArray);
}
}
}
Task.Run
将启动一个新线程,除非您等待它,否则操作 运行 的现有线程将继续运行,最终返回并获取上下文,您的方法运行在新线程依赖。如果您 do await 它,那么 运行 在单独的线程中就没有意义了;你只是无缘无故地消耗了额外的资源。
简而言之,您根本不应该为此使用 Task.Run
。它与 "running the background" 不同。相反,您应该安排电子邮件在不同的进程或至少 IHostedService
上发送。您可以使用 QueuedBackgroundService
。在 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#queued-background-tasks.
有可用的实现
对于您的问题,这是因为您从另一个线程引用了范围服务 EntityContext
。对于 EntityContext
,它将在控制器发出 return 请求时处理。
根据 Chris 的建议,您可以在请求 return 返回客户端之前调用 t1.Wait();
来完成 t1 任务。通过调用 t1.Wait();
,EntityContext _context
将不会被释放,这样您就不会收到任何错误。
对于另一种选择,您可以尝试通过 IServiceProvider
创建一个新的 EntityContext
而不是引用由 Controller
[=20= 创建的现有 EntityContext
]
public class HomeController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<HomeController> _logger;
public HomeController(ApplicationDbContext context
, IServiceProvider serviceProvider
, ILogger<HomeController> logger)
{
_context = context;
_serviceProvider = serviceProvider;
_logger = logger;
}
public IActionResult TestTask()
{
Task t1 = Task.Run(() => SendEmailAsync(_serviceProvider));
//t1.Wait();
return Ok();
}
private void SendEmailAsync(IServiceProvider serviceProvider)
{
var context = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<ApplicationDbContext>();
var result = context.Student.ToList();
_logger.LogInformation(JsonConvert.SerializeObject(result));
}
}
我正在尝试使用 Task.Run 从 IActionResult 调用一个方法。这是我得到的异常。
An exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll but was not handled in user code: 'An exception was thrown while attempting to evaluate a LINQ query parameter expression. To show additional information call EnableSensitiveDataLogging() when overriding DbContext.OnConfiguring.' Inner exceptions found, see $exception in variables window for more details. Innermost exception System.NullReferenceException : Object reference not set to an instance of an object.
EntityContext 实例的初始化:
private readonly EntityContext _context;
public ApiController (
UserManager<User> userManager,
SignInManager<User> signInManager,
ILoggerFactory loggerFactory,
IMemoryCache memoryCache,
EntityContext context,
IRepository repository,
Context session,
IEmailService emailService,
IHostingEnvironment environment,
IHttpContextAccessor contextAccessor, ViewRender view, IStringLocalizer<SharedResources> localizer) : base (userManager, signInManager, loggerFactory, memoryCache, context, repository, session, contextAccessor) {
//_view = view;
_emailService = emailService;
this.environment = environment;
_localizer = localizer;
this._context = context;
}
Startup.cs
services.AddEntityFrameworkNpgsql ()
.AddDbContext<EntityContext> (
options => options.UseNpgsql (connectionString)
);
从控制器调用方法:
if(updated){
Task t1 = Task.Run(()=>SendEmailAsync(entity,true,responsible,_context));
}else{
Task t1 = Task.Run(()=>SendEmailAsync(entity,false,responsible,_context));
}
我调用的方法:
public void SendEmailAsync (Activity entity, bool updated, User responsible, EntityContext ctx) {
List<string> emailList = new List<string> ();
var mail = new MailComposer (_emailService, environment, _localizer);
if (responsible.IsSubscriber) {
emailList.Add (responsible.Email);
}
if (entity.Participants.Count > 0) {
foreach (var item in entity.Participants) {
var p = ctx.Users.Where(c=>c.Id==item.Participant.Id).FirstOrDefault(); //This is where I am getting an exception.
if (p.IsSubscriber) {
emailList.Add (p.Email);
}
}
}
if (emailList.Count != 0) {
var emailArray = emailList.ToArray ();
if (updated) {
mail.SendActivityUpdate (entity, emailArray);
} else {
mail.SendActivityCreated (entity, emailArray);
}
}
}
Task.Run
将启动一个新线程,除非您等待它,否则操作 运行 的现有线程将继续运行,最终返回并获取上下文,您的方法运行在新线程依赖。如果您 do await 它,那么 运行 在单独的线程中就没有意义了;你只是无缘无故地消耗了额外的资源。
简而言之,您根本不应该为此使用 Task.Run
。它与 "running the background" 不同。相反,您应该安排电子邮件在不同的进程或至少 IHostedService
上发送。您可以使用 QueuedBackgroundService
。在 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#queued-background-tasks.
对于您的问题,这是因为您从另一个线程引用了范围服务 EntityContext
。对于 EntityContext
,它将在控制器发出 return 请求时处理。
根据 Chris 的建议,您可以在请求 return 返回客户端之前调用 t1.Wait();
来完成 t1 任务。通过调用 t1.Wait();
,EntityContext _context
将不会被释放,这样您就不会收到任何错误。
对于另一种选择,您可以尝试通过 IServiceProvider
创建一个新的 EntityContext
而不是引用由 Controller
[=20= 创建的现有 EntityContext
]
public class HomeController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<HomeController> _logger;
public HomeController(ApplicationDbContext context
, IServiceProvider serviceProvider
, ILogger<HomeController> logger)
{
_context = context;
_serviceProvider = serviceProvider;
_logger = logger;
}
public IActionResult TestTask()
{
Task t1 = Task.Run(() => SendEmailAsync(_serviceProvider));
//t1.Wait();
return Ok();
}
private void SendEmailAsync(IServiceProvider serviceProvider)
{
var context = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<ApplicationDbContext>();
var result = context.Student.ToList();
_logger.LogInformation(JsonConvert.SerializeObject(result));
}
}