【发布时间】:2019-02-11 16:21:59
【问题描述】:
我正在使用 asp.net core 2.2 和 ef core 2.2.1 开发一个 web api。该 api 除了处理 Angular 应用程序发出的 restful 请求外,还负责处理一些用作与其他软件接口的 xml 文件。文件在应用服务器本地,通过FileWatcher 检测到。
我在测试期间注意到,当我多次重新处理一个 xml 测试文件时,从第二次重新处理该文件开始,我得到了异常:
System.InvalidOperationException:实体类型的实例 无法跟踪“QualityLot”,因为另一个实例具有密钥 值“{QualityLotID: ...}”已被跟踪。什么时候 附加现有实体,确保只有一个实体实例具有 附加给定的键值。
当我调用DbContext.QualityLot.Update(qualityLot);方法时
“处理文件”服务及其使用的服务配置到Startup.cs文件中,如下所示:
services.AddHostedService<InterfaceDownloadService>();
services.AddTransient<IQLDwnldService, QLDwnldService>();
db 上下文配置如下:
services.AddDbContext<MyDbContext>(cfg =>
{
cfg.UseSqlServer(_config.GetConnectionString("LIMSConnectionString"));
});
类看起来像:
public class InterfaceDownloadService : BackgroundServiceBase
{
[...]
public InterfaceDownloadService(IHostingEnvironment env,
ILogger<InterfaceDownloadService> logger,
IServiceProvider serviceProvider)
{
_ServiceProvider = serviceProvider;
}
[...]
private void processFiles()
{
[...]
_ServiceProvider.GetService<IQLDwnldService>().QLDownloadAsync(ev);
}
}
public abstract class BackgroundServiceBase : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
这是关键点,我有例外:
public async Task QLDownloadAsync(FileReceivedEvent fileReceivedEvent)
{
Logger.LogInformation($"QLDwnld file {fileReceivedEvent.Event.FullPath} received for Processing");
try
{
QualityLotDownload qualityRoutingDwnld = deserializeObject<QualityLotDownload>(fileReceivedEvent.XsltPath, fileReceivedEvent.Event.FullPath);
Logger.LogDebug($"QLDwnld file {fileReceivedEvent.Event.FullPath} deserialized correctly. Need to determinate whether Insert or Update QualityLot {qualityRoutingDwnld.QualityLots.QualityLot.QualityLotID}");
for (int remainingRetries = fileReceivedEvent.MaxRetries; remainingRetries > 0; remainingRetries--)
{
using (var transaction = await DbContext.Database.BeginTransactionAsync())
{
try
{
var qualityLotDeserialized = qualityRoutingDwnld.QualityLots.QualityLot;
// insert the object into the database
var qualityLot = await DbContext.QualityLot.Where(x => x.QualityLotID == qualityLotDeserialized.QualityLotID).FirstOrDefaultAsync();
if (qualityLot == null) // INSERT QL
{
await InsertQualityLot(qualityLotDeserialized);
}
else // UPDATE QL
{
await UpdateQualityLot(qualityLot, qualityLotDeserialized);
}
[...]
transaction.Commit();
}
catch (Exception ex)
{
Logger.LogError(ex, $"Retry {fileReceivedEvent.MaxRetries - remainingRetries +1}: Exception processing QLDwnld file {fileReceivedEvent.Event.FullPath}.");
transaction.Rollback();
if (remainingRetries == 1)
{
return;
}
}
调用方法UpdateQualityLot(qualityLot, qualityLotDeserialized);是因为实体已经存在于db中
private async Task UpdateQualityLot(QualityLot qualityLot, QualityLotDownloadQualityLotsQualityLot qualityLotDeserialized)
{
[fields update]
DbContext.QualityLot.Update(qualityLot);
await DbContext.SaveChangesAsync();
}
对DbContext.QualityLot.Update(qualityLot); 的调用失败。
据我所见,QLDwnldService 的实例对于正在处理的每个文件都是新的,换句话说,以下方法在每次新对象时返回(如在 Startup.cs 中配置的那样)
_ServiceProvider.GetService<IQLDwnldService>().QLDownloadAsync(ev);
,而 DbContext 被重用,这可能是实体结果已经被跟踪的原因。
我也尝试在 DbContext OnConfiguring() 中设置非跟踪选项
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}
所以我的问题是。这里有什么问题?可能是架构有问题,或者可能是 e 核心的误导性配置?提前感谢您的支持。
【问题讨论】:
标签: c# entity-framework asp.net-core .net-core entity-framework-core