"ObjectContext has been disposed" /multi-controller view - c#

#foreach (Thing thing in Model) {
#Html.Action("someAction", "someOtherController", thing)
//kind of a PartialView but coming from another controller
}
-
public class someOtherController: Controller
{
public PartialViewResult someAction(Thing Model)
{
...
}
When this Html.Action is getting called I get The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
This is an error I'm usually able to fix using .Include() in the appropriate place in the code.
But in this case:
On debugging it doesn't look like "thing" or included sub-entities have been disposed.
I can put a breakpoint on #Html.Action("someAction", "someOtherController", thing) but if I put a breakpoint on the first line of someAction(...) method, it is never reached.
I have another page using such kind of partial view and it works perfectly
if I generate the content in the place of #Html.Action("someAction", "someOtherController", thing) instead of calling it through the partial view, it works perfectly. -
So there must be something wrong between that call and the controller, but I can't point out what. Any idea how I could debug this further and/or solve the problem?
Somebody asked for the query:
Data access layer :
public static List<Thing> GetAllThings(string[] Include = null)
{
using (Entities ctx = new Entities())
{
if ((Include != null) && (Include.Length > 0))
{
System.Data.Entity.DbSet<Thing> things= ctx.Things;
System.Data.Entity.Infrastructure.DbQuery<Thing> thingQuery= things.Include(Include[0]);
for (int i = 1, c = Include.Length; i < c; i++)
thingQuery= thingQuery.Include(Include[i]);
return thingQuery.ToList();
}
return ctx.Things.ToList();
}
}
In controller:
public ActionResult Index()
{
string[] include = new string[] { "Stuff1.Stuff2", "Stuff4.Stuff5.Stuff6", "Stuff7.Stuff8" };
List<Things> things = ThingsManager.GetAllThings(include).OrderBy(x => x.Name).ToList();
return this.View(things);
}

The exception message explains it all: your database context is falling out of scope before something is trying to enumerate a lazy-loaded related entity.
Check to make sure that you are eager-loading all your related entities that this block of code operates on:
#foreach (item dto in items) {
#Html.Action("someAction", "someOtherController", item) //kind of PartialView but coming from another controller

Related

Force DBContext refresh after Create/Edit ASP.NET MVC EF 6.1

I have a website that is using ASP.NET MVC 5 with EF6.1 and I'm having a problem with it showing the old data after an Edit procedure. The data is saved to the database properly, but when redirecting to the Index view it still shows old data.
If I go back to the Edit view, it still shows the old data also. The DBContext does not seem to be refreshing the data.
I have a base controller that holds the DBContext if that matters.
Here's the code for the Edit views in my controller:
public ActionResult FeaturedItemEdit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
using (nfmContext localContext = new nfmContext())
{
List<FeaturedItem> fi = localContext.FeaturedItems.AsNoTracking().ToList();
FeaturedItem featuredItem = fi.Find(x => x.ID.Equals(id));
if (featuredItem == null)
{
return HttpNotFound();
}
return View(featuredItem);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult FeaturedItemEditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
using (nfmContext db = new nfmContext())
{
var item = db.FeaturedItems.Find(id);
if (TryUpdateModel(item, "", new string[] { "ID", "Title", "ImageAlt", "ImagePath", "Link", "Visible", "Content" }))
{
try
{
item.TimeStamp = DateTime.Now;
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("FeaturedItems");
}
catch (DataException ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
}
return View(item);
}
}
Once the edit is done, it goes back to the Featured Items view which just loads a list of items:
public ActionResult FeaturedItems()
{
using (nfmContext localContext = new nfmContext())
{
return View(localContext.FeaturedItems.OrderBy(x => x.Visible).ThenBy(x => x.Title).AsNoTracking().ToList());
}
}
Nothing fancy there. At first I thought it was because the calls were all async but in changing it back to non-asynchronous it didn't make much of a difference. I haven't posted the code to create an item, however it has the same issue.
I've even decorated my controller with [OutputCache(Location = System.Web.UI.OutputCacheLocation.None)] to make sure it wasn't a client side caching issue.
So, am I doing something wrong or is there a way to force the DBContext to pull fresh records from the database?
UPDATE 1:
Here's the line that initializes my DBContext in the base controller:
<strike>protected nfmContext db = new nfmContext();</strike>
This is no longer used - see update 2
And here's my context setup:
public class nfmContext : DbContext
{
public nfmContext() : base("connectionStringHere")
{
}
public DbSet<FeaturedItem> FeaturedItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
base.OnModelCreating(modelBuilder);
}
}
UPDATE 2: I've switched the DBContect to now be local to each method. The code above reflects the changes and I've removed the DBContect property from the base controller.
Always use your dbContext only as long as required. Usually use a using statement within a method.
Reopening a connection to the database doesn't take long due to connection pooling.
Found out the issue after digging through and searching the project for various caching implementations.
At some point, EntityFramework.Cache was setup in the project (or EFCache as it's referred). That was causing the caching issue even though it was set up based on their documentation.
SaveChanges is supposed to be done in a transaction (which I assume is the case) and EFCache is supposed to watch for transactions and refresh the cache. Somewhere with the two there is a break down where the cache is not getting expired.
For reference, here's what I was using to implement the Secondary caching for the framework:
public class Configuration : DbConfiguration
{
public Configuration()
{
var transactionHandler = new CacheTransactionHandler(new InMemoryCache());
AddInterceptor(transactionHandler);
var cachingPolicy = new CachingPolicy();
Loaded +=
(sender, args) => args.ReplaceService<DbProviderServices>(
(s, _) => new CachingProviderServices(s, transactionHandler,
cachingPolicy));
}
}
Once I've removed this code from the project, the system worked perfectly as expected, but that also removed the feature of having extra caching.
In order to get it to work and give me the ability to purge the DbSet on demand, I switched the declaration of the CacheTransactionHandler to use a static InMemoryCache. Then, once it was set that way, I could use InvalidateDbSets to remove the item from the Memory Cache.
Here's what I did specifically:
Added this to my DbContext:
public static readonly EFCache.InMemoryCache Cache = new EFCache.InMemoryCache();
Changed the transactionHandler declaration in the Configuration sub to:
var transactionHandler = new CacheTransactionHandler(nfmContext.Cache);
After any .SaveChanges calls, I added:
nfmContext.Cache.InvalidateSets(new List<string>() { "[insert entity name here]" });
Everything works now and it uses the cache whenever needed. After any time I make a change, I clear that cache item and it reloads the next time in. Works great.

generic action method mvc with database call

I am searching for a way to make generic action methods that can be inherited by multiple controllers, so I don't have to repeat the same method in MVC for different controllers and tables. I think this would be applicable to a lot of the CRUD stuff I have to frequently do for multiple classes.
For example, here's the code I'd like to duplicate:
public ActionResult ToggleQC(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
BACTERIA_EVW bacteria = db.BACTERIA_EVW.Find(id);
if (bacteria == null)
{
return HttpNotFound();
}
//add/remove QC status (switch to whichever one it isn't right now)
if (bacteria.QCOn == null) //if it hasn't been QCed
{
bacteria.QCOn = DateTime.Now;
bacteria.QCBy = User.Identity.Name;
}
else //if it has been QCed and they are undoing it
{
bacteria.QCBy = null;
bacteria.QCOn = null;
}
//save changes
db.Entry(bacteria).State = EntityState.Modified;
db.SaveChanges();
//return updated QC status partial
return PartialView("_QCStatus", bacteria);
}
I need to do the same thing in a chemistry controller, but I'd rather not repeat the whole thing and just change one part. Is it possible to pass the model type into the method as a parameter, to replace BACTERIA_EVW? How would I do that?
I apologize if this is something really basic; I may not know the right terms to look for. I've searched for generic action methods but I haven't found any answers, although there was something about generic controllers - do I need to make a generic controller class somehow to include a method like this?
Thanks very much.
If you just care about removing code redundancy you can create a generic controller with that method and inherit other controllers from that:
namespace BaseControllers
{
public class CoolController
{
public virtual ViewResult Get()
{
var awesomeModel = new object();
return View(awesomeModel);
}
}
}
And in your child controller:
public class CoolController : BaseControllers.CoolController
{
public override ViewResult Get()
{
var ignoredResult = base.Get();
// ViewData.Model now refers to awesomeModel
return View("NotGet");
}
}
Same can happen for other CRUD operations.

MVC application throws ObjectDisposedException eventhough I used ToList() method

I am writing MVC web application and I've got stucked at one point.
The application throws ObjectDisposedException eventhough I used ToList() method while dbContext was still open. Here is my code:
Data Access Layer:
public List<CompanyCode> SearchCompanyCodes(int? projectId=null)
{
IQueryable<sl_CompanyCodes> filtered=dbContext.sl_CompanyCodes;
if(projectId!=null)
{
filtered = filtered.Where(cc => cc.Project_ID == projectId);
}
return filtered.ToList();
}
Bussiness logic layer:
public List<CompanyCode> CompanyCodes(int? cc_projectId=null)
{
List<CompanyCode> result;
using(var repository=new E6Repository())
{
result=repository.SearchCompanyCodes(projectId:cc_projectId);
}
// E6Repository.Dispose calls dbContext.Dispose()
return result;
}
Controller:
public JsonResult CompanyCodes()
{
var logic = new GetDataLogic();
List<CompanyCode> l = logic.CompanyCodes();
return Json(l, JsonRequestBehavior.AllowGet);
}
I have no idea what to do... When I stop debugger just before return action in controller, I can see well filled list when I type l in immediate window (notice at this point dbContext is already disposed).
Interesting thing: if I change ActionResult into view with IEnumerable<CompanyCode> as model, response is fine and there is no errors.
Can anyone explain me what's going on?
I think you have not created an instance of your EF context. Try this:
public List<CompanyCode> SearchCompanyCodes(int? projectId=null)
{
using (var context = new dbContext())
{
IQueryable<sl_CompanyCodes> filtered = from compCodes in dbContext.sl_CompanyCodes
select compCodes;
}
if(projectId!=null)
{
filtered = filtered.Where(cc => cc.Project_ID == projectId);
}
return filtered.ToList();
}

User is null when Function not called from UI

In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.
My approach
To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.
When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.
EDIT Action method
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
// Valuestring from another function
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.
Exception
Object reference not set to an instance of an object.
Question
Why does the edit work while called from the UI without any Exception , but not from HomeController?
Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?
EDIT - Added how the Edit function is called
//Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
public ActionResult Index()
{
test();
return View();
}
public void test()
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
}
}
Update - General Overview of the problem
The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.
Is it that the controller gets access to the Windows user property only when it is called from the UI?
Is it not possible to access the User Property in a function that is not called via UI?
And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.
You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -
When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.
But when you are creating the controller variable like this -
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.
ANSWER TO QUESTIONS:
Is it that the controller gets access to the Windows user property only when it is called from the UI?
=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.
Is it not possible to access the User Property in a function that is not called via UI?
=> Only if, you manually assign it otherwise NO.
MORE INSIGHT:
Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..
The best way to solve your situation is like this -
public class ModelService {
public void Edit(IPrincipal user, SetValue setValue){
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
Then in both the controllers constructors -
public class ControllerOne : Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
_modelService.Edit(User, Setvalue);
}
}
//controller 2
public class HomeController: Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Index()
{
foreach(var item in db.SetValue.ToList())
{
_modelService.Edit(User, item);
}
}
}
You can take help from IoC Container for dependency injection, that is even better approach.
Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.
public ActionResult Index()
{
test(User);
return View();
}
public void test(IPrincipal user)
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item, user);
}
}
And the oter controller
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
var currentUser = User == null? user : User;//or somthing like that
}
Didn't check this code. but it should give you something to work with.

EF4 DBContext and MVC3. The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

So, I am learning to make a very simple MVC + EF page. It is supposed to get a list of countries into a dropDownList. And I run into this error:
ObjectDisposedException was unhandled by user code: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I thought I was aware of that DBContext will be disposed, so I pull up the values in string and array string[]. Why do I still get this issue?
View.cshtml...
<select id="register-country">
#foreach (var country in ViewBag.Countries) { //******** ERROR HERE ********
<option value="#country[0]">#country[1]</option>
}
</select>
Controller.cs ...
public virtual ActionResult RegisterDialogPartial() {
var csm = new CountryStateModelRepository();
ViewBag.Countries = csm.GetCountries();
return PartialView(MVC.Shared.Views._RegisterDialogPartial);
}
DataAccessLayer ...
public class CountryStateModelRepository {
public IQueryable<string[]> GetCountries() {
using (var db = new CountryStateModelContainer()) {
return db.Country.Select(r => new string[] { r.CountryISO3, r.CountryName });
}
}
public IQueryable<string> GetCountryStates(string countryISO3) {
using (var db = new CountryStateModelContainer()) {
return db.CountryState.Filter(r => r.CountryISO3 == countryISO3.ToUpper()).Select(r=>r.CountryISO3).AsQueryable();
}
}
}
I am pretty sure that Entity Framework and LINQ work such that the query is not executed until the values are enumerated (and in this case, that doesn't happen until your view). If you wish to enumerate the values at the time of your function call modify your method as such:
return db.Country.Select(r => new string[] { r.CountryISO3, r.CountryName }).AsEnumerable();

Categories