c# - 无法访问 ASP.NET Core 后台服务中的已释放对象
问题描述
我试图弄清楚为什么我的数据库上下文被下面的代码处理掉了。我正在尝试在整个处理阶段更新记录的状态,但是在尝试将更改保存到 htis 实体的状态字段时出现异常。
如果我在 .SaveChanges() 或 .FirstOrDefault() 调用上使用异步或非异步方法,这没有区别。
这已注册为由 IHostedService 运行
IHostedService:
public class HostedDaemon : IHostedService
{
private readonly ILogger<HostedDaemon> _logger;
private Timer _queueTimer;
private Timer _runTimer;
private IServiceProvider Services { get; set; }
public HostedDaemon(IServiceProvider services, ILogger<HostedDaemon> logger)
{
Services = services;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
var queueAutoEvent = new AutoResetEvent(false);
_logger.LogInformation("HostedDaemon Starting");
_queueTimer = new Timer(QueueJobs, queueAutoEvent, TimeSpan.Zero,
TimeSpan.FromSeconds(900));
_runTimer = new Timer(RunQueuedJobs, queueAutoEvent, TimeSpan.Zero,
TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("HostedDaemon Stopping");
_queueTimer?.Change(Timeout.Infinite, 0);
_runTimer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_queueTimer?.Dispose();
_runTimer?.Dispose();
}
private void QueueJobs(object stateInfo)
{
using (var scope = Services.CreateScope())
{
var autoEvent = (AutoResetEvent)stateInfo;
var daemonProcessing = scope.ServiceProvider.GetRequiredService<IDaemonService>();
//daemonProcessing.QueueJob();
//daemonProcessing.RunJob();
autoEvent.Set();
}
}
private void RunQueuedJobs(object stateInfo)
{
using (var scope = Services.CreateScope())
{
var autoEvent = (AutoResetEvent)stateInfo;
var daemonProcessing = scope.ServiceProvider.GetRequiredService<IDaemonService>();
//daemonProcessing.QueueJob();
daemonProcessing.RunJob();
autoEvent.Set();
}
}
}
后台服务:
public async Task RunJob()
{
try
{
_logger.LogInformation("Starting transaction processing");
//TODO: Hook up SP
var nextQueuedJob = _context.Transaction
.Where(x => x.TransactionStatusId == (int) TransactionStatus.Queued).OrderBy(x => x.QueuedTime)
.Include(x => x.BrokerageData).FirstOrDefault();
if (nextQueuedJob != null)
{
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.ProcessingData;
_context.Transaction.Update(nextQueuedJob);
//await _context.SaveChangesAsync();
var brokerageData = nextQueuedJob.BrokerageData.FirstOrDefault();
var order = JsonConvert.DeserializeObject<OrderShort>(brokerageData.BrokerageContent);
var dynamicsSalesOrder = await CreateNewSalesOrderShort(order);
var salesOrderLines = new List<SalesOrderLineShort>();
foreach (var orderItem in order.Items)
{
var unitPrice = (orderItem.SubTotal + orderItem.Tax)/orderItem.Quantity;
var salesOrderLine = new SalesOrderLineShort
{
ItemId = new Guid("42dd99ff-7133-41f7-88c5-81f922ef77dd"),
LineType = "Item",
Description = orderItem.ProductDescription,
UnitOfMeasureId = new Guid("cab0928b-e7af-4bd6-923f-3706b4761681"),
UnitPrice = unitPrice,
DiscountAmount = orderItem.CouponDiscountAmount,
Quantity = orderItem.Quantity,
TaxCode = "DEV"
};
salesOrderLines.Add(salesOrderLine);
}
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.ProcessingComplete;
await _context.SaveChangesAsync();
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.ReadyForTransfer;
//TOOO: hook into reconciliation tables to commit an update
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.TransferringData;
foreach (var salesOrderLine in salesOrderLines)
{
await _requestClient.PostRequest(
$"https://api.businesscentral.dynamics.com/v1.0/afff8eda-37c9-4c72-833b-a36dc6c8d5df/api/beta/companies/c7fdd45d-617d-47b3-adbe-7c0c69c21dd1/salesOrders/{dynamicsSalesOrder.Id}/salesOrderLines",
salesOrderLine);
}
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.TransferComplete;
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.ReadyForValidation;
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.ValidatingOriginData;
var query = new CrmQuery();
query.UpdateOrderStatus(order.SalesOrderId, CrmQuery.OrderFIStatus.Completed,
order.FI_LastModified);
nextQueuedJob.TransactionStatusId = (int)TransactionStatus.TransactionCompleted;
}
}
catch (Exception e)
{
_logger.LogError($"exception occurred while running job.\n{e.InnerException}");
}
}
例外:
Message "Cannot access a disposed object. A common cause of this error
is disposing a context that was resolved from dependency injection and then
later trying to use the same context instance elsewhere in your
application.
This may occur if you are calling Dispose() on the context, or wrapping the
context in a using statement. If you are using dependency injection, you
should let the dependency injection container take care of disposing context
instances.\r\nObject name: 'FisEntities'." string
启动配置:
public void ConfigureServices(IServiceCollection services)
{
DynamicsAccountGuid = Configuration["Services:Dynamics:AccountGuid"];
DynamicsCompany = Configuration["Services:Dynamics:Companies:Development"];
DynamicsBasicAuth = Configuration["Services:Dynamics:BasicAuth"];
ConnectionString = Configuration["ConnectionStrings:FIS_Local"];
services.AddSingleton(Configuration);
services.AddEntityFrameworkSqlServer()
.AddDbContext<FisEntities>(options => options.UseSqlServer(ConnectionString));
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddHttpClient<IRequestClient, DynamicsClient>("dynamics", d =>
{
d.BaseAddress = new Uri($"https://api.businesscentral.dynamics.com/v1.0/{DynamicsAccountGuid}/api/beta/companies/{DynamicsCompany}");
d.DefaultRequestHeaders.Add("Authorization", DynamicsBasicAuth);
});
services.AddTransient<IQueueClient, CrmClient>();
services.AddHostedService<HostedDaemon>();
services.AddScoped<IDaemonService, TransactionService>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
解决方案
在您的课堂RunQueuedJobs
方法中HostedDaemon
,替换daemonProcessing.RunJob();
为daemonProcessing.RunJob().Wait();
如下:
private void RunQueuedJobs(object stateInfo)
{
using (var scope = Services.CreateScope())
{
var autoEvent = (AutoResetEvent)stateInfo;
var daemonProcessing = scope.ServiceProvider.GetRequiredService<IDaemonService>();
//daemonProcessing.QueueJob();
daemonProcessing.RunJob().Wait(); // <-- Here it is
autoEvent.Set();
}
}
现在它应该可以正常工作了。
推荐阅读
- firebase - 如何在firebase中按两个字段排序数据
- javascript - 如何根据下拉选择获取复选框的值?
- vue.js - 在类星体中加载 vue-native-websocket
- python-3.x - 没有让多线程为 GTK 3 python 工作
- python - python中异步线程和事件循环的问题
- java - 无法使用 Java11 和 Windows 在 cmd 上运行 Java 应用程序
- javascript - SVG 在外部托管时不会在任何浏览器中呈现
- firebase - 为最终用户处理 Flutter 上的 Firebase 身份验证错误
- open-telemetry - Opentelemetry 收集器和代理有什么区别?
- android - 为什么 Kotlin Coroutine withTimeoutOrNull 花费的时间比分配的时间长?