Entityframework ObjectContext instance has been disposed in certain location [duplicate] - c#

This question already has answers here:
Solving "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection" InvalidOperationException
(8 answers)
Closed 5 years ago.
I'm getting the exception:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
Only when I try to add the object or some properties of that object to #Html.ActionLink.
I have reviewed my code couple of times and can't find anything unusual any suggestions on what might be causing this exception?
Thank you!
UserManagerResult.cs
public class UserManagerResult {
public bool Success{get; set;}
public string ErrorMessage{get; set;}
public Account User{get; set;}
public List <Account> UserList{get;}
public UserManagerResult() {
Success = false;
UserList = new List <Account>();
}
}
UserManager.cs
public static UserManagerResult GetUserList() {
UserManagerResult userManagerResult = new UserManagerResult();
try {
using (AlenMotorsDbEntities alenMotorsDbEntities = new AlenMotorsDbEntities()) {
foreach (Account account in alenMotorsDbEntities.Accounts.ToList()) {
userManagerResult.UserList.Add(account);
}
return userManagerResult;
}
}
catch (Exception ex) {
userManagerResult.ErrorMessage = ex.Message;
return userManagerResult;
}
}
DeveloperViewModel.cs
public class DeveloperViewModel {
[Display(Name = "New Role")]
public string NewRole{get; set;}
[Display(Name = "Remove Role")]
public List <SelectListItem> RoleList{get; set;}
[Display(Name = "User list")]
public List <Account> UserList{get; set;}
}
UserManagerController.cs
public ActionResult Developer(DeveloperViewModel model) {
UserManagerResult getUserList = UserManager.GetUserList();
model.UserList = getUserList.UserList.ToList();
return View(model);
}
The view I'm using
#{
foreach (Account account in Model.UserList.ToList()) {
<tr>
<th scope="row">--</th>
<td>#account.Email</td>
<td>#account.LastName</td>
<td>#account.FirstName</td>
<td>
#Html.ActionLink("Remove", "Remove", account)
</td>
</tr>
}
}

In your GetUserList(), try this:
foreach (Account account in alenMotorsDbEntities.Accounts.Include(i=>i.YourOtherEntity).Include(i=>i.AnotherEntity).ToList())
And keep adding all related entities that is relevant.

Related

EF Code first problem. It creates the new objects

I am a beginner in Entity Framework Code First. So I am having troubles in working with migrations. So this is an issue:
I have following models:
Defect - table of defects(general)
User - table of users
Order - table or orders
DefectEntry - defects per order(entered by a user).
DefectEntry has:
public virtual Defect defect { get; set; }
public virtual User user { get; set; }
public virtual Order order { get; set; }
All the other models contain:
public virtual ICollection<DefectEntry> Entries { get; set; }
When I am trying to add a defect per order(order & user known) the EF is creating a new order and user objects with compeletely another IDs(Because ID is unique per user and treated as Primary_key).
private Order order;
private User user;
public AddDefect(ref Order order, ref User user) //this is a name of UserControl
{
InitializeComponent();
this.order = order;
this.user = user;
loadDefects();
}
private void loadDefects()
{
using (MyBridgeContext context = new MyBridgeContext())
{
var defects = context.Defects;
foreach (var obj in defects)
{
defectList.Items.Add(obj);
}
}
}
private void addButton_Click(object sender, RoutedEventArgs e)
{
if (doubleCheck.IsChecked == true)
{
try
{
using (MyBridgeContext context = new MyBridgeContext())
{
DefectEntry entry = new DefectEntry();
foreach (Defect def in defectList.Items)
{
if(defectList.SelectedItem == def)
{
entry.defect = def;
}
}
entry.user = user;
entry.order = order;
entry.dt = DateTime.Now;
context.Entries.Add(entry);
context.SaveChanges();
this.Content = new MainMonitoring(ref order, ref user);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
When I push an addButton I have these kind of duplicates:
User table look Defects table look Order table look Defect Entry table look
It does this because your user and order entities are disconnected from EF context. You have to reattach them to the context before using them in DefectEntry object. So you have to do something like this right after using (MyBridgeContext context=new MyBridgeContext()) { statement inside the addButton_Click method:
context.Users.Attach(user);
context.Orders.Attach(order);

How to load navigation child items from a controller to view

Am having issues loading navigation(child ) element from a controller to a view
I created this model
public class MultipleItems
{
public IEnumerable<Order> Orders { get; set; }
public IEnumerable<support> Supports { get; set; }
public IEnumerable<DbModel.Track> Tracks { get; set; }
public IEnumerable<Receipt> Receipts { get; set; }
public IEnumerable<Quota> Quotas { get; set; }
}
And the controller
public ActionResult Client()
{
string useremail = User.Identity.GetUserName();
var myModel = new MultipleItems();
myModel.Supports = new SupportW().GetSupports(useremail);
myModel.Orders = new Orders().GetOrders(useremail);
myModel.Receipts = new Receipts().GetReceipts(useremail);
myModel.Tracks = new Track().GetTracks(useremail);
myModel.Quotas = new Quotas().GetQuota(useremail);
return View(myModel);
}
Am interested in Tracks() and this is the method
public IEnumerable<DbModel.Track> GetTracks(string email)
{
try
{
using (var da = new CourierEntities())
{
da.Tracks.Include(a => a.Order);
da.Tracks.Include(a => a.Order.User);
da.Tracks.Include(a => a.destination);
da.Tracks.Include(a => a.Source);
da.Tracks.Include(a => a.CurrentLocation);
var q = (from p in da.Tracks where p.Order.User.Email == email select p).ToList();
if (q != null)
{
return q;
}
}
return null;
}
catch (Exception ex)
{
returnMsg = ex.Message;
return null;
}
}
While the view is
<td>
#if (Model.Orders.Count() != 0)
{
foreach (var i in Model.Tracks)
{
<tr>
Order id #i.OrderID
<br />
Trackid #i.TrackID
<br />
#i.Order.Packagename
</tr>
}
}
</td>
am having issues with #i.Order.Packagename and i recieve this error
The ObjectContext instance has been disposed and can no longer be used for
operations that require a connection.
Description: An unhandled exception occurred during the execution of the
current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ObjectDisposedException: The ObjectContext
instance has been disposed and can no longer be used for
operations that require a connection.
Source Error:
Line 120:
Line 121: <br />
Line 122: #i.Order.Packagename
Line 123: </tr>
Although the comments suggest the use of a View Model, which would be a good idea, the fundamental issue with your code is the lifetime of the database context(s).
When you execute code such as:
myModel.Supports = new SupportW().GetSupports(useremail);
The object SupportW has a lifetime of that statement. It will likely be disposed before your view executes. If the method GetSupports(useremail) returns IQueryable then when that is resolved in the view, the source is no longer there.
To resolve this you should change the lines to:
myModel.Supports = new SupportW().GetSupports(useremail).ToList();
The addition of the ToList() resolves the `IQueryable' immediately and the data will be available in your view.

Null Exception thrown in View

Using ASP.NET MVC, .NET Framework 4.5.2, Entity Data Model for SQL DB, Visual Studio 2017.
I have a class generated from the ADO.NET(EF Designer from Database) :
BookInfo.cs
namespace LibraryMS
{
using System;
using System.Collections.Generic;
public partial class BookInfo
{
public string BookID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public string PublishDate { get; set; }
public string Edition { get; set; }
public virtual Inventory Inventory { get; set; }
}
}
The database is designed where the "BookID" in the BookInfo table has a foreign key "BookID" in the Inventory table.
In a view to update an inventory's properties referenced by "BookID", I then proceed to query the list and update the correct instance.
Screenshot of update inventory page:
When landing on page to enter info the [HttpGet] UpdateInventory() is called, when clicking "Create" button as seen above, the [HttpPost] UpdateInventory(...) is called.
Logic/Code in Controller:
[HttpGet]
public ActionResult UpdateInventory()
{
return View();
}
[HttpPost]
public async Task<ActionResult> UpdateInventory(string bookID, string ttlIn, string lowin, string outnow)
{
var bf = await SqlRestApiHelper.searchFromBooks(bookID);
bf.Inventory.TotalIn = Convert.ToInt16(ttlIn);
bf.Inventory.LowIn = Convert.ToInt16(lowin);
bf.Inventory.Out = Convert.ToInt16(outnow);
await SqlRestApiHelper.UpdateBookInfoInventory(bf.Inventory);
await SqlRestApiHelper.SaveChanges();
return View("All");
}
[HttpGet]
public async Task<ActionResult> All()
{
return View(await SqlRestApiHelper.getAllBooksInfo(0, 10));
}
SqlRestApiHelper.cs
namespace LibraryMS
{
public static class SqlRestApiHelper
{
private static libraryDBEntities entities = new libraryDBEntities();
public static async Task<LibraryMS.BookInfo> searchFromBooks(string id)
{
return entities.BookInfoes.ToList().Find(book => book.BookID == id);
}
public static async Task UpdateBookInfoInventory(LibraryMS.Inventory inv)
{
var newInv = inv;
var el = entities.BookInfoes.ToList().Find(x => x.Inventory.BookID == newInv.BookID);
if (el != null)
{
el.Inventory.TotalIn = newInv.TotalIn;
el.Inventory.LowIn = newInv.LowIn;
el.Inventory.Out = newInv.Out;
// the above updates the list item referenced
}
}
public static async Task SaveChanges()
{
await entities.SaveChangesAsync();
}
public static async Task<IPagedList<BookInfo>> getAllBooksInfo(int page, int itemsPerPage)
{
List<BookInfo> bookinfo = new List<BookInfo>();
bookinfo = (from o in entities.BookInfoes
orderby o.Title descending //use orderby, otherwise Skip will throw an error
select o)
.Skip(itemsPerPage * page).Take(itemsPerPage)
.ToList();
int totalCount = bookinfo.Count();//return the number of pages
IPagedList<BookInfo> pagebooks = new StaticPagedList<BookInfo>(bookinfo, page + 1,10,totalCount);
return pagebooks;//the query is now already executed, it is a subset of all the orders.
}
The Null Exception Thrown:
Code for all.cshtml view page:
#model PagedList.IPagedList<LibraryMS.BookInfo>
#using PagedList.Mvc;
#{
ViewBag.Title = "All";
}
<h2>all</h2>
<table class="table">
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.Publisher)
</td>
<td>
#Html.DisplayFor(modelItem => item.PublishDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Edition)
</td>
<td>
#Html.ActionLink("Details","Details",new { item.BookID})
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page => Url.Action("All","BookInfoController", new { page }))
your view is throwing error because you are returning the view without passing the model, you are using return View("All") without passing the model
the right way is by passing the model with the view, you can do it this way
View("ViewName", ModelData);
in your case
return View("All", await SqlRestApiHelper.getAllBooksInfo(0, 10));
for the saving part I am not sure why, but i can see few errors,
first what if book does not have inventory info ?
it will throw null error, so first check if null, create new inventory, if not update accordingly
here is how i would do it
public static async Task UpdateBookInfoInventory(Inventory inv)
{
var newInv = inv;
// get book info
var el = entities.BookInfoes.FirstOrDefault(x => x.BookID == inv.BookID);
if (el != null)
{
if(el.Inventory != null)
{
// update accordingly
el.Inventory.TotalIn = newInv.TotalIn;
el.Inventory.LowIn = newInv.LowIn;
el.Inventory.Out = newInv.Out;
// the above updates the list item referenced
}
else
{
/// add new if null
el.inventory = newInv;
}
await SqlRestApiHelper.SaveChanges();
}
As a first step , put the breakpoint in the getAllBooksInfo method and see whether the list count is coming or not in visual studio. This will help you a lot.
And As an alternative step, you can also solve this error by using the ToPagedList(pageIndex, pageSize); method, i have used it personally and it was worked well
ToPagedList example as per your code:
**public static async Task<IPagedList<BookInfo>> getAllBooksInfo(int page, int itemsPerPage)
{
List<BookInfo> bookinfo = new List<BookInfo>();
bookinfo = (from o in entities.BookInfoes
orderby o.Title descending //use orderby, otherwise Skip will throw an error
select o)
.Skip(itemsPerPage * page).Take(itemsPerPage)
.ToList();
int totalCount = bookinfo.Count();//return the number of pages
//changes made to the below line
IPagedList<BookInfo> pagebooks = bookinfo.ToPagedList(1, 10);
return pagebooks;//the query is now already executed, it is a subset of all the orders.
}**
official source:https://github.com/troygoode/PagedList
Note: Even with this approach, kindly check whether you are getting the data or not from the database by using the breakpoint in the changed line.
Hope this will surely solve your problem kindly let me know thoughts or feedbacks
Thanks
karthik

Dynamic Model Properties, get model property by name

Not sure how to word this...
I have a Model, here is a section of it:
public class AnswerSheet
{
public string Q1 { get; set; }
public string Q2 { get; set; }
public string Q3 { get; set; }
public string Q4 { get; set; }
I am using a Viewmodel to reuse the same view to answer each question separately. It is almost working. Is there any way I can use my controller as follows to dynamically assign the model.q#, ex:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateNextQ([Bind(Include = "ID, qCounter, Question,Comment")] AnswerSheetCreateVM answerVM)
{
if (ModelState.IsValid)
{
string questionAns = answerVM.Question + answerVM.Comment;
AnswerSheet answer= db.AnswerSheets.Find(answerVM.ID);
//THIS PART HERE IS WHERE I HAVE A PROBLEM
answer.Q(answerVM.qCounter) = questionAns;
//That one line above
db.AnswerSheets.Add(answer);
db.SaveChanges();
So basically can I get data from my controller variable (qCounter in this case) and assign it to my model like Model.Q(qcounter)
As a side note I am open to suggestion on how to word this question or what tags to assign to it.
I found this post useful:
Set object property using reflection
This is what I ended up doing, still have to test performance:
string test = string.Format("Q" + answerVM.qCounter);
string questionAns = (answerVM.Question + " - " + answerVM.NoComment);
AnswerSheet answer= db.AnswerSheets.Find(answerVM.ID);
if (answer== null)
{
return HttpNotFound();
}
answer.GetType().GetProperty(test).SetValue(answer, questionAns, null);
db.Entry(answer).State = EntityState.Modified;
db.SaveChanges();
Maybe that will help someone down the line....NOt sure if this counts as using reflection....

Return type IResult<T>

I try solve this problem in WPF app with MVVM design.
I need return on database access more "data":
result messages
return value
and if was database successful
So I create interface for return type, here is it:
public interface IResult<T>
{
bool IsSuccess { get; set; }
string ResultMessage { get; set; }
T ReturnValue { get; set; }
}
This interface implements some class:
public class DbResult: IResult<IList<Archive>>
{
public bool IsSuccess{ get; set;}
public string ResultMessage{ get; set;}
public IList<Archive> ReturnValue { get; set; }
}
Method in class on databases access have return type IResult<Archive>, Archive is class generated with LINQ TO SQL.:
public interface IArchiveDbManager
{
IResult<Archive> LoadConversationTo(string nick, DateTime toDt);
}
[Export(typeof(IArchiveDbManager))]
public partial class ArchiveDbManager : IArchiveDbManager
{
public IResult<Archive> LoadConversationTo(string nick, DateTime toDt)
{
var result = new DbResult();
try
{
var query = from m in _dc.Archive
where m.Nick == nick
where m.Time <= toDt
orderby m.Time
select m;
result.ReturnValue = query.ToList();
if (query.Count() == 0)
{
result.ResultMessage = "For the specified search criteria found no record in the database.";
result.IsSuccess = false;
}
else
{
result.ResultMessage =
string.Format("For the specified search criteria found {0} record in the database.", query.Count());
result.IsSuccess = true;
}
return result;
}
catch (Exception exception)
{
throw exception;
}
}
}
Class ArchiveDbManager is inject with MEF in view model class.
I have some question:
What is correct solution for this
problem in MVVM? Scenario is view
model create through
ArchiveDbManager access to the
database, for example if database
table is empty, ArchiveDbManager
class return message "Archive is
empty" and view model show this
message in view to the user.
It is right to create a report of
the search results in a database in
"ArchiveDbManager class (it is a
class on database access)"
What do you think about this
solution?
Keep it simple: You don't really need the interface or class.
You only need to ask for the result, and the method should throw an exception with the error message is something goes wrong.
This kind of success result messages: result.ResultMessage = "For the specified search criteria found no record in the database."; do not belong to business logic or the data layer. The UI layer should be responsible for showing the messages to the user.

Categories

Resources