EF6 The operation cannot be completed because the DbContext has been disposed - c#

I know there are a bunch of these error messages in SO because I've read them all, sadly to no avail.
I have a WebApi controller that is getting a set of 'Persons' via EF6 from a SQL Server DB. Very simple example
Things I've tried so far, with no success:
- Disabling proxy generation
- Disabling lazy loading
- Adding Includes to get child refs with both linq and string parameters.
- Replacing the using with try/finally -> dispose of DbContext.
- Remove "application/xml" from supported media types via WebApiConfig
- Ensured circular dependencies are attributed with [IgnoreDataMember]
- ... More I cannot remember :)
Here is the PersonController Get method:
public IEnumerable<Person> Get()
{
try
{
IEnumerable<Person> persons = null;
using (PersonContext entities = new PersonContext())
{
entities.Configuration.ProxyCreationEnabled = false;
entities.Configuration.LazyLoadingEnabled = false;
persons = entities.Persons.Take(5);
}
return persons;
}
catch(Exception ex)
{
...
}
}
Now no exception is thrown at ANY point in the controller. The exception is however displayed in the browser:
"<Error><Message>An error has occurred.<\Message>
<ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type 'application\/json; charset=utf-8'.
<\ExceptionMessage>
<ExceptionType>System.InvalidOperationException<\ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.<\/Message>
<ExceptionMessage>**The operation cannot be completed because the DbContext has been disposed.**<\/ExceptionMessage>
<ExceptionType>System.InvalidOperationException<\/ExceptionType>
<StackTrace> at
System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
The error tells me that something else is trying to read the context after the using clause has popped but I'm at a loss to know what that could be? As you can see I copy the enumerated data from the context into the local list before returning that. Got me stuffed!
Any suggestions appreciated.

The line
persons = entities.Persons.Take(5);
is a definition of how to retrieve data, but the data itself is not yet retrieved at that point ('delayed execution'). The line is located inside the using(){} construct, so right after that the DbContext is disposed. A while later the View needs the data, the DbContext is consulted, but it is closed already.
Solution:
Retrieve all data before closing the DbContext. This is frequently done using ToArray() or ToList(), whichever suits you best.
So the line should be e.g.:
persons = entities.Persons.Take(5).ToArray();

persons = entities.Persons.Take(5).ToList() or ToArray.
You are actually closing the connection before fetching the data.
If this doesn't work, try remove using clause for dbcontext just to check whats happening.

Related

Entity Framework tracks change after second update

The application was built on a bunch of asp .net core mvc and entity framework.
I have a map with markers on it. I want to change the parameters of a certain object through textboxes. The request from the frontend is written in axios, and it works flawlessly. From the first time I get the changes in the database. (mysql, provider: pomelo.mysql).
When I try to access the get request for the first time, I get the old state of the object.
HttpGet request is described here:
public async Task<IEnumerable<Poi>> GetPois()
{
var pois = await _poiService.GetPois();
if (pois.Status == Domain.Enum.StatusCode.Ok)
{
return pois.Data;
}
else { return null; }
}
I have an interface that describes the necessary set of manipulations with the Poi object.
IPoiService is described here:
public interface IPoiService
{
Task<BaseResponse<IEnumerable<Poi>>> GetPois();
Task<BaseResponse<Poi>> GetPoi();
Task<BaseResponse<bool>> DeletePoi();
Task<BaseResponse<Poi>> CreatePoi();
Task<BaseResponse<Poi>> UpdatePoi(Poi entity);
}
The service for working with the Poi object is described here:
public async Task<BaseResponse<IEnumerable<Poi>>> GetPois()
{
try
{
return new BaseResponse<IEnumerable<Poi>>
{
Data = await _poiRepository.GetAll().ToListAsync(),
Status = Domain.Enum.StatusCode.Ok
};
}
catch(Exception ex)
{
return new BaseResponse<IEnumerable<Poi>>
{
Status = Domain.Enum.StatusCode.InternalServerError,
Description = $"[GetPois]: {ex.Message}"
};
}
}
BaseResponse and the corresponding interface represents the response from the database, so it doesn't affect the update problem in any way.
I also have a repository that directly implements instance operations at the database level.
The repository is described here:
public class PoiRepository : IBaseRepository<Poi>
{
private readonly ApplicationDbContext db;
public PoiRepository(ApplicationDbContext db)
{
this.db = db;
db.Database.OpenConnection();
}
public Task Create(Poi entity)
{
throw new NotImplementedException();
}
public Task Delete(Poi entity)
{
throw new NotImplementedException();
}
public IQueryable<Poi> GetAll()
{
return db.Pois;
}
public Poi Update(Poi entity)
{
db.Pois.Update(entity);
db.SaveChanges();
return entity;
}
}
Thus, I get the problem that in order to get the current data, I need to perform two HttpGet requests, and only after that EF Core will return its current value to me.
The reason that Update(entity) sends off warning bells is that you are passing entities between server and client and back. When a controller returns a View(entity) you are sending a reference entity to the view engine to build the view. The view's #Model allows you to apply bindings but it is not a client-side copy of the entity. However, when your form submit or Ajax call etc. calls back with the #model that is NOT an entity, let alone the entity the view engine was given. It will only be a copy of data and only as complete as the view bindings could populate.
So it's hard to deduce what exactly you are witnessing without stepping through the application, but my gut says you are most likely getting confused by what you think is passing entity references around. Think of it this way, in your POST actions you could accept a set of ints, strings, and such for each of the values of the model, or a completely different class definition (DTO/ViewModel) with the same fields as the entity. ASP.Net would attempt to fill in using the data submitted with a Form POST or Ajax call. By accepting an "Entity" you are just telling EF to populate the data into a new untracked entity class. It's not the same instance as a DbContext originally loaded, and the DbContext is a different instance (or should be) than when the entity was originally loaded, it isn't tracking the entity that was originally loaded.
The resulting object will only contain the details that the view happened to have stored in the individual bound controls, pieced back together behind the scenes.
My recommendation is simply to never pass entities to, and especially from a view. Use an explicit ViewModel to represent the state sent to and from a view, then in your Update method:
Fetch the actual entity using the ViewModel ID,
Check a concurrency token (RowVersionNumber / Timestamp) to ensure no changes were made to the DB since you originally fetched the data to populate the View. (optional, but recommended)
Validate the data in your view model
Copy the data from the view model into the Entity. (Automapper can help here)
SaveChanges()
No use of Update or Attach in the DbContext/DbSet.

WebApi Internal Server Error

I have 2 projects, a Front-End (AngularJS) and a Back-End (C# Web-Api). The thing is, when the front-end query the api (e.g GET localhost/api/Especialistas?rol=XXXX) I get a 500 error. Here is the code of the API:
public IHttpActionResult GetEspecialista(string rol)
{
Especialista especialista = db.Especialistas.First( e=> e.Rol == rol);
if (especialista == null)
{
return NotFound();
}
return Ok(especialista);
}
The API is working, since I reach the return Ok(especialista).
The Front-end is using this Restangular to query the API
Restangular.one("Especialistas?rol=XXXX").get()
The Network console shows a request 200 OK OPTION, but a 500 Internal Server Error GET.
I tried a message handler in the WebApiConfig.cs file to check if the GET request was reaching the Api, and is indeed reaching it, so I don't know what happened, since I didn't change any configuration file.
Any clue on how to fix this problem will be appreciated, thanks.
If your action is called successfully, but still receive a 500 error, I think the error is created by the serializing of especialista object when converted to a HTTP response.
Most probably, serialization fails because of some navigation properties which creat cycles in your object graph. It is recommended to return simple objects, not entity framework models.
Try the following:
var retObj = new { Prop1 = especialista.Prop1, Prop2 = especialista.Prop2 };
return Ok(retObj);
If above code works, I suggest creating service models "mirror" objects that should be populated based on your data models. These objects should be returned instead of data models.
A good helper to avoid the boilerplate code of property value assignments is Automapper.

Put Attempted on document using non current Etag error

I am going through the popular Pro ASP.NET MVC 5 book and building the Sports Store application, but I am using RavenDb instead of Entity Framework.
Initially, I created the Product records in Raven through the local API (not through code). I gave the records a manual Id and created the json fields and values for each product - 9 in total. I then wrote the parts of the application that loaded these products and everything worked totally fine. The data returned exactly as I expected every time.
However, when I got to the part of the application that allowed users to create new records through the MVC interface, I got a crash when calling the SaveChanges() function on IDocumentSession.
This is the full text of the error:
Server Error in '/' Application.
PUT attempted on document 'products/9' using a non current etag
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: Raven.Abstractions.Exceptions.ConcurrencyException: PUT attempted on document 'products/9' using a non current etag
This is the full text of the Save function I am calling:
private readonly IDocumentSession _session;
public void Save(Product product)
{
_session.Store(product);
_session.SaveChanges();
}
_session is initialized in the repository constructor like this:
public ProductRepository()
{
_session = RavenDbStoreSingleton.DocumentStore.OpenSession();
}
And this all interacts with a singleton for DocumentStore:
public class RavenDbStoreSingleton
{
private static readonly Lazy<IDocumentStore> Store = new Lazy<IDocumentStore>(CreateStore);
public static IDocumentStore DocumentStore => Store.Value;
private static IDocumentStore CreateStore()
{
IDocumentStore store = new DocumentStore()
{
Url = "http://localhost:8080",
DefaultDatabase = "SportsStore"
}.Initialize();
return store;
}
}
So my guess is that my manually entering ALL of the first records and then trying to save new records somehow caused this error, but I'm not sure why Raven can load the Products but can't seem to save without hitting conflicting Ids. I even went and increased the Hilo for the Products (which is the only document type) from 32 to 64, but that didn't affect anything.
Update: I noticed that each time I got this error, it conflicted on a higher Id than it did previously. I was able to keep attempting to save until the error no longer occurred, at which point the Id generated was Products/65, which makes sense as I had modified the Hilo from 32 to 64. It did not try to generate Products/10.
However, I don't understand why modifying the Hilo halfway through my 9 crashes didn't make Raven start generating at Products/65 and instead it kept trying to generate already taken Ids.
The reason why it did it was that it was caching the hilo value, and until it run out of the hilo range, it had no reason to request a new range.
The hilo doesn't go to the server on every request.

System.ObjectDisposedException when trying to list an object

Trying to fix this error
public ActionResult MyEvents()
{
Personne personne = (Personne)Session["User"];
personne.Evenements.ToList();
return View(personne);
}
Got the System.ObjectDisposedException and Evenements is null
Using many to many relation between Personne and Evenement so cannot read the join table in the db.
The problem is that the Evenements are probably being loaded using lazy loading (i.e. not retrieved until the property is accessed).
When you are coding web applications the connection used to fetch information is usually closed when the HTTP request ends.
Thus when you on the second request try to access Evenements property the connection have been closed. And you therefore get the ObjectDisposedException.
You can activate eager loading by using the Include method: http://www.entityframeworktutorial.net/EntityFramework4.3/eager-loading-with-dbcontext.aspx
That way the property have been loaded and stored for all future requests.

Best practice for getting single record from WCF DataService Client

I have DataService where T is an EntityFramework DbContext class
My client app is a Windows Forms app with a Service Reference.
What is the best approach to get a single entity from the service?
This code works:
var uri = new Uri("http://localhost/ProductService.svc/");
var context = new ProductContext(uri);
var result = context.Products.Where(x => x.ProductId == 123).FirstOrDefault();
However, it works because the product exists. That is because I can see the result by executing
http://localhost/ProductService.svc/Products(123)
in the browser. If I want to query product 123456, which does not exist in the database
http://localhost/ProductService.svc/Products(123456)
I see an errortext ` Resource not found for the segment 'Products'
The thing is, on the client side I get an exception but I would expect FirstOrDefault() to be null instead. Sure I could use some exception handling, but I am wondering if my approach is correct or if there is a better way to fetch a single object.
Update: Found the solution here https://stackoverflow.com/a/5987733/98491
The key is to set
context.IgnoreResourceNotFoundException = true;
Now SingleOrDefault() and FirstOrDefault() behave like I would expect. But I am still wondering if this is the right decision because in a browser
http://localhost/ProductService.svc/Prodducts(1)
(notice the typo) throws the same ResourceNotFound exception

Categories

Resources