1

I'm developing a web application using ASP.NET MVC and EF6 to access the database.

One of the features of my web application allow the user to download a Excel file. The query to get the information from the database takes like 5 seconds and I notice that until the query it's done we can't do anything on the rest of the web application.

Is this the normal behaviour of EF, lock the database even with AsNoTracking on the query?

If I'm not doing anything wrong and this is the default behaviour of EF how should I resolve this locking problem?

(Update)

I'm using a SQL Server database and the "lock" happens when for exemple I export the excel file and at the same time do a search that uses the same table.

To organize my code i'm using Repository and UnitOfWork pattern and to create the instances i'm using DI Unity.

The UnitOfWork implementation:

public class UnitOfWork : IUnitOfWork
{

    private bool _disposed;
    private DbContext _dbContext;
    private Dictionary<string, dynamic> _repositories;
    private DbContextTransaction _transaction;

    public DbContext DbContext
    {
        get { return _dbContext; }
    }

    public UnitOfWork(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public int SaveChanges()
    {
        return _dbContext.SaveChanges();
    }

    public IRepository<TEntity> Repository<TEntity>()
    {   
        try
        {
            if (ServiceLocator.IsLocationProviderSet)
                return ServiceLocator.Current.GetInstance<IRepository<TEntity>>();

            if (_repositories == null)
                _repositories = new Dictionary<string, dynamic>();

            var type = typeof(TEntity).Name;

            if (_repositories.ContainsKey(type))
                return (IRepositoryAsync<TEntity>)_repositories[type];

            var repositoryType = typeof(Repository<>);

            _repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), this));

            return _repositories[type];
        }
        catch(ActivationException ex)
        {
            throw new ActivationException(string.Format("You need to configure the implementation of the IRepository<{0}> interface.", typeof(TEntity)), ex);
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~UnitOfWork()
    {
        Dispose(false);
    }

    public void Dispose(bool disposing)
    {
        if(!_disposed)
        {
            if(disposing)
            {
                try
                {
                    _dbContext.Dispose();
                    _dbContext = null;
                }
                catch(ObjectDisposedException)
                {
                    //the object has already be disposed
                }
                _disposed = true;
            }
        }
    }
}

The Repository implementation:

public class Repository<TEntity> : IRepository<TEntity>
        where TEntity : class
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly DbContext _dbContext;
    private readonly DbSet<TEntity> _dbSet;

    public Repository(IUnitOfWork unitOfWork)
    {

        _unitOfWork = unitOfWork;
        _dbContext = unitOfWork.DbContext;
        _dbSet = _dbContext.Set<TEntity>();
    }

    #region IRepository<TEntity> implementation

    public void Insert(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    public void Update(TEntity entity)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
    }

    public void Delete(TEntity entity)
    {
        _dbSet.Remove(entity);
    }

    public IQueryable<TEntity> Queryable()
    {
        return _dbSet;
    }

    public IRepository<TEntity> GetRepository<TEntity>()
    {
        return _unitOfWork.Repository<TEntity>();
    }

    #endregion

}

The Unity configuration:

    container.RegisterType<DbContext, DbSittiusContext>(new PerRequestLifetimeManager());
    container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());

    //Catalog respository register types
    container.RegisterType<IRepository<Product>, Repository<Product>>();

    UnityServiceLocator locator = new UnityServiceLocator(container);
    ServiceLocator.SetLocatorProvider(() => locator);

To create my query have to create a extension method like this:

public static Product FindPublishedAtDateById(this IRepository<Product> repository, int id, DateTime date)
{
    return repository.
            Queryable().
            Where(p => p.Id == id).
            Where(p => p.PublishedFrom <= date && (p.PublishedTo == null || p.PublishedTo >= date)).
            SingleOrDefault();
}
FxmL
  • 170
  • 10
  • 2
    You can start by sharing what database you are using (oracle, access, sql server, etc). Then also share the translated query from EF in the database along with any database specific info like a query plan if its Sql Server. – Igor Jul 25 '16 at 19:36
  • `AsNoTracking` just means that EF will not keep the entity attached to the context and eliminates a lot of overhead if you're not updating anything. It has no bearing on the query that is executed. Sounds like you want to run the query `WITH NOLOCK`; in that case, http://stackoverflow.com/questions/926656/entity-framework-with-nolock – Paul Abbott Jul 25 '16 at 19:44
  • I'm pretty sure your problem is not related to EF and AsNoTracking method. When you execute select statement you put the shared lock on the table. It ensures that other operations that are changing data cannot be executing at the same time. In my opinion, web application is trying to change data in the table you are reading. Try to turn on sql profiler or use extended events to capture sql queries generated by EF (i assume that you use SQL Server). I don't recommend using WITH NOLOCK hint because it's really dangerous and can lead to big problems. – Bartek Falkowski Jul 25 '16 at 20:04
  • 1
    How do you know it's related to locking? Maybe the process takes 100% CPU. Maybe the database is out of resources. Your question doesn't give enough details to give you any sound advice. – Gert Arnold Jul 25 '16 at 20:57
  • 2
    @GertArnold - true, the OP could have a static `DbContext` somewhere and using that instead of creating a new `DbContext` when it is needed or in each request. That could certainly wreck the whole site and its only visible now that there happens to be a longer running db transaction. This is a very poor question in that there are no concrete details at all. – Igor Jul 25 '16 at 21:00
  • Post how your instantiating you `DbContext` and your connection string. – mxmissile Jul 25 '16 at 21:17
  • I'm instantiating the DbContext using Unity configuration. – FxmL Jul 27 '16 at 08:48

1 Answers1

0

If you're downloading a lot of data synchronously it will make the UI freeze up on you. Consider doing this asynchronously. What are you using client side, anyway?

I'm assuming you're generating an excel file from data in the database and it's just a matter of it being enough data that it takes ~5 seconds to create the file and send it to the user.

cthon
  • 111
  • 4
  • `What are you using client side, anyway?` - 1st sentence in post `I'm developing a web application using` **ASP.NET MVC** `and EF6 to access the database.`. Also `asp.net-mvc` is tagged in the question. – Igor Jul 25 '16 at 20:49
  • I meant how is the browser requesting this Excel file. AJAX calls? Just plain HTML? – cthon Jul 25 '16 at 20:54
  • How would that matter? The question is not why does it take so long its why do all requests on the web site stop working as soon as this database call is being executed. – Igor Jul 25 '16 at 20:58