I'm using entity framework 6 and Autofac in my web application.
I inject unit of work with DbContext inside, both externally owned so I can dispose them myself.
DbContext registered PerLifetimeScope,
Unit of work is a factory, therefore registered as per dependency.
When Executing the first http Get action everthing works fine and I see the unit of work with the context are disposed after the response is coming from the db which is great.
My issue is that whenever I execute a second request, the context for some reason is disposed before I return an IQueryable. Therefore I get an execption saying:
The operation could not be executed because the DbContext is disposed.
For example - calling the GetFolders method works the first time, and afterwards fails..
I see the context is disposed too early, what I don't understand is what triggers it too soon in the second request..
public interface IUnitOfWork : IDisposable
{
bool Commit();
}
public EFUnitOfWork : IUnitOfWork
{
public IRepository<Folder> FoldersRepository {get; set;}
public IRepository<Letter> LettersRepository {get; set;}
private readonly DbContext _context;
public EFUnitOfWork(DbContext context, IRepository<Folder> foldersRepo, IRepository<Letter> lettersRepo)
{
_context = context;
_foldersRepo = foldersRepo;
LettersRepository = lettersRepo;
}
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_context.Dispose();
}
disposed = true;
}
}
public bool Commit()
{
try
{
return SaveChanges() > 0;
}
catch (DbEntityValidationException exc)
{
// just to ease debugging
foreach (var error in exc.EntityValidationErrors)
{
foreach (var errorMsg in error.ValidationErrors)
{
logger.Log(LogLevel.Error, "Error trying to save EF changes - " + errorMsg.ErrorMessage);
}
}
return false;
throw exc;
}
}
}
public class Repository<T> : IRepository<T>
{
protected readonly DbContext Context;
protected readonly DbSet<T> DbSet;
public EFRepository(DbContext context)
{
Context = context;
}
public IQueryable<T> Get()
{
return DbSet;
}
public void Add(T item)
{
DbSet.Add(item);
}
public virtual Remove(T item)
{
DbSet.Remove(item);
}
public void Update(T item)
{
Context.Entry(item).State = EntityState.Modified;
}
public T FindById(int id)
{
return DbSet.Find(id);
}
}
public class DataService : IDataService
{
private Func<IUnitOfWork> _unitOfWorkFactory;
public (Func<IUnitOfWork> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public List<FolderPreview> GetFolders()
{
using(unitOfWork = _unitOfWorkFactory())
{
var foldersRepository = unitOfWork.FoldersRepository;
var foldersData = foldersRepository.Get().Select(p => new FolderPreview
{
Id = p.Id,
Name = p.Name
}).ToList();
return foldersData;
}
}
}
public class FolderPreview
{
public int Id {get; set;}
public string Name {get; set;}
}
Startup code:
{
_container.RegisterGeneric<IRepository<>,Repository<>>().InstancePerLifetimeScope();
_container.RegisterType<IDataService, DataService>().SingleInstance();
_container.RegisterType<EFUnitOfWork, IUnitOfWork>().PerDepnendecny().ExternallyOwned();
_container.RegisterType<DbContext, MyDbContext>().InstancePerLifetimeScope().ExternallyOwned();
}
Is this related to singletons some how? Almost all of my application is singletons, the DataService is also Singleton. Anyone?
Thanks!
The problem is that you are instancing only one Repository and one DbContext per request, but are instancing one new IUnitOfWork every time.
So when you call GetFolders you are creating a new IUnitOfWork and disposing it (which disposes the DbContext -on IUnitOfWork.Dispose()-): so when you call GetFolders again, when you create a second IUnitOfWork, since it's the same lifetime scope, it's injecting the already-created repository and the already-created DbContext, which is disposed (the container doesn't try to create a new instance since you are on the same lifetime scope)...
So on the second call, your Repository and IUnitOfWork are trying to use the disposed instance of DbContext, thus the error you are seeing.
As a solution, you can just not dispose the DbContext on IUnitOfWork, and dispose it only at the end of your request... or you could even not dispose it at all: this may sound strange, but check this post
I'm copying the important part in case the link goes dead, by Diego Vega:
The default behavior of DbContext is that the underlying connection is automatically opened any time is needed and closed when it is no longer needed. E.g. when you execute a query and iterate over query results using “foreach”, the call to IEnumerable.GetEnumerator() will cause the connection to be opened, and when later there are no more results available, “foreach” will take care of calling Dispose on the enumerator, which will close the connection. In a similar way, a call to DbContext.SaveChanges() will open the connection before sending changes to the database and will close it before returning.
Given this default behavior, in many real-world cases it is harmless to leave the context without disposing it and just rely on garbage collection.
That said, there are two main reason our sample code tends to always use “using” or dispose the context in some other way:
The default automatic open/close behavior is relatively easy to override: you can assume control of when the connection is opened and closed by manually opening the connection. Once you start doing this in some part of your code, then forgetting to dipose the context becomes harmful, because you might be leaking open connections.
DbContext implements IDiposable following the recommended pattern, which includes exposing a virtual protected Dispose method that derived types can override if for example the need to aggregate other unmanaged resources into the lifetime of the context.
So basically, unless you are managing the connection, or have a specific need to dispose it, it's safe to not do it.
I'd still recommend disposing it, of course, but in case you don't see where it'd be a good time to do it, you may just not do it at all.
Related
I have a windows forms application where I am trying to use dependency injection for some services, so I did the following configuration initially in Program.cs I register the services:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var services = new ServiceCollection();
ConfigureServices(services);
using (ServiceProvider serviceProvider = services.BuildServiceProvider())
{
var mainForm = serviceProvider.GetRequiredService<SelecionarEmpresa>();
Application.Run(mainForm);
}
}
private static void ConfigureServices(ServiceCollection services)
{
services.AddDbContext<AnalistDbContext>();
services.AddSingleton<MainForm>();
services.AddScoped<Form1>();
services.AddScoped<Form2>();
services.AddScoped<Form3>();
services.AddTransient<IEmpRepository, EmpRepository>();
services.AddTransient<ISisRepository, SisRepository>();
}
}
So far everything working, there are 3 Forms I made to test the functionality, in Form1 I inject the services I need:
private readonly ISisRepository _sisRepository;
private readonly IEmpRepository _empRepository;
public Form1(ISisRepository sistRepository,
IEmpRepository empRepository)
{
_sisRepository= sistRepository;
_empRepository = empRepository;
InitializeComponent();
}
And the idea is to use, for example, _sisRepository to update a record, the first time I save it works, if I click to save again, an exception is raised, before putting the exception I already inform you that I am using a generic repository, which is the next:
public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity, new()
{
protected readonly AnalistDbContext Db;
protected readonly DbSet<TEntity> DbSet;
protected Repository(AnalistDbContext db)
{
Db = db;
DbSet = db.Set<TEntity>();
Db.ChangeTracker.AutoDetectChangesEnabled = false;
var existeBanco = (Db.Database.GetService<IDatabaseCreator>() as RelationalDatabaseCreator).Exists();
if (!existeBanco)
{
(Db.Database.GetService<IDatabaseCreator>() as RelationalDatabaseCreator).Create();
(Db.Database.GetService<IDatabaseCreator>() as RelationalDatabaseCreator).CreateTables();
}
}
public IEnumerable<TEntity> Buscar(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate).AsNoTracking().ToList();
}
public virtual TEntity ObterPorId(Guid id)
{
return DbSet.AsNoTracking().FirstOrDefault(s => s.Id == id);
}
public virtual List<TEntity> ObterTodos()
{
return DbSet.AsNoTracking().ToList();
}
public void Adicionar(TEntity entity)
{
DbSet.Add(entity);
SaveChanges();
}
public void Atualizar(TEntity entity)
{
DbSet.Update(entity);
SaveChanges();
}
public void Remover(Guid id)
{
DbSet.Remove(new TEntity { Id = id });
SaveChanges();
}
public int SaveChanges()
{
return Db.SaveChanges();
}
public void Dispose()
{
Db?.Dispose();
}
}
The exception is:
Despite the clarity of the message, I could not understand if this is a problem caused by the use of dependency injection in the Form's constructor and having caused some problem in relation to the repository instance, it seems that my object is still the same as instantiated earlier, because as I said, this error only occurs on the second call. If that is really the problem, how could you solve it? Or when it comes to windows forms, would I abandon the use of dependency injection?
When you update an item, the DbContext starts tracking that item. When you do a second update, the same DbContext tries to track another item with the same id. It's not an issue with the dependency injection, it's an issue with the lifetime of the injected AnalistDbContext and the objects tracked by it.
Typically, a DbContext would be instantiated on a per-operation basis, instead of living for the duration of the entire form. This helps prevent inconsistencies when two users are trying to modify the same entity. You could try:
Update Form1 to use a factory that can create repositories, and dispose the repository after using it
Update Repository to use a factory that can create an AnalistDbContext, and dispose the context after using it
If you think you're going to be the only user updating the entry, and aren't worried about race conditions or anything like that, you can check if the entry is already being tracked by the db context before trying to attach it.
Let's say i've a DbContextFactory which I use in repositories to get DbContext
(I'm not sure it'a the best solution).
public class DbContextFactory : Disposable, IDbContextFactory
{
private readonly Dictionary<Type, System.Data.Entity.DbContext> _dbContexts;
public DbContextFactory()
{
_dbContexts = new Dictionary<Type, System.Data.Entity.DbContext>();
}
public T GetDbContext<T>() where T : System.Data.Entity.DbContext, new()
{
if (!_dbContexts.ContainsKey(typeof(T)))
{
_dbContexts.Add(typeof(T), new T());
}
return _dbContexts[typeof(T)] as T;
}
protected override void DisposeCore()
{
foreach (var kvpDbContext in _dbContexts)
{
kvpDbContext.Value?.Dispose();
}
}
}
And i have UnitOfWork which i inject in BusinessLogic class
public class UnitOfWork<T> : IUnitOfWork
where T : System.Data.Entity.DbContext, new()
{
private readonly IDbContextFactory _dbContextFactory;
private T _dbContext;
public UnitOfWork(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public T DbContext => _dbContext ?? (_dbContext = _dbContextFactory.GetDbContext<T>());
public void Commit()
{
DbContext.SaveChanges();
}
}
than i call repository method, and let's say it throws an exception:
public void CreateUser(User user)
{
_userRepository.Add(user);
throw new Exception();
UnitOfWork.Commit();
}
what happend if i call other repository method in the same request (or just do not use factory as instance-per-request), and that method end successfully, and UnitOfWork.Commit() will be called and does it means that changes made in CreateUser method which failed will be saved too ? or just after throwing exception the connection close and there's no risk the changes from that method will be saved?
To make it more clear:
I want to host that in WCF service, let's say in singleton mode.
And then - one request call method which contains multiple (for example 5) repository calls, and first three will success and the fourth will fail, it means i won't call UnitOfWork.Commit() there.
And then other request come, and it's just success. Does it mean, changes from first three repositories calls from previous method will be saved?
Because of singleton - there'll be still the same DbContextFactory sa the same DbContext.
As #Igor said in the comments, whether you handled or not that exception makes a difference, but as your scenario involves calling anothet repository on the same request, I'll assume you did handled It.
As you said you would reuse the same instance of your DbContextFactory and that's the guy holding your DbContext instances, it's safe to say that, unless you disposed that factory somewhere else, your context would still have that same instance of User added to it and thus calling Commit on that same context on anothet repository would still insert said User.
I'm having two problems, the first is scaling and became visible while load testing.
Under load, things quickly (10 concurrent or less) fail with the error:
There is already an open DataReader associated with this Command which must be closed first.
Or
ExecuteReader requires an open and available Connection. The connection's current state is open.
The stack trace references a repository every time, example:
Line 23: public AboutViewDto GetAboutView()
Line 24: {
Line 25: var featured = UnitOfWork.FaqRepository
Line 26: .GetFeatured()
Original NinjectDependencyResolver:
public class NinjectDependencyResolver
: IDependencyResolver, System.Web.Mvc.IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver() : this(new StandardKernel())
{
}
public NinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
AddBindings(_kernel);
}
public IDependencyScope BeginScope()
{
return this;
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType);
}
public void Dispose()
{
// nothing??
}
private static void AddBindings(IBindingRoot kernel)
{
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InSingletonScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InSingletonScope();
kernel.Bind<IFaqRepository>().To<FaqRepository>().InSingletonScope();
kernel.Bind<IListValueRepository>().To<ListValueRepository>().InSingletonScope();
kernel.Bind<INoticeRepository>().To<NoticeRepository>().InSingletonScope();
kernel.Bind<IOrganizationRepository>().To<OrganizationRepository>().InSingletonScope();
kernel.Bind<ITagRepository>().To<TagRepository>().InSingletonScope();
kernel.Bind<IAdminService>().To<AdminService>();
kernel.Bind<IAutoMapperService>().To<AutoMapperService>();
kernel.Bind<IHomeService>().To<HomeService>();
kernel.Bind<IInfoService>().To<InfoService>();
kernel.Bind<IMailService>().To<MailService>();
kernel.Bind<INoticeService>().To<NoticeService>();
kernel.Bind<IOrganizationService>().To<OrganizationService>();
kernel.Bind<ISearchService>().To<SearchService>();
kernel.Bind<IValidator<QuestionDto>>().To<QuestionDtoValidator>();
kernel.Bind<IValidator<NoticeCommentDto>>().To<CommentDtoValidator>();
kernel.Bind<IValidator<NoticeContactDto>>().To<NoticeContactDtoValidator>();
kernel.Bind<IValidator<NoticeDto>>().To<NoticeDtoValidator>();
kernel.Bind<IValidator<OrganizationDto>>().To<OrganizationDtoValidator>();
}
}
}
I had a hunch that InSingletonScope() was causing the problem so I changed it to:
kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InRequestScope();
And changed all of the other SingletonScopes to RequestScope.
After making that change, the site handles 400+ concurrent users without any failures, however...
Now no commits work against the database. I can trace the calls through the controller, to the service, to the repository, and to the DBContext commit, but the inserts or updates are not happening.
I'll post snippets of each item here in hopes someone can spot the dumb error we've made or suggest improvements.
Snippets follow:
Activity, update a Notice, everything involved:
1) Ninject:
kernel.Bind<IDataContext>().To<PublicCommentDbContext>().InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
kernel.Bind<INoticeService>().To<NoticeService>();
.. etc...
2) Controller:
public sealed class NoticeController : BaseController
{
public NoticeController(IAutoMapperService autoMapperService, INoticeService noticeService)
{
AutoMapperService = autoMapperService;
NoticeService = noticeService;
}
...
3) NoticeService
public class NoticeService : BaseService, INoticeService
{
public NoticeService(
IUnitOfWork unitOfWork,
IAutoMapperService autoMapperService,
IValidator<NoticeDto> noticeDtoValidator)
: base(unitOfWork)
{
AutoMapperService = autoMapperService;
NoticeDtoValidator = noticeDtoValidator;
}
4) Unit of Work
public class UnitOfWork : IUnitOfWork
{
private IDataContext _dataContext;
private bool _disposed;
private ObjectContext _objectContext;
private DbTransaction _transaction;
public UnitOfWork(
IDataContext dataContext,
INoticeRepository noticeRepository)
{
_dataContext = dataContext;
NoticeRepository = noticeRepository;
}
5) Notice Repository
public class NoticeRepository : Repository<Notice>, INoticeRepository
{
public NoticeRepository(IDataContext context) : base(context)
{
}
...
6) Controller Action
public ActionResult Create(NoticeViewModel notice)
{
notice.TypeId = Convert.ToInt32(NoticeType.ManuallyEnteredDocument);
var newNotice = AutoMapperService.Map<NoticeViewModel, NoticeDto>(notice);
NoticeService.Create(newNotice);
return RedirectToAction("Details", new { name = notice.Arc });
}
7) NoticeService.Create(new):
public void Create(NoticeDto notice)
{
NoticeDtoValidator.ValidateAndThrow(notice);
var newNotice = AutoMapperService.Map<NoticeDto, Notice>(notice);
UnitOfWork.NoticeRepository.Add(newNotice);
UnitOfWork.SaveChanges();
}
8) Generic Repository Add():
public virtual void Add(TEntity entity)
{
entity.ObjectState = ObjectState.Added;
_dbSet.Attach(entity);
_context.SyncObjectState(entity);
}
9) UnitOfWork SaveChanges():
public int SaveChanges()
{
return _dataContext.SaveChanges();
}
When I step through this, the entity that is attached in #8 looks right, the status is added, when save changes is called there are no changes recorded in the base SaveChanges():
public override int SaveChanges()
{
SyncObjectsStatePreCommit();
base.ChangeTracker.DetectChanges();
var result = base.ChangeTracker.HasChanges();
var changes = base.SaveChanges();
result here is false
How could that be? What am I missing, other than abstraction hell and UoW/Repos on top of EF6 context, (I inherited this).
Someone out there see anything ELSE dumb?
No errors, no exceptions, the inserts just don't happen.
Problem solved, the issue was that Ninject was injecting a different context into our repositories.
We found two ways to solve this:
Option 1) In our unit of work we created an additional constructor that just receives a dbcontext and creates new repositories passing them the dbcontext. We left the old constructor as well so we can inject the repos for testing. This worked great and was a trivial change to make.
Option 2) Removed Ninject and overloaded the constructors for each Controller, manually creating the items. Since this project was small, we only had to touch 6 controllers and it worked fine, just 4 new lines of code in each. We left the old constructor as well so we can inject the services for testing.
I'm writing a sync service between our Salesforce environment and our local environment. My use of the Salesforce API is purely on a batch level due to limitations on # of API requests per day, although I do have details on failures at the atomic level. However, I would like to save changes on a atomic level in my local environment as I don't want an entire transaction to fail if one entity fails.
I am using Entity Framework 6 with a Unit of Work and Repository pattern. Here is my relevant code (Implementation Details Below):
IUnitOfWork
public interface IUnitOfWork: IDisposable
{
IReadUpdateRepository<EntityType1> EntityType1Repository { get; }
ISyncRepository<EntityType2> EntityType2Repository { get; }
ISyncRepository<EntityType3> EntityType3Repository { get; }
...other repos
void SaveChanges();
}
Implementation of IUnitOfWork
public class UnitOfWork : IUnitOfWork
{
private bool _isDisposed;
private DbContext _context;
private ISyncRepository<Entity> _entityRepo;
...other private repos
public UnitOfWork(DbContext context)
{
_context = context;
}
public ISyncRepository<Entity> EntityRepository
{
get
{
if (_entityRepo == null)
_entityRepo = new GenericSyncRepository<Entity>(_context);
return _entityRepo ;
}
}
...other getters for other repos
public void SaveChanges()
{
//client classes handle all errors here
_context.SaveChanges();
}
private void dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
_context.Dispose();
}
_isDisposed = true;
}
public void Dispose()
{
dispose(true);
}
}
ISyncRepository
public interface ISyncRepository<T> where T : BaseEntity
{
void DeleteItems(IEnumerable<T> items);
void DeleteItemsById(IEnumerable<int> ids);
void DeleteItem(T item);
void InsertItems(IEnumerable<T> items);
void Insert(T item);
T GetItemById(int id);
List<T> GetItems(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "");
}
Implementation of ISyncRepository
public class GenericSyncRepository<T> : ISyncRepository<T> where T : BaseEntity
{
private readonly DbContext _context;
private readonly DbSet<T> _set;
public GenericSyncRepository(DbContext context)
{
_context = context;
_set = _context.Set<T>();
}
public T GetItemById(int id)
{
var result = _set.Find(id);
return result;
}
public List<T> GetItems(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>,IOrderedQueryable<T>> orderBy = null ,string includeProperties = "")
{
IQueryable<T> query = _set.AsExpandable();
if (predicate != null)
{
query = query.Where(predicate);
}
if (!String.IsNullOrEmpty(includeProperties))
{
var splitProps = includeProperties.Split(',');
foreach (var prop in splitProps)
{
query = query.Include(prop);
}
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
return query.ToList();
}
public void DeleteItemsById(IEnumerable<int> ids)
{
var items = ids.Select(i => _set.Find(i));
DeleteItems(items);
}
public void DeleteItem(T item)
{
_set.Remove(item);
}
public void DeleteItems(IEnumerable<T> items)
{
_context.Set<T>().RemoveRange(items);
}
public void Insert(T item)
{
_set.Add(item);
}
public void InsertItems(IEnumerable<T> items)
{
_set.AddRange(items);
}
public List<T> GetViewItems(Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _set.AsExpandable();
return query.Where(predicate).ToList();
}
}
I am using this repository in services that relate to each object group involved in the sync, into which I inject the IUnitOfWork via the constructor:
private readonly IUnitOfWork _uow;
public EntityDataService(IUnitOfWork uow, ICamsSfDTOMapper mapper)
{
_uow = uow;
}
Then use the UoW to fetch repositories to performs queries:
var repo = _unitOfWork.PartyRepository;
var p = repo.GetItems(p => Ids.Contains(someValue));
And when inserts or updates are made, I can call SaveChanges:
_unitOfWork.SaveChanges();
This works great for data retrieval, however for data persistence, I run into a snag. On the local domain side, I want to iterate through objects one by one, saving changes or inserting and calling SaveChanges to persist. If an error occurs, I store it in a result object that I log at the end of each sync step and continue onto the next object to do work on.
However, as my app is currently structured, if a database exception occurs on SaveChanges, that validation error remains in the context until it is disposed. Since this context is injected into each repository in the UoW class, it sticks around for what I presume is the life of the entire sync process.
I am using Autofac for DI in a WindowsService hosted using TopShelf and I'm registering my UoW as such:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().WithParameter("context", new EntityModel());
Here are my questions:
Should this question be posted to Code Review :)
I feel like I should invert my repository with UoW, with UoW being passed into each repository and my repositories being passed into each service (a service can make use of more than one repository)
Does it make sense to spin up a new DbContext for each SaveChanges and to have one DbContext (the one passed into UoW) as my read context and all other contexts new'ed up when SavingChanges?
Any help would be appreciated.
Doesn't bother me, I only use SO and don't intend to go anywhere else, and don't mind having questions like these on SO.
I would highly recommend that your service as for IRepository and your repository ask for IUoW. I even like to think of IRepository as more than just a barrier to EF. I normally only use a repository if either
I know for a fact in advance I will be implementing a non EF repository as well otherwise
I want to implement some actual logic which I want abstracted from my service and would not make it generic. E.g. I would make a UserProfileRepository, which would have methods such as SyncCurrentUserProfile rather than exposing Add/Insert/Update. Exposing Add/Insert/Update adds nothing to the equation if I don't plan to use a non EF based model.
It depends. If you don't use tracking, and have a lot of unrelated changes over time. Than yes, as each time you add/update something it will add it to the context, which causes future additions/modifications become slower. But if you are adding/modifying 10 things here and there, probably wouldn't worry about it not unless you need a fresh context each time.
I am actually writing a blog post about this. Will try to post a link later.
I'm using a very simple asp.net mvc application with Entity Framework 6.0.2, .Net 4.5.1:
public class HomeController : Controller
{
public ActionResult Index()
{
int count;
using (var db = new LocalContext())
{
count = db.Counters.Count();
}
return View(count);
}
}
public class Counter
{
public int Id { get; set; }
}
public class LocalContext : DbContext
{
public DbSet<Counter> Counters { get; set; }
}
If I do a load test on it, I eventually get an Out Of Memory Exception. (tinyget -srv:localhost -port:<port> -uri:/home/index/ -threads:30 -loop:5000). In Performance monitor I see the generation 2 Heap steadily grow. If I use a smaller loop value (say 500), the size grows until tinyget stops. Then the heap size stays the same (for at least 20 minutes, after that I stopped the server).
What am I doing wrong?
EDIT
So I tried Simon Mouriers suggestion and left out the EF code. Then I don't have memory problems. So I thought, maybe if I use Release instead of Debug, it will make a difference. And it did! Memory was released after a while and I could put high load on the site. Then I switched back to Debug to see if I could get more info and... even in Debug mode no problems anymore. FML, I worked a day on it and now I can't reproduce it anymore.
In your case the internally managed class that inherits from DbContext would then need to implement IDisposable and inside of the LocalContext add the following:
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Manage any native resources.
}
//Handle any other cleanup.
}
Without specifically overriding the call to dispose, the using statement is only going to call Dispose() against the base class, while you need to dispose of the parent and base.
I don't see anything wrong with your code. Maybe this could be an issue with the underlying ADO.NET provider. Which database are you using?
I remember having issues with some unit test that did not release SQLite database files which I eventually solved with this code (in my DbContext class)
public class LocalContext : DbContext
{
protected override void Dispose(bool disposing)
{
var connection = this.Database.Connection;
base.Dispose(disposing);
connection.Dispose();
}
}
May be unrelated but I would give it a try.
This might not be the correct answer, but i suggest to keep your context managed by a IoC container. And add it with TrasientScope, or PerHttpRequest scope(example not provided due to a large varierty of ioc container syntax). If you want a specfic example, please reply for what DI you want
I would go for creating a class connection to the DB ..
public class DBconnection : IDisposable
{
private ChatEntities _db = new ChatEntities();
protected ChatEntities Db {
get { return _db; }
}
public void Dispose()
{
if (_db != null)
{
_db.Dispose();
}
}
}
Then when you would like to connect and manipulate .. Lets call it the DBlogic class ..
public class DBlogic : DBconnection
{
internal void WriteToDB(String str){
//Do something ...
Db.SaveChanges();
}
}
This will eventually cause the Dispose to empty resources.. plus its cleaner .. at least for my eyes :D
Actually, the OutOfMemotyException is normal in this situation since the Garbage Collector does not occur immediately after you've finished with the object. In this scenario, you need to use GC.Collect() to perform a collection on all generations of memory and reclaim all memory that is inaccessible, immediately.
public class HomeController : Controller
{
public ActionResult Index()
{
int count;
using (var db = new LocalContext())
{
count = db.Counters.Count();
}
GC.Collect();
return View(count);
}
}
Note that you should not use GC.Collect() in production code since it interferes with the Garbage Collection mechanism.