nam vo
nam vo

Reputation: 3447

Entity Framework Core thread safe?

I follow Generic Repository Pattern in ASP.NET Core, but on IRepository, I use IQueryable instead of 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);
}

and implementation class:

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

Service class:

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

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

This code throws an error:

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

If I switch back to IEnumerable on IRepository, then it runs fine.

Any idea how to get it to work with IQueryable, to make to EF Core to run in the right way ?

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

Upvotes: 8

Views: 15049

Answers (1)

Andrii Litvinov
Andrii Litvinov

Reputation: 13192

Entity Framework DbContext is not thread safe. You can execute only one query at a time otherwise you will get an exception like you did above.

I suppose that you use your repository multiple times during the same request in parallel that's why you get the exception. If you don't create a transaction per request you can simply make repository transient. In this case new repository will be created for each instance of your service and you will avoid concurrency issue.

Upvotes: 9

Related Questions