简体   繁体   English

HostedService:无法跟踪实体类型的实例

[英]HostedService: The instance of entity type cannot be tracked

I'm developing a web api with asp.net core 2.2 and ef core 2.2.1.我正在开发一个带有 asp.net core 2.2 和 ef core 2.2.1 的 web api。 The api, besides handling the restful requests made by an angular app, is in charge of processing some xml files that are used as an interface with other software. api 除了处理 angular 应用程序发出的 restful 请求外,还负责处理一些 xml 文件,这些文件用作与其他软件的接口。 Files are local to the application server and detected through a FileWatcher .文件位于应用程序服务器本地,并通过FileWatcher检测。

I've noticed during my tests that when I reprocess multiple times an xml test file, starting from the second time the file is being reprocessed, I obtain the Exception:我在测试期间注意到,当我多次重新处理一个 xml 测试文件时,从第二次重新处理文件开始,我获得了异常:

System.InvalidOperationException: The instance of entity type 'QualityLot' cannot be tracked because another instance with the key value '{QualityLotID: ...}' is already being tracked. System.InvalidOperationException:无法跟踪实体类型“QualityLot”的实例,因为已跟踪具有键值“{QualityLotID: ...}”的另一个实例。 When attaching existing entities, ensure that only one entity instance with a given key value is attached.附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

when I call the method DbContext.QualityLot.Update(qualityLot);当我调用方法DbContext.QualityLot.Update(qualityLot);

The "processing file" service and the service it's using are configured into the Startup.cs file as following: “处理文件”服务及其使用的服务配置到Startup.cs文件中,如下所示:

services.AddHostedService<InterfaceDownloadService>();
services.AddTransient<IQLDwnldService, QLDwnldService>();

the db context is configued like this: db 上下文配置如下:

services.AddDbContext<MyDbContext>(cfg =>
{                
    cfg.UseSqlServer(_config.GetConnectionString("LIMSConnectionString"));
});

and the class looks like:这个类看起来像:

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();
    }
}

Here the critical point, where I have the exception:这是关键点,我有例外:

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;
                    }
                }

The method UpdateQualityLot(qualityLot, qualityLotDeserialized);方法UpdateQualityLot(qualityLot, qualityLotDeserialized); is invoked because the entity already exists in the db被调用是因为实体已经存在于数据库中

private async Task UpdateQualityLot(QualityLot qualityLot, QualityLotDownloadQualityLotsQualityLot qualityLotDeserialized)
{
    [fields update]
    DbContext.QualityLot.Update(qualityLot);
    await DbContext.SaveChangesAsync();
}

The call to DbContext.QualityLot.Update(qualityLot);调用DbContext.QualityLot.Update(qualityLot); fails.失败。

From what I can see the instance of QLDwnldService is new for every file being processed, in other words the following method returns every time a new object (as configured into Startup.cs)从我所看到的QLDwnldService的实例对于正在处理的每个文件QLDwnldService都是新的,换句话说,每次新对象(如配置到 Startup.cs 中)时,以下方法都会返回

_ServiceProvider.GetService<IQLDwnldService>().QLDownloadAsync(ev);

, while the DbContext is reused and that's probably the reason why the entity results already tracked. ,而 DbContext 被重用,这可能是实体结果已经被跟踪的原因。

I also tride to setup the non-tracking option in the DbContext OnConfiguring()我也尝试在 DbContext OnConfiguring()设置非跟踪选项

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    base.OnConfiguring(optionsBuilder);
    optionsBuilder
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);  
}

So my question is.所以我的问题是。 What's wrong here?这里有什么问题? Maybe an architecture problematic or maybe a misleading configuration of e core?可能是架构有问题,还是 e 核的配置有误导性? Thanks in advance for any support.在此先感谢您的任何支持。

To be honest I could not figure out where your DBContext is actually injected from your code.老实说,我无法弄清楚您的 DBContext 是从您的代码中实际注入的位置。

But from the error message I'd say your context is reused in a place where it should not be.但是根据错误消息,我会说您的上下文在不应该出现的地方被重用。 So it's injected once and then used over and over and over.所以它被注射一次,然后一遍又一遍地使用。

You have registered your service as "Scoped" (because that's the default).您已将服务注册为“Scoped”(因为这是默认设置)。

You should register it as "Transient" to ensure you will get a new instance on every call to your service provider:您应该将其注册为“Transient”,以确保每次调用您的服务提供商时都会获得一个新实例:

services.AddDbContext<MyDbContext>(cfg =>
{                
    cfg.UseSqlServer(_config.GetConnectionString("LIMSConnectionString"));
}, 
ServiceLifetime.Transient);

Brad mentioned that this will have consequences for the rest of your application and he's right.布拉德提到这将对您申请的其余部分产生影响,他是对的。

The better option might be to leave your DbContext scoped and inject the IServiceScopeFactory into your hosted service.更好的选择可能是保留DbContext范围并将IServiceScopeFactory注入您的托管服务。 Then create a new scope where you need it:然后在需要的地方创建一个新范围:

using(var scope = injectedServiceScopeFactory.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetService<DbContext>();

    // do your processing with context

} // this will end the scope, the scoped dbcontext will be disposed here

Please note that this still does not mean that you should access the DbContext in parallel.请注意,这仍然并不意味着您应该并行访问 DbContext。 I don't know why your calls are all async.我不知道为什么你的电话都是异步的。 If you are actually doing parallel work, make sure you create one DbContext per thread.如果您实际上是在进行并行工作,请确保为每个线程创建一个 DbContext。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法跟踪实体类型的实例 - The instance of entity type cannot be tracked 实体类型“”的实例无法跟踪 - The instance of entity type '' cannot be tracked 实体类型的实体框架实例无法跟踪 - Entity Framework instance of entity type cannot be tracked InvalidOperationException:无法跟踪实体类型&#39;ApplicationUser&#39;的实例 - InvalidOperationException: The instance of entity type 'ApplicationUser' cannot be tracked 实体类型Menu的实例无法追踪 - The instance of entity type Menu cannot be tracked 无法跟踪实体类型“BookLoan”的实例 - The instance of entity type 'BookLoan' cannot be tracked 即使使用 AsNoTracking 也无法跟踪实体类型的实例 - The instance of entity type cannot be tracked even with AsNoTracking 实体框架核心 - 无法跟踪实体类型的实例,因为已在跟踪具有键值的另一个实例 - Entity framework Core - The instance of entity type cannot be tracked because another instance with the key value is already being tracked InvalidOperationException:无法跟踪实体类型“Vessels”&gt; 的实例,因为另一个实例 - InvalidOperationException: The instance of entity type 'Vessels' > cannot be tracked because another instance 无法跟踪实体类型“Entity”的实例,因为已跟踪另一个具有与 {'Id'} 相同键值的实例 - The instance of entity type 'Entity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM