.Net Entity Framework ObjectContext error - asp.net-mvc

I have method with code:
using (var cc = new MyDBContext())
{
var myList = (from user in cc.Users
where user.UserGroup.Name == "smth"
orderby user.ID ascending
select user);
if (startIndex != null)
return View(myList.Skip((int)startIndex).Take(50));
else
return View(myList);
}
In view I catch exception The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Some people says that .ToList() must solve problem, but it throws an exception with myList.ToList() too. What is my problem?
P.S. in debug mode I have exception at #item.FullName in view, but if I move mouse on FullName property I can see correct value.
Sorry for my bad english.

Take the "return View()" statements outside of the "using" block completely. That will ensure you have retrieved the complete data sets before your DbContext object is disposed. Like this:
using (var cc = new MyDBContext())
{
var myList = (linq).ToList();
}
return View(myList);
I'm pretty sure the problem is that you are returning an IEnumerable to the View, which means the items haven't actually been retrieved yet. But when you return the object to your View, the DbContext is getting disposed before the view has a chance to retrieve the rows.

The problem was in lazy loaded sub property of User entity. I add to link statement Include("PropName") and it works good.

Related

ASP.NET MVC 3 - Pass Session data to Layout

I want to show some data on every page with the layout (_Layout.cshtml), so I made a parent controller class, and the database access executes in its constructor. This works well except the case when I want to reach the session data, because when I try to check if the session variable exists, an exception (NullReferenceException) is thrown:
if (Session["UserId"] != null)
System.NullReferenceException: Object reference not set to an instance of an object.
I think that's because the Session object doesn't exist yet in the parent class. I can't find out another soluton to pass session related data to the layout, only when I copy the code to all action controllers. Any ideas?
Update:
Dave A, here is the parent class:
public class PCMarketController : Controller
{
protected PCMarketContext db = new PCMarketContext();
public PCMarketController()
{
int numberOfCartItems = 0;
if (Session["UserId"] != null) //Throws NullReferenceException in parent, works in action method
{
string UserId = HttpContext.Session["UserId"].ToString();
List<CartItem> CartItems = db.CartItems.Where(i => i.UserId == UserId).ToList();
foreach (var item in CartItems)
{
int count = item.Count;
numberOfCartItems += count;
}
}
ViewBag.NumberOfCartItems = " (" + numberOfCartItems + ")";
List<Category> Categories = db.Categories.ToList();
ViewBag.Categories = Categories;
}
}
You are correct in assuming the reason for Session to be null. The session HttpContext.Session injected at a later time in the page life cycle by the ControllerBuilder.
I would usually override OnActionExecuting method of the controller for such a case (http://msdn.microsoft.com/en-au/library/system.web.mvc.controller.onactionexecuting(v=vs.98).aspx).
One word of caution, using sessions may hinder the testability of your controller via standard unit tests
Cheers

Error copying a record from one entity to another using Entity Framework and Automapper

I am trying to move a record from one table into another matching (almost) table using EF5, MVC, and Automapper.
This code is what I am using:
In My Global Application_Start
//Create Map and manually map StatusCode to Status
Mapper.CreateMap<InstitutionStaging, InstitutionStaging_Archive>()
.ForMember(dest => dest.Status,o =>o.MapFrom(src=>src.StatusCode));
In my Controller
private MyContext db = new MyContext();
Public ActionResult ArchiveMe(int id = 0){
var institutionstaging = db.InstitutionStagings.Find(id);
if (institutionstaging == null)
{
return HttpNotFound();
}
if (ModelState.IsValid)
{
var institutionArchive = Mapper.Map<InstitutionStaging, InstitutionStaging_Archive>(institutionstaging);
//Set Archive date to now.
institutionArchive.ArchiveDate = DateTime.Now;
//Error happens on the next line
db.InstitutionStaging_Archives.Add(institutionArchive);
db.InstitutionStagings.Remove(institutionstaging);
db.Entry(institutionArchive).State = EntityState.Added;
//Commit the changes
var result = db.SaveChanges();
}
}
When it hits the line marked "Error happens here==>" I get the following error message.
{"The entity type InstitutionStaging_Archive is not part of the model for the current context."}
The MyContext contains DbSets for both InstitutionStaging and InstitutionStaging_Archive.
Any idea what is happening?
TIA
J
This error isn't typically a problem with AutoMapper, but rather a problem with your Entity Framework model setup.
It can be because you are using the wrong connection string, or it can be because you don't have the model mapped correctly.
Since we don't know what your model is, what your database looks like, or how your mappings are.. can't help much beyond that.
To prove it to yourself, just comment out the automapper stuff and do it by hand, and I'm pretty sure you'll get the same error.

InvalidOperationException when using updatemodel with EF4.3.1

When I update my model I get an error on a child relation which I also try to update.
My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:
InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.]
My update method:
public ActionResult ChangeOrder(Order model)
{
var order = this.orderRepository.GetOrder(model.OrderId);
if (ModelState.IsValid)
{
var success = this.TryUpdateModel(order);
}
this.orderRepository.Save();
return this.View(order);
}
I tried all solutions I saw on SO and other sources, none succeeded.
I use .Net MVC 3, EF 4.3.1 together with DBContext.
There are a number of code smells here, which I'll try to be elegant with when correcting :)
I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.
I also presume orderRepository.GetOrder() is a data layer call that retrieves an order from a data store?
You are also declaring potentially unused variables. "var order =" will be loaded even if your model is invalid, and "var success =" is never used.
TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.
In your case, I would recommend the following pattern. There's minimal abstraction, but it still gives you more control than using TryUpdateModel / UpdateModel:
public ActionResult ChangeOrder(OrderViewModel model) {
if(ModelState.IsValid) {
// Retrieve original order
var order = orderRepository.GetOrder(model.OrderId);
// Update primitive properties
order.Property1 = model.Property1;
order.Property2 = model.Property2;
order.Property3 = model.Property3;
order.Property4 = model.Property4;
// Update collections manually
order.Collection1 = model.Collection1.Select(x => new Collection1Item {
Prop1 = x.Prop1,
Prop2 = x.Prop2
});
try {
// Save to repository
orderRepository.SaveOrder(order);
} catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
return View(model);
}
return RedirectToAction("SuccessAction");
}
return View(model);
}
Not ideal, but it should serve you a bit better...
I refer you to this post, which is similar.
I assume that the user can perform the following actions in your view:
Modify order (header) data
Delete an existing order item
Modify order item data
Add a new order item
To do a correct update of the changed object graph (order + list of order items) you need to deal with all four cases. TryUpdateModel won't be able to perform a correct update of the object graph in the database.
I write the following code directly using a context. You can abstract the use of the context away into your repository. Make sure that you use the same context instance in every repository that is involved in the following code.
public ActionResult ChangeOrder(Order model)
{
if (ModelState.IsValid)
{
// load the order from DB INCLUDING the current order items in the DB
var orderInDB = context.Orders.Include(o => o.OrderItems)
.Single(o => o.OrderId == model.OrderId);
// (1) Update modified order header properties
context.Entry(orderInDB).CurrentValues.SetValues(model);
// (2) Delete the order items from the DB
// that have been removed in the view
foreach (var item in orderInDB.OrderItems.ToList())
{
if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
context.OrderItems.Remove(item);
// Omitting this call "Remove from context/DB" causes
// the exception you are having
}
foreach (var item in model.OrderItems)
{
var orderItem = orderInDB.OrderItems
.SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
if (orderItem != null)
{
// (3) Existing order item: Update modified item properties
context.Entry(orderItem).CurrentValues.SetValues(item);
}
else
{
// (4) New order item: Add it
orderInDB.OrderItems.Add(item);
}
}
context.SaveChanges();
return RedirectToAction("Index"); // or some other view
}
return View(model);
}

Datatable reset during foreach loop

I'm currently stuck on an issue that I cannot understand:
The following code produce a Datatable ("outDT") in order to populate a gridview later on. The datatable is build using rows of others datatables issued from various SQL quieres (those "get" function that return also Datatable).
The issue come when I call the "getAverage" method. Right after the call, the "pcDT" Datatable is nullified and cause a further "Collection was modified; enumeration operation might not execute." error on next loop.
I never modified a single row of the "pcDT" Datatable in the foreach loop, nor in the problematic "getAverage" method.
public DataTable getReportTable(int idClient)
{
Object thisLock = new Object();
DataTable outDT = new DataTable();
outDT.Columns.Add("PC Name");
DataTable pcDT = getPCNames(idClient);
*foreach (DataRow pcRow in pcDT.Rows)*
{
DataRow outRow = outDT.NewRow();
outRow["PC Name"] = pcRow["Name"];
**DataTable collectedDT = getAverage((int)pcRow["idPC"]);**
foreach (DataRow dataRow in collectedDT.Rows)
{
outDT.Columns.Add(dataRow["name"].ToString());
outRow[dataRow["name"].ToString()] = dataRow["AVG(MeasurePoint.dataValue)"];
}
outDT.Rows.Add(outRow);
}
return outDT;
}
(*)This foreach cause the famous "Collection was modified; enumeration operation might not execute." error
(**) And here his the method call that reset the "pcDT" Datatable. This function simply call a mySQL queries and retrieve a Datatable.
It's ok, I've found my mistake. It was in the class that manage basic SQLQueries; I used a Datatable instance that is linked to the class and not to the method, so when I recall a method that used this instance, I lost previous filled Datatable...
My bad :/

Calling UpdateModel with a collection of complex data types reset all non-bound values?

I'm not sure if this is a bug in the DefaultModelBinder class or what.
But UpdateModel usually doesn't change any values of the model except the ones it found a match for.
Take a look at the following:
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Edit(List<int> Ids)
{
// Load list of persons from the database
List<Person> people = GetFromDatabase(Ids);
// shouldn't this update only the Name & Age properties of each Person object
// in the collection and leave the rest of the properties (e.g. Id, Address)
// with their original value (whatever they were when retrieved from the db)
UpdateModel(people, "myPersonPrefix", new string[] { "Name", "Age" });
// ...
}
What happens is UpdateModel creates new Person objects, assign their Name & Age properties from the ValueProvider and put them in the argument List<>, which makes the rest of the properties set to their default initial value (e.g. Id = 0)
so what is going on here?
UPDATE:
I stepped through mvc source code (particularly DefaultModelBinder class) and here is what I found:
The class determines we are trying to bind a collection so it calls the method: UpdateCollection(...) which creates an inner ModelBindingContext that has a null Model property. Afterwards, that context is sent to the method BindComplexModel(...) which checks the Model property for null and creates a new instance of the model type if that is the case.
That's what causes the values to be reset.
And so, only the values that are coming through the form/query string/route data are populated, the rest remains in its initialized state.
I was able to make very few changes to UpdateCollection(...) to fix this problem.
Here is the method with my changes:
internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) {
IModelBinder elementBinder = Binders.GetBinder(elementType);
// build up a list of items from the request
List<object> modelList = new List<object>();
for (int currentIndex = 0; ; currentIndex++) {
string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
if (!DictionaryHelpers.DoesAnyKeyHavePrefix(bindingContext.ValueProvider, subIndexKey)) {
// we ran out of elements to pull
break;
}
// **********************************************************
// The DefaultModelBinder shouldn't always create a new
// instance of elementType in the collection we are updating here.
// If an instance already exists, then we should update it, not create a new one.
// **********************************************************
IList containerModel = bindingContext.Model as IList;
object elementModel = null;
if (containerModel != null && currentIndex < containerModel.Count)
{
elementModel = containerModel[currentIndex];
}
//*****************************************************
ModelBindingContext innerContext = new ModelBindingContext() {
Model = elementModel, // assign the Model property
ModelName = subIndexKey,
ModelState = bindingContext.ModelState,
ModelType = elementType,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
object thisElement = elementBinder.BindModel(controllerContext, innerContext);
// we need to merge model errors up
VerifyValueUsability(controllerContext, bindingContext.ModelState, subIndexKey, elementType, thisElement);
modelList.Add(thisElement);
}
// if there weren't any elements at all in the request, just return
if (modelList.Count == 0) {
return null;
}
// replace the original collection
object collection = bindingContext.Model;
CollectionHelpers.ReplaceCollection(elementType, collection, modelList);
return collection;
}
Rudi Breedenraed just wrote an excellent post describing this problem and a very helpful solution. He overrides the DefaultModelBinder and then when it comes across a collection to update, it actually updates the item instead of creating it new like the default MVC behavior. With this, UpdateModel() and TryUpdateModel() behavior is consistent with both the root model and any collections.
You just gave me an idea to dig into ASP.NET MVC 2 source code.
I have been struggling with this for two weeks now. I found out that your solution will not work with nested lists. I put a breakpoint in the UpdateCollection method ,and it never gets hit. It seems like the root level of model needs to be a list for this method to be called
This is in short the model I have..I also have one more level of generic lists, but this is just a quick sample..
public class Borrowers
{
public string FirstName{get;set;}
public string LastName{get;set;}
public List<Address> Addresses{get;set;}
}
I guess that, I will need to dig deeper to find out what is going on.
UPDATE:
The UpdateCollection still gets called in asp.net mvc 2, but the problem with the fix above is related to this HERE

Resources