Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
like image 268
nam vo Avatar asked Apr 15 '17 09:04

nam vo


Video Answer


1 Answers

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 our repository multiple times during 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 fro each instance of your service and you will avoid concurrency issue.

like image 124
Andrii Litvinov Avatar answered Oct 15 '22 20:10

Andrii Litvinov