Web Forms - Entity Framework Context Disposal - c#

I have a .NET 4 Web Application (Web Forms) that uses Entity Framework to manage data with a MySQL Database. For every Page, I create the context for this Model in the Page_Load.
string connString = ConfigurationManager.ConnectionStrings["dbconnection"].ToString();
MyModel = new MyEntities(connString);
Now, in subsequent actions on the Page I can use MyModel to retrieve and update data. This is clean and simple to me, but I always assumed .NET discarded the previous Page's MyModel when a new page request was made. I realize this may not be the case? and Memory may be being used inefficiently.
I have seen a good case being made for incorporating the using (MyEntities MyModel = new MyEntities (ConfigurationManager.ConnectionStrings["dbconnection"].ToString())) that handles disposal, but this does not seem clean if I have 6+ actions on a Page each needing to recreate the context when called (Not that my current method does any better).
Is there a clean way to create a context once on initial Page Load and dispose of it when a new page is called, non-postback is called, or the user's session ends?

You can override virtual Dispose method of System.Web.UI.Control and dispose of your context there:
public override void Dispose()
{
if (MyModel != null)
MyModel.Dispose();
base.Dispose();
}
Further, you can make MyModel into a property with the context created on demand:
private MyEntities fMyModel = null;
protected MyEntities MyModel
{
get
{
if (fMyModel == null)
{
string connString = ConfigurationManager.ConnectionStrings["dbconnection"].ToString();
fMyModel = new MyEntities(connString);
}
return fMyModel;
}
}
Then, in Dispose work with the field:
public override void Dispose()
{
if (fMyModel != null)
fMyModel.Dispose();
base.Dispose();
}
Furthermore, you can create a base Page class with the above property and Dispose override and inherit your pages from it - then you do not need to repeat this code in all your pages.

