Reputation: 3447
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
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