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();
}
Related
I have created service which communicates with my database. GetAvailableUserId service's method cannot be run simultaneously, because I don't want to return same user's id for two different calls. So far I have managed this:
public class UserService : IUserService
{
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
transaction.Commit();
return availableUser.Id;
}
}
}
}
I wanted to test if service will work as intended, so I created simple console application to simulate synchronous requests:
Parallel.For(1, 100, (i, state) => {
var service = new UserServiceReference.UserServiceClient();
var id = service.GetAvailableUserId();
});
Unfortunately, It failed that simple test. I can see, that it returned same id for different for iterations.
Whats wrong there?
If I understood you correctly, you wan to lock method from other threads. If yesm then use lock:
static object lockObject = new object();
public class UserService : IUserService
{
public int GetAvailableUserId()
{
lock(lockObject )
{
// your code is omitted for the brevity
}
}
}
You need to spend some time and delve into the intricadies of SQL Server and EntityFramework.
Basically:
You need a database connection that handles repeatable results (which is a database connection string setting).
You need to wrap the interactions in EntityFramework within one transaction so that multiple instances do not possibly return the same result in the query and then make problems in the save.
Alternative method to achieve this is to catch DbUpdateConcurrencyException to check whether values in the row have changed since retrieving when you try to save.
So if e.g. the same record is retrieved twice. The first one to have the Available value updated in the database will cause the other one to thow concurrency exception when it tries to save because the value has changed since it was retrieved.
Microsoft - handling Concurrency Conflicts.
Add ConcurrencyCheck attribute above the Available property in your entity.
[ConcurrencyCheck]
public bool Available{ get; set; }
Then:
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
try
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
return availableUser.Id;
}
catch (DbUpdateConcurrencyException)
{
//If same row was already retrieved and updated to false, do not save, instead call the method again to get the next true row.
return GetAvailableUserId();
}
}
}
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.
#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
I have this working in my controller, but I want to follow best practices and put my database logic in Model.
I want to put all database logic (select, update, delete, insert) to MODEL, therefore I create methods in my model.
My method for retrieving the data:
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
}
This is my modelView:
public class ChatLogsNameViewModel
{
public UserProfile UserProfile { get; set; }
public string Message { get; set; }
public DateTime Time { get; set; }
}
I call my getChatLogWithName() method in my controller like this:
List<ChatLogsNameViewModel> items = null;
using (var dba = new ChatLogContext())
{
items = dba.getChatLogWithName().ToList();
return View(items);
}
Error I get is:
The operation cannot be completed because the DbContext has been disposed.
What is the proper way to do this? I just want to pass collection (all records from 2 tables via join) to controller.
To ensure that the DBContext is not getting referenced after disposal. How about returning a list so you dont have to call .ToList():
public List<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list.ToList();
}
}
and
items = dba.getChatLogWithName();
Or
Since it appears that dba is the same as db, couldn't you change your code to use the dba instance which won't get disposed until the end of your using statement within your controller.
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
var list = (from b in this.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
Lifetime - DbContext
The lifetime of the context begins when the instance is created and
ends when the instance is either disposed or garbage-collected. Use
using if you want all the resources that the context controls to be
disposed at the end of the block. When you use using, the compiler
automatically creates a try/finally block and calls dispose in the
finally block.
The problem was when the inner using got disposed, it invalidated the DbContext. So you need to use .ToList() to save the query result in memory.
Suppose getChatLogWithName is defined in the class called Repo, you can change the controller logic to something like this:
var repo = new Repo();
var items = repo.getChatLogWithName().ToList();
Or move .ToList() to getChatLogWithName.
Btw, you should not use the nested DbContexts cope, in your controller, you don't have to wrap it using another DbContextscope.
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();