【问题标题】:Entity Framework Core thread safe?实体框架核心线程安全吗?
【发布时间】:2017-04-15 09:59:21
【问题描述】:

我关注Generic Repository Pattern in ASP.NET Core,但在IRepository 上,我使用IQueryable 而不是IEnumerable

public  interface IRepository<T> where T: BaseEntity
{
    IQueryable<T> Table { get; }
    IEnumerable<T> TableNoTracking { get; }
    T Get(long id);
    void Insert(T entity);
    void Update(T entity);
    void Delete(T entity);
}

和实现类:

    public class EFRepository<T> : IRepository<T> where T : BaseEntity
    {
        private readonly ApplicationDbContext _ctx;
        private DbSet<T> entities;
        string errorMessage = string.Empty;

        public EFRepository(ApplicationDbContext context)
        {
            this._ctx = context;
            entities = context.Set<T>();
        }

        public virtual IQueryable<T> Table => this.entities;
    }

服务类:

public class MovieService : IMovieService
{
        private readonly IRepository<MovieItem> _repoMovie;

        public MovieService(IRepository<MovieItem> repoMovie)
        {
            _repoMovie = repoMovie;
        }

        public async Task<PaginatedList<MovieItem>> GetAllMovies(int pageIndex = 0, int pageSize = int.MaxValue,
               IEnumerable<int> categoryIds = null)
        {
            var query = _repoMovie.Table;

            if (categoryIds != null)
            {
                query = from m in query
                        where categoryIds.Contains(m.CategoryId)
                        select m;
            }

            return await PaginatedList<MovieItem>.CreateAsync(query, pageIndex, pageSize);
        }
}

在 Startup.cs 上:

  public void ConfigureServices(IServiceCollection services)
  {
        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddMvc();
        services.AddScoped(typeof(IRepository<>), typeof(EFRepository<>));           
        services.AddTransient<IMovieService, MovieService>();
        services.AddTransient<ICategoryService, CategoryService>();
    }

此代码抛出错误:

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

如果我在IRepository 上切换回IEnumerable,那么它运行良好。

知道如何让它与IQueryable 一起工作,以使 EF Core 以正确的方式运行吗?

query = from m in query
        where categoryIds.Contains(m.CategoryId)
        select m;

【问题讨论】:

  • 我猜可能是 services.AddScoped(typeof(IRepository), typeof(EFRepository));和 services.AddTransient();这将调用另一个 ef 上下文。如果我只是删除服务类并直接使用Repository,那就可以了。

标签: c# asp.net-mvc entity-framework


【解决方案1】:

实体框架DbContext 不是线程安全的。你一次只能执行一个查询,否则你会像上面那样得到一个异常。

我想你在同一个请求中多次使用我们的存储库,这就是你得到异常的原因。如果您不为每个请求创建事务,则可以简单地使存储库瞬态。在这种情况下,将为您的每个服务实例创建新的存储库,您将避免并发问题。

【讨论】:

  • 感谢您的帮助。我试过 services.AddTransient(typeof(IRepository), typeof(EFRepository));但仍然失败,不知道如何修复它。我刚刚用几行代码开始了这个项目,现在卡住了:D
  • 我发现 categoryIds.Contains(m.CategoryId) 导致问题的地方。如果我转换 categoryIds.ToList() (来自 IEnumerable),那么它运行正常。
  • @namvo,如果您在单独的查询中从数据库中检索categoryIds,它会解释并发性。好你发现了。
  • Tnx,我花了一整天的时间来解决这个问题,将我的 repos 的 AddScoped 更改为 AddTransient 解决了这个问题。
  • @Nikola Gaić 你拯救了我的一天!多谢。将 AddScoped 更改为 AddTransient 也对我有用。
猜你喜欢
  • 2017-12-23
  • 2016-08-29
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多