I'm using ASP.NET 4.0.
I've got the following code that returns with an error of "Cannot access a disposed object.
Object name: 'DataContext accessed after Dispose.'."
public IEnumerable<BatchHeader> GetHeaders()
{
using(NSFChecksDataContext context = DataContext)
{
IEnumerable<BatchHeader> headers = (from h in context.BatchHeaders
select h);
return headers;
}
}
If I change this to:
public IEnumerable<BatchHeader> GetHeaders()
{
using(NSFChecksDataContext context = DataContext)
{
return context.BatchHeaders.ToList();
}
}
It will work fine. I'm using this method to populate a RadGrid. Can anyone explain why the second method will work but not the first?
Thanks.
The first doesn't work because when the method returns the data context instantiated in the using block is disposed. However, the IEnumerable<BatchHeader> returned is lazily evaluated and needs this data context to be alive to enumerate its results.
You could do something like this:
public IEnumerable<BatchHeader> GetHeaders() {
using(NSFChecksDataContext context = DataContext) {
foreach(var header in context.BatchHeaders) {
yield return header;
}
}
}
The second block works because the query results are enumerated over and stored in memory before the data context is disposed of. After that happens, the data context isn't needed anymore. However, be careful when using code like your second block; if the BatchHeaders table is large you just pulled it all into memory.
Now, and here is the most serious part of my answer: I absolutely can't stand seeing queries that instantiate data contexts to execute. I want to know and control when my data contexts are being used.
I'm guessing the IEnumerable from your context is using deferred execution, so unless you force it to enumerate using ToList it doesn't do so until you use the values, which in this case is outside of the using block so the object will be disposed.
return headers.AsEnumerable();
should work, because by default, a linq query returns an IQueryable object, which means that the data is not fetched from db until enumerated using foreach, ToArray, ToList or AsEnumerable. When Asp.Net tried to access the IQueryable and fetch the data using foreach, the connection was already closed.
Related
I'm in the midst of setting up an Umbraco 8 website and have run into some weird behaviour. The project is using .NET 4.7.2.
Basically, I have an IENumerable of type Event, a simple list of content that I'd like to render in to a list. However, whenever I do anything with the list (which has items), the list is immediately emptied. This includes simply assigning to a different variable, checking for null etc.
I don't believe this is an Umbraco 8 issue but for clarity, I'm currently running through a Surface Controller and render it by calling the following in my view:
#Html.Action("RenderUpcoming", "Events")
This is the controller:
using Index.Models.Events;
using Index.Models.PublishedContent;
using Papermoon.Umbraco.Kupo.Core.Services.Interfaces;
using System;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Web.Mvc;
namespace Index.Web.Controllers.Surface
{
public class EventsController : SurfaceController
{
private readonly KupoGeneralSettings _kupoGeneralSettings;
public EventsController(IKupoSettingsService kupoSettingsService)
{
_kupoGeneralSettings = kupoSettingsService.GetSettings<KupoGeneralSettings>("kupoGeneralSettings");
}
public ActionResult RenderUpcoming()
{
UpcomingEventsModel model = new UpcomingEventsModel();
model.Title = "Upcoming Events";
model.Events = Umbraco.ContentAtXPath("root/homepage/events/event").Select(x => new Event(x));
model.Events = model.Events.Where(x => x.StartDate > DateTime.Now).OrderBy(x => x.StartDate).Take(3);
model.TotalEvents = model.Events.Count();
model.EventListingLink = _kupoGeneralSettings.EventListingLink;
return PartialView("~/Views/Partials/Events/UpcomingEvents.cshtml", model);
}
}
}
So here, when I call model.Events = model.Events.Where(x => x.StartDate > DateTime.Now).OrderBy(x => x.StartDate).Take(3); - I have results then when I do model.TotalEvents = model.Events.Count(); the list (model.Events) is then empty.
This also happens when I assign to another variable, when I call model.Events.Any(), or when I even do Model.Events != null.
It's potentially easier to show this than tell so see the accompanying gif of this happening: https://i.imgur.com/rE3VAqe.gif
Thanks,
Ben
Your IEnumerable comes from this call:
Umbraco.ContentAtXPath("root/homepage/events/event")
How it is done exactly I do not know since it's Umbraco business, but IEnuemerable itself allows "lazy" evaluation. It means that e.g. if you are reading from a SQL database without buffering it will read each time you iterate.
Depending on the data it can return same results or new results (if the data has changed). So what you get in IEnumerable depends totally on the implementation details, so if you reiterate you don't know what happens behind the scenes (ranging from nothing special to a new DB query).
To prevent this behavior when the source of IEnumerable is unknown you can do ToList() at the end of your query:
Umbraco.ContentAtXPath("root/homepage/events/event")
.Select(x => new Event(x))
.ToList();
What will happen is that you iterate through your collection once and add all elements to the list. This list won't change unless you do this yourself.
Sure – you don't know the actual type of the object other than that it's something you can iterate over (an IEnumerable).
It could be a generator that returns an infinite stream of things, for instance (well, in this case you know it's not).
If you need a concrete collection, you could use .ToList() to cast it into a List<> you can certainly iterate over multiple times.
I searched and could not find an answer to my question, sorry if this is a duplicate please refer me to correct solution.
I have a entity framework core setup which tracks an object 'TestRun'. I have created a list of TestRuns and wish to add them to the DbContext then write them to the database. This works fine with a regular ForEach loop but when I try to use the List.Foreach inside the using block Visual Studio suggests that the disposable object is already disposed. "Access to disposed closure".
using (var context = new TestResultsContext())
{
_testRuns.ForEach(t => context.Add(t));
context.SaveChanges();
}
I figure this has to do with the 'lazy loading' but ForEach is a void return so there is no way to force the result to enumerate.
List<T>.ForEach is not a LINQ method, that's from the List<T> class, and it's also not lazily-evaluated. So regarding the Visual Studio warning, not all warnings should be taken into account, this is one of them. The analyzers are not perfect.
You don't need to use any of those methods, however, since the proper way is use the DbSet<T>'s method AddRange:
using (var context = new TestResultsContext())
{
context.TestRuns.AddRange(_testRuns);
context.SaveChanges();
}
I am currently hunting down memoryleaks in our application and when it comes to viewmodels that execute any linq queries I find an object in memory with that namespace. I am using dotMemory to do the inspection and it lists the object with a +<>c ending. I have not found any explanation what kind of object this is and if it is a real issue that this resides in memory, but I have found out that it is connected to the linq query. Code that reprodce this and as you can see the Linq query result is never used.
public class myViewModel : PropertyChangedBase
{
public myViewModel()
{
var memissue = _dummyList.ToList().Any(c => c == false);
}
public string SomeBoundProperty
{
get { return _someBoundProperty; }
set
{
if (value == _someBoundProperty) return;
_someBoundProperty = value;
NotifyOfPropertyChange();
}
}
}
Snapshot from dotmemory:
I hope that someone can explain what kind of object a +<>c is and maybe why it is not released from memory or is this just the way that Linq works?
.ToList() enumerates the whole collection and stores a copy in memory. This is typically not useful if you aren't going to use all the information in the collection. Try removing .ToList() and see what happens. You should expect to see immediate savings.
To shed some light on the +<>c thing, I believe that comes from it being a generic type. I'm inferring from the code you provided that the type is really List<Boolean>.
I know how to fix this problem, but, this error is appearing in a situation totally different to me.
In my MVC Razor view I have the following code that will filter a list of the model, and bring to me a specific object based on the user logged on and also with a field where the result is different than null:
#{
Models.PostOrcamentoServicoProposta proposta = Model.PostOrcamentoServicoProposta.FirstOrDefault(p => p.Usuarios.UsuEmail.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase) && p.PosOrcSerProStatus != null);
if (proposta != null)
{
// create a div here
}
}
In my ActionResult I have the following load
using (ServiciliEntities db = new ServiciliEntities())
{
// include data
PostOrcamentoServico orcamento = db.PostOrcamentoServico.Include("PostOrcamentoServicoProposta").Where(o => o.PosOrcSerId == id).FirstOrDefault();
return View(orcamento);
}
As we can see, in my view I receive a model that contains a list of PostOrcamentoServicoProposta and I want to get just one specic PostOrcamentoServicoProposta item according the user logged on.
No problem about that, but I also want the one where PosOrcSerProStatus shouldn't be null.
And there is the problem, this field PosOrcSerProStatus.
The PosOrcSerProStatus field is an Nullable<Enumerable> and in sometimes it may be possible to be nullable.
So, when I debug my view, I can see that PosOrcSerProStatus is null, and, that's okay to me, however, even if I'm trying to manipulate to get only the one that isn't nullable with the condition p.PosOrcSerProStatus != null, then, I get the error:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
First: the list of PosOrcSerProStatus is populated in the action with Include("PostOrcamentoServicoProposta");
Second: Model.PostOrcamentoServicoProposta contains objects;
Third: In all PostOrcamentoServicoProposta objects they're populated properly and PosOrcSerProStatus can be or cannot be null.
I think your problem is that PosOrcSerProStatus is a child relationship and Nullable<Enumerable> does not make sense to entity framework. If entity framework wants to do a .Include(), it tries to instantiate it and can't because it is a null.
Just try creating PosOrcSerProStatus as virtual like this and see if it is solved.
// Instantiate your class and at the same time make PosOrcSerProStatus a new list
public PostOrcamentoServicoProposta()
{
PosOrcSerProStatuses = new List<PosOrcSerProStatus>();
}
// Also make PosOrcSerProStatuses a virtual ICollection
public virtual ICollection<PosOrcSerProStatus> PosOrcSerProStatuses { get; set; }
The only other thing I can think of is that you are closing the DbContext before executing the query within the View...i.e. you are using a using() in your ActionResult and when it gets to the view and you try to do your FirstOrDefault() query the DbContext is closed already.
The following method works properly in my service layer:
public override IList<City> GetAll()
{
var query = from item in _tEntities
select item;
query.Load();
return _tEntities.Local;
}
but when i try to run following method, the method returns old data loaded by GetAll() method.
public override IList<City> GetAll(Func<City, bool> predicate)
{
var query = from item in _tEntities
select item;
query.Where<City>(predicate);
query.Load();
return _tEntities.Local;
}
What is the problem and how can i fix them?
How can i use local method in this sample and reload new data into local(cache)?
You are looking at the wrong problem. What you are most likely seeing is a result of the fact that when you do the first query, the local cache is empty. So it only returns the results from your query. But when you do the second, it's returning the results of your first query AND your second query.
This comes down to the fact that you are using a shared DbContext between all your methods. Local contains a cache of all records the context has retrieved, not just the most recent query.
The correct solution is to not use Local in this manner. Even better, don't use a shared context since this can lead to context cache bloat.
I'm not too sure what you are trying to achieve with a .Load method here but it seems like you want the following.
public override IList<City> GetAll(Func<City, bool> predicate)
{
return _tEntities.Where<City>(predicate).ToList();
}
query.Where<City>(predicate);
This doesn't change query. The query.Load() on the next line ignores the predicate: you're calling query.Load() and not query.Where<City>(predicate).Load(). It's as if you had written
int i = 3;
i + 1;
Console.WriteLine(i); // still prints 3
In that example, C# does not really actually allow an addition to be used as a statement, but .Where(predicate) is a method call, and method calls can be used as such, even if they return values.
This is not your only issue (see the other answers), but my guess is that this issue is the one that leads to the unexpected results you're seeing.