The easiest thing to do would be to add a call to Dispose() on your data context in the Unload event on the page.
protected void Page_Unload(object sender, EventArgs e)
{
MyModel.Dispose();
}
A better way that I think should be used as default for web applications (doesn't matter if it is WebForms or MVC is to create one data context per request and dispose it when the request ends. To do that easily you can use an IoC container framework such as Ninject.

Related

How to dispose the ContextDB class automatically after any query done using it

ASP.NET MVC 5 Project.
I know that the best practice of using EF context object as the following
using(var context = new ContextDB())
{
}
But I am working with a large existing project which not used this practice.
the project using the following pattern
public abstract class BaseService
{
private static ContextDB _data { get; set; }
public static ContextDB Data
{
get
{
if (_data== null)
_data= new ContextDB();
return _data;
}
}
}
Actually, because of this pattern, I am receiving this exception (sometimes, not always)
So to solve this I have to change all the code which is using the shared Data
property and replace it with the new instance of ContextDB as I mentioned in the beginning of the question.
The problem that this is a very large modification, and I will not be allowed to do that modification.
The Question, can I solve this problem without changing a ton of code, In another word, can I solve the problems with modifications done only inside the BaseService class, for example, Is there any event which I could handle to know if any query is executed and then dispose of the ContextDB
here is the pseudo-code of the idea in my mind
public abstract class BaseService
{
public static ContextDB Data
{
get
{
ContextDB _data= new ContextDB();
_data.SqlQueryExecuted += () => { this._data.dispose(); }
return _data;
}
}
}
NOTE: the SaveChanged event is not suitable, because not all of the query are updating or inserting.
I may use following solution.
In Global.asax
Begin Request : Create Instance of your dbContext. Store it in HttpContext.Current.Items.
End Request : Grab the context and close / dispose connection.
Another better solution is to use DI. Dependency Injection and limit the scope of your instance. There are many way Like Singleton, PerRequest etc.

Is this a bad way of using DbContext for EF CodeFirst?

I have one base controller class which has following DbContext. Instead of using "using" statement for each database work, can I rely on this. So far app runs as expected. I am not sure if Dispose part is really needed.
private static Context _database;
public static Context Db
{
get
{
if (_database == null)
{
_database = new Context();
}
return _database;
}
}
protected override void Dispose(bool disposing)
{
if (_database == null)
{
_database.Dispose();
}
base.Dispose(disposing);
}
No. not sure what class is being disposed of here but if it ever got disposed it would crash every time after that. you have to set _database = null after calling dispose or you would use it in a disposed state. if you want to do any type of multi threading this won't work. if more than 1 user is using this at same time it will crash. you won't be able to unit test the use of database with static either. It will cache all data you get causing stale data after a time too unless you are only user.
Having a automatic disposal of requests is very convenient don't make it a manual task.
Just don't use static in anyway, always using usingstatement for every request.

Console app with MVC, Ninject and WCF Service (Dispose issue?)

I have a MVC application with all Ninject stuff wired up properly. Within the application I wanted to add functionality to call a WCF service, which then sends bulk messages (i.e. bulk printing) to RabbitMQ queue .
A 'processor' app subscribes to messages in the queue and process them. This is where I also want to update some stuff in the database, so I want all my services and repositories from the MVC app to be available too.
The processor app implements the following:
public abstract class KernelImplementation
{
private IKernel _kernel;
public IKernel Kernel
{
get
{
if (_kernel != null)
return _kernel;
else
{
_kernel = new StandardKernel(new RepositoryModule(),
new DomainModule(),
new ServiceModule(),
new MessageModule());
return _kernel;
}
}
}
}
All Ninject repository bindings are specified within RepositoryModule, which is also used within MVC app and look like this:
Bind<IReviewRepository>().To<ReviewRepository>().InCallScope();
The processor class
public class Processor : KernelImplementation
{
private readonly IReviewPrintMessage _reviewPrintMessage;
public Processor()
{
_reviewPrintMessage = Kernel.Get<IReviewPrintMessage>();
[...]
_bus.Subscribe<ReviewPrintContract>("ReviewPrint_Id",
(reviewPrintContract) => _reviewPrintMessage.ProcessReviewPrint(reviewPrintContract));
//calling ProcessReviewPrint where I want my repositories to be available
}
}
Everything works fine until I update the database from the MVC app or database directly. The processor app doesn't know anything about those changes and the next time it tries to process something, it works on a 'cached' DbContext. I'm sure it's something to do with not disposing the DbContext properly, but I'm not sure what scope should be used for a console app (tried all sort of different scopes to no avail).
The only solution I can think of at the moment is to call WCF service back from the processor app and perform all the necessary updates within the service, but I would want to avoid that.
UPDATE: Adding update logic
Simplified ReviewPrintMessage:
public class ReviewPrintMessage : IReviewPrintMessage
{
private readonly IReviewService _reviewService;
public ReviewPrintMessage(IReviewService reviewService)
{
_reviewService = reviewService;
}
public void ProcessReviewPrint(ReviewPrintContract reviewPrintContract)
{
var review =
_reviewService.GetReview(reviewPrintContract.ReviewId);
[...]
//do all sorts of stuff here
[...]
_reviewService.UpdateReview(review);
}
}
UpdateReview method in ReviewService:
public void UpdateTenancyAgreementReview(TenancyAgreementReview review)
{
_tenancyAgreementReviewRepository.Update(review);
_unitOfWork.Commit();
}
RepositoryBase:
public abstract class EntityRepositoryBase<T> where T : class
{
protected MyContext _dataContext;
protected EntityRepositoryBase(IDbFactory dbFactory)
{
this.DbFactory = dbFactory;
_dbSet = this.DataContext.Set<T>();
}
[...]
public virtual void Update(T entity)
{
try
{
DataContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
throw new EntityException(string.Format("Failed to update entity '{0}'", typeof(T).Name), exception);
}
}
}
Context itself is bound like this:
Bind<MyContext>().ToSelf().InCallScope();
From the description of scopes I thought that Transient scope was the right choice, but as I said earlier I tried all sorts including RequestScope, TransientScope, NamedScope and even Singleton (although I knew it wouldn't be desired behaviour), but none of them seem to be disposing the context properly.
What you'll need is one DbContext instance per transaction.
Now other "applications" like web-applications or wcf-service may be doing one transaction per request (and thus use something like InRequestScope(). Also note, that these application create an object graph for each request. However, that is a concept unknown to your console application.
Furthermore, scoping only affects the instantiation of objects. Once they are instantiated, Scoping does not have any effect on them.
So one way to solve your issue would be to create the (relevant) object tree/graph per transaction and then you could use InCallScope() (InCallScope really means "once per instantiation of an object graph", see here).
That would mean that you'd need a factory for IReviewPrintMessage (have a look at ninject.extensions.factory) and create an instance of IReviewPrintMessage every time you want to execute IReviewPrintMessage.ProcessReviewPrint.
Now you have re-created the "per request pattern".
However, regarding CompositionRoot this is not recommended.
Alternative: you can also only re-create the DbContext as needed. Instead of passing it around everywhere (DbContext as additional parameter on almost every method) you use a SynchronizationContext local storage (or if you don't use TPL/async await: a ThreadLocal). I've already described this method in more detail here

Entity Framework and Web API ObjectDisposedException

I have a data layer that uses Entity Framework 5 to connects to Sql Server. I also have a LINQ query that gets some data. This query fails when used with Web API. I get the ObjectDisposedException. Here's the query:
using (MyContext container = new myContext())
{
return container.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
}
The data layer is a dll, exposed by a business layer, also a dll, which is called by a web api controller. I had assumed that the JSON serializer was lazy loading the includes but none of my fixes have worked. Any ideas? I will update the question as need be if info is missing.
Here is the business layer call to the data layer:
public Sale GetSale(int id)
{
SaleRepository s = new SaleRepository();
return s.GetSale(id);
}
And finally the web api call to the business layer:
public Sale GetSale(int id)
{
SaleManager s = new SaleManager();
return s.GetSale(id);
}
Here is the exception:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
This happens because lazy loading is being performed after your DbContext has been disposed.
To resolve the issue you can disable lazy loading by doing this:
container.Configuration.LazyLoadingEnabled = false;
There must be an issue with lazy loading and the include not working as expected - try changing your code to select the actual object and the included entities.
using (MyContext container = new myContext())
{
var result = container
.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
result.Products.ToList();
return result;
}
This is an issue with lazy loading. The more appropriate way to do this is to not dispose the DbContext till the request is done processing. You can do it in a couple of ways,
1) dispose it in the controller. framework ties the controller's life time with the requests life time.
public class MyController : ApiController
{
private SaleManager _saleManager = new SaleManager();
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_saleManager.Dispose();
}
}
}
2) explicitly tie the resource with the request, (useful in cases where you are not inside a controller) using Request.RegisterForDispose()
SaleManager saleManager = new SaleManager();
Request.RegisterForDispose(saleManager);

Entity Framework - Load Reference Keys after disposing entity object context

I am using ASP.Net / WebForms / Entity Model / Framework 3.5
Here is my project's simple structure
Forms > BLL > DAL ( uses entity model )
Here is my DAL's snippet
public class MyDAL : IDisposable
{
private MyEntities db;
public BaseDAL()
{
db = new MyEntities();
}
public User GetUserByID(int userId)
{
try
{
IQueryable<User> objUser = null;
objUser = from res in db.Users
where res.UserId == userId
select res;
return objUser.FirstOrDefault();
}
catch
{
throw;
}
}
public void Dispose()
{
db.Dispose();
}
}
I call the DAL's function from my BLL like this
public class MyBLL
{
public User GetUserByID(int userId)
{
try
{
using (MyDAL objMyDAL = new MyDAL())
{
return objMyDAL.GetUserByID(userId);
}
}
catch
{
throw;
}
}
}
I am calling the DAL through using block so MyDAL's Dispose event will fire soon after the BLL returns the User object. So at this point ObjectContext instance gets disposed.
Now in my Web Form, I am calling this function like this to get user information and Group details which is a foreign key of user_Group Table in User table
protected void Page_Load(object sender, EventArgs e)
{
MyBLL objMyBll = new MyBLL();
User objUser = objMyBll.GetUserByID(123);
objUser.User_GroupReference.Load(); // ERROR LINE
int groupId = objUser.User_Group.Group_Id;
}
When the ode comes on line objUser.User_GroupReference.Load(); I get this exception
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
How to resolve this? If I do not do db.Dispose(); in my DAL's dispose method, it works fine and no exceptions comes. But if I do not dispose the db object there, when & where should I dispose it?
And how to access Reference Keys after disposed object context?
The exception is fired because lazy loading is fired when you access that navigation property but lazy loading works only within scope of context used to load the entity. If you dispose the context you will lose lazy loading ability. There is no way to use lazy loading after context disposal (except attaching the entity to the new context but it will only work if you detach it from the original context before you dispose it).
In your architecture you must use Include to explicitly load every relation you will need in upper layers. If you want to use lazy loading your context must live for the whole duration of the request. In case of web forms it can be handled for example in BeginRequest and EndRequest event handlers where you create context in BeginRequest and dispose it in EndRequest. The context will be stored in HttpContext.Items. You should get the context from this collection (you can make helper method for that) and pass it to constructor of BLL which will in turn pass it to DAL. Don't access HttpContext.Items from BLL or DAL.

Categories

Resources