使用依赖注入时,如何修复“在前一个操作完成之前在此上下文中启动的第二个操作......”?

Posted

技术标签:

【中文标题】使用依赖注入时,如何修复“在前一个操作完成之前在此上下文中启动的第二个操作......”?【英文标题】:How to fix 'A second operation started on this context before a previous operation completed...' when working with dependency injection? 【发布时间】:2018-03-05 19:54:33 【问题描述】:

从数据库读取数据时出现此错误:

在前一个操作之前在此上下文上启动了第二个操作 完全的。不保证任何实例成员都是线程安全的。

我有以下 ApplicationContext.cs:

public class ApplicationContext : Microsoft.EntityFrameworkCore.DbContext

    public ApplicationContext(DbContextOptions<ApplicationContext> options)
        : base(options)
     

    public DbSet<MyClass> MyClasses get; set; 
   

以下ApplicationContextFactory.cs

public class ApplicationContextFactory : IDesignTimeDbContextFactory<ApplicationContext>

    public ApplicationContext CreateDbContext(string[] args)
    
        var builder = new DbContextOptionsBuilder<ApplicationContext>();
        var connection = "myConnectionString";

        builder.UseSqlServer(connection);

        return new ApplicationContext(builder.Options);
    
   

以下 ServiceLoader.cs(我在其中声明 DI):

public static class ServiceLoader

    public static void ConfigureServices(IServiceCollection services)
    
        services.AddSingleton<IRepository, Repository>();

        var connection = "myConnectionString";
        services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(connection));
    

最后是引发异常的以下存储库:

public class Repository : IRepository

    private ApplicationContext _db;

    public Repository (ApplicationContext db)
    
        _db = db;
    

    public List<MyClass> Get()
    
        _db.MyClasses.ToList();
    

我也尝试将 Repository 声明为 Transient 而不是 Singleton,但抛出了类似的错误

'在配置时尝试使用上下文。 DbContext 实例不能在 OnConfiguring 中使用,因为此时它仍在配置中。如果在前一个操作完成之前对此上下文启动了第二个操作,则可能会发生这种情况。不保证任何实例成员都是线程安全的。'

关于如何解决这个问题的任何想法?谢谢!

【问题讨论】:

您应该在Repository 单例之前声明上下文。另请注意,隐藏实体框架的大多数存储库实现都是浪费开发时间并给出 0 值。您应该对在存储库中隐藏存储库有一个很好的解释 还要注意这个模型很奇怪:Repository(单例)依赖于一个临时服务(DbContext 另外,Repository 正在使用Entity Framework Core,它以sync 的方式允许完整的async 方法,从而不必要地阻塞线程。 如果问题得到解决,您应该发布答案 @CamiloTerevinto 正如我所说的,它也是一种补充大查询的 DRY 原则的方法。实际上,当我不得不替换系统中广泛使用的一种存储库方法的实现以使用 StoredProcedure 而不是 EF 查询(例如)时,我遇到了很多情况。如果这些查询分散在整个系统中会发生什么? :) 【参考方案1】:

就我而言,我发现以下信息很有帮助:

https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

并在启动时使用重载的 AddDbContext 方法将我的 Db 上下文的生命周期范围更改为瞬态:

services.AddDbContext<MyAppDbContext>(options => 
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection"));
        , ServiceLifetime.Transient);

【讨论】:

【参考方案2】:

您可以在 Get() 函数周围包裹一个异步任务并等待您的结果:

public async Task<List<MyClass>> Get()

   return await _db.MyClasses.ToListAsync();

【讨论】:

【参考方案3】:

我写了一个使用队列的解决方案。它仍然是单线程的,但你可以从不同的线程调用它。

public class ThreadSafeDataContext
    
        private Thread databaseThread;
        private Queue<PendingQuery> pendingQueries = new Queue<PendingQuery>();
        private DatabaseContext db = new DatabaseContext();
        private bool running = true;
        public ThreadSafeDataContext()
        
            databaseThread = new Thread(new ThreadStart(DoWork));
            databaseThread.Start();
        
        public void StopService()
        
            running = false;
        
        private void DoWork()
        
            while(running)
            
                if (pendingQueries.Count > 0)
                
                    // Get and run query
                    PendingQuery query = pendingQueries.Dequeue();
                    query.result = query.action(db);
                    query.isFinished = true;
                
                else
                
                    Thread.Sleep(1); // Waiting for queries
                
            
        

        public T1 Query<T1>(Func<DatabaseContext, T1> action)
        
            Func<DatabaseContext, object> a = (DatabaseContext db) => action(db);
            PendingQuery query = new PendingQuery(a);
            pendingQueries.Enqueue(query);

            while (!query.isFinished) 
                Thread.Sleep(1); // Wait until query is finished
            

            return (T1)query.result;
        
    
    class PendingQuery
    
        public Func<DatabaseContext, object> action;
        public bool isFinished;
        public object result;

        public PendingQuery(Func<DatabaseContext, object> action)
        
            this.action = action;
        
    

然后您可以使用以下命令从不同线程运行查询:

TeamMembers teamMembers = threadSafeDb.Query((DatabaseContext c) => c.team.ToArray())

【讨论】:

以上是关于使用依赖注入时,如何修复“在前一个操作完成之前在此上下文中启动的第二个操作......”?的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core:在前一个操作完成之前在此上下文上启动了第二个操作

Blazor:在前一个操作完成之前在此上下文上启动了第二个操作

asp.net core 在前一个操作完成之前在此上下文上启动了第二个操作

对 ITVF 的引用会引发“在前一个操作完成之前在此上下文上启动的第二个操作”异常

多线程 DbContext 操作

重新打包 Spring Boot Jar 时如何修复压缩错误