Entity Framework and Web API ObjectDisposedException - c#

I have a data layer that uses Entity Framework 5 to connects to Sql Server. I also have a LINQ query that gets some data. This query fails when used with Web API. I get the ObjectDisposedException. Here's the query:
using (MyContext container = new myContext())
{
return container.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
}
The data layer is a dll, exposed by a business layer, also a dll, which is called by a web api controller. I had assumed that the JSON serializer was lazy loading the includes but none of my fixes have worked. Any ideas? I will update the question as need be if info is missing.
Here is the business layer call to the data layer:
public Sale GetSale(int id)
{
SaleRepository s = new SaleRepository();
return s.GetSale(id);
}
And finally the web api call to the business layer:
public Sale GetSale(int id)
{
SaleManager s = new SaleManager();
return s.GetSale(id);
}
Here is the exception:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

This happens because lazy loading is being performed after your DbContext has been disposed.
To resolve the issue you can disable lazy loading by doing this:
container.Configuration.LazyLoadingEnabled = false;

There must be an issue with lazy loading and the include not working as expected - try changing your code to select the actual object and the included entities.
using (MyContext container = new myContext())
{
var result = container
.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
result.Products.ToList();
return result;
}

This is an issue with lazy loading. The more appropriate way to do this is to not dispose the DbContext till the request is done processing. You can do it in a couple of ways,
1) dispose it in the controller. framework ties the controller's life time with the requests life time.
public class MyController : ApiController
{
private SaleManager _saleManager = new SaleManager();
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_saleManager.Dispose();
}
}
}
2) explicitly tie the resource with the request, (useful in cases where you are not inside a controller) using Request.RegisterForDispose()
SaleManager saleManager = new SaleManager();
Request.RegisterForDispose(saleManager);

Related

How to handle on-demand data fetching for Domain Models

Given the following scenario...
I am concerned about two things...
1) Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
2) Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
If this design is incorrect then someone please tell me how should business models be loaded.
//Business model.
interface IFolder
{
int Id { get; }
IEnumerable<IFolder> GetSubFolders();
}
class Folder : IFolder
{
private readonly int id_;
private readonly IFolderDataProvider provider_;
public Folder(int id, IFolderDataProvider provider)
{
id_ = id;
provider_ = provider;
}
public int Id { get; }
public IEnumerable<IFolder> GetSubFolders()
{
return provider_.GetSubFoldersByParentFolderId(id_);
}
}
interface IFolderDataProvider
{
IFolder GetById(int id);
IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id);
}
class SqlFolderDataProvider : IFolderDataProvider
{
private readonly DbContext context_;
public SqlFolderDataProvider(DbContext context)
{
context_ = context;
}
public IFolder GetById(int id)
{
//uses the context to fetch the required folder entity and translates it to the business object.
return new Folder(id, this);
}
public IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id)
{
//uses the context to fetch the required subfolders entities and translates it to the business objects.
}
}
Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
Yes, how else would you be able to call the provider and get the data?
However, the suffix DataProvider is very confusing because it is used for the provider that you use to connect to the database. I recommend changing it to something else. Examples: Repository, Context.
Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
It won't necessarily live on forever. You decide its life span in your ConfigureServices function when you're adding it as a service, so you can change its scope from Singleton to whatever you like. I personally set the scope of my DBContext service to Transient and I also initiate it there with the connection string:
services.AddTransient<IDbContext, DbContext>(options =>
new DbContext(Configuration.GetConnectionString("DefaultDB")));
I then open and close the database connection in every function in my data layer files (you call it provider). I open it inside a using() statement which then guarantees closing the connection under any condition (normal or exception). Something like this:
public async Task<Location> GetLocation(int id) {
string sql = "SELECT * FROM locations WHERE id = #p_Id;";
using (var con = _db.CreateConnection()) {
//get results
}
}
Is it okay to inject a provider into a business model object
Yes if you call it "business" provider :). Actually do not take too serious all this terminology "inject", "provider". Till you pass (to business model layer's method/constructor) interface that is declared on business model layer (and document abstraction leaks) - you are ok.
should I instantiate the context in the constructor?
This could be observed as an abstraction leak that should be documented. Reused context can be corrupted or can be shared with another thread and etc -- all this can bring side effects. So developers tend to do create one "heavy" object like dbContext per "user request" (that usually means per service call using(var context = new DbContext()), but not always, e.g. Sometimes I share it with Authentication Service Call - to check is the next operation allowed for this user). BTW, DbContext is quite quick to create so do not reuse it just for "optimization".

EF Core multiple HTTP requests throws an error

I cannot seem to find an answer to this question.
So in the frontend when the user loads a page we call an API for each item on that page (10 items). So that equals 10 API calls.
Most of the calls work but there are always a few that fail when trying to query the database resulting in the following error:
InvalidOperationException: A second operation started on this
context before a previous operation completed. Any instance members
are not guaranteed to be thread safe.
Now I understand that Entity Framework is not thread safe but I am unsure how to get around this error.
Everywhere where I am using a DBContext it is always injected in using the built in .net core Ioc container.
Here is the DI setup
services.AddScoped<IOmbiContext, OmbiContext>();
services.AddTransient<ISettingsRepository, SettingsJsonRepository>();
All of my repositories are setup in a Transient scope with the Context as Scoped according to this article: https://learn.microsoft.com/en-us/aspnet/core/data/entity-framework-6
Now I have tried changing the context to Transient and it still happens.
How can I avoid this?
More Information
The API Method:
[HttpGet("movie/info/{theMovieDbId}")]
public async Task<SearchMovieViewModel> GetExtraMovieInfo(int theMovieDbId)
{
return await MovieEngine.LookupImdbInformation(theMovieDbId);
}
Which eventually calls the following where the exception is being thrown:
public async Task<RuleResult> Execute(SearchViewModel obj)
{
var item = await PlexContentRepository.Get(obj.CustomId); <-- Here
if (item != null)
{
obj.Available = true;
obj.PlexUrl = item.Url;
obj.Quality = item.Quality;
}
return Success();
}
PlexContentRepository
public PlexContentRepository(IOmbiContext db)
{
Db = db;
}
private IOmbiContext Db { get; }
public async Task<PlexContent> Get(string providerId)
{
return await Db.PlexContent.FirstOrDefaultAsync(x => x.ProviderId == providerId); <-- Here
}
If you use Entity Framework Core usually you do not need to add your Database Context as an additional service
I recommend to setup your DbContext in the Startup.cs as following:
services.AddEntityFrameworkSqlServer()
.AddDbContext<OmbiContext>();
Followed by a Controller class for your API calls taking the DBContext as constructor parameter.
public class ApiController : Controller
{
protected OmbiContext ctx;
public ApiController(OmbiContext dbctx)
{
ctx = dbctx;
}
public async Task<IActionResult> yourAsyncAction()
{
// access ctx here
}
}

How to properly inject a DbContext for WebAPI using a ContextFactory and a repository?

I'm trying to safely inject a database context per web request for a repository on the back of a Web API. The consuming class calls the repository in order to retrieve the object, and if it comes back null, then it gets the object from a different data store and saves that in the database for faster access later. This means it's trying to Get from the DB then doing stuff then creating a new record.
Currently I have this repository
public class OrganisationRepository : IOrganisationRepository
{
public Func<IOrganisationDomainDbContext> ContextFactory { get; set; }
public Organisation GetDetailByIdentifier(int id)
{
using (var context = ContextFactory.Invoke())
{
var org = context.Organisations.SingleOrDefault(x => x.Id == id);
return org;
}
}
public void Create(Organisation orgToCreate)
{
using (var context = ContextFactory.Invoke())
{
context.Organisations.Add(orgToCreate);
context.SaveChanges();
}
}
}
and the repository is injected into the consuming class with a transient lifestyle. The DbContext is injected per web request.
Previously, the Repository was injected with a singleton lifestyle, which was breaking on the Create action.
My question is, am I doing a cheap hack by making the Repository transient? Will this cause me problems down the line? If so, how should I be doing this differently?
EDIT: For further information, the DI Container in use is Castle Windsor
EDIT: Relevant parts of the DI Installer
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IOrganisationDomainDbContext>().ImplementedBy<OrganisationDomainDbContext>().LifeStyle.PerWebRequest,
Component.For<Func<IOrganisationDomainDbContext>>().Instance(container.Resolve<IOrganisationDomainDbContext>),
Component.For<IOrganisationRepository>().ImplementedBy<OrganisationRepository>().LifeStyle.Transient,
Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
}
UPDATE: Transient repository did not fix the problem, that was a mistake on my part - I just forgot that the record I was looking at had in fact been committed to the database, and therefore the create action was not being called. My mistake, apologies.
As user jbl pointed out, I didn't need to use using blocks around the context usage - the DI Container disposes of the context as it is used per web request.
Here's the code now:
public class OrganisationRepository : IOrganisationRepository
{
public Func<IOrganisationDomainDbContext> ContextFactory { get; set; }
public Organisation GetDetailByIdentifier(int id)
{
var context = ContextFactory.Invoke()
var org = context.Organisations.SingleOrDefault(x => x.Id == id);
return org;
}
public void Create(Organisation orgToCreate)
{
var context = ContextFactory.Invoke();
context.Organisations.Add(orgToCreate);
context.SaveChanges();
}
}
And the DI container installer:
container.Register(
Component.For<IOrganisationDomainDbContext>().ImplementedBy<OrganisationDomainDbContext>().LifeStyle.PerWebRequest,
Component.For<Func<IOrganisationDomainDbContext>>().Instance(container.Resolve<IOrganisationDomainDbContext>),
Component.For<IOrganisationRepository>().ImplementedBy<OrganisationRepository>().LifeStyle.PerWebRequest,
Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
Hopefully this is the correct answer, debugging shows that the contexts are disposed of after each web request and I'm not running into the exceptions. Thanks for all the help jbl and Mark Seeman.

The context cannot be used while the model is being created exception [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I've got the following error using Entity Framework 6.0.0.0.
The context cannot be used while the model is being created.
This exception may be thrown if the context is used inside the
OnModelCreating method or
if the same context instance
is accessed by multiple threads concurrently.
Note that instance members of DbContext and
related classes are not guaranteed to be thread safe.
It is fired inside an async method
,upd:
public async Task<IList<Model>> GetEntities(filter f)
{
using (var db = new MyDbContext())
{
var query = db.MyDbSet.AsQueryable();
if (f != null && f.field.Any())
//Exception throwed here
query = query.Where(a => f.field.Contains(a.field));
return await query.ToListAsync();
}
}
But any await calls or other multithreading operation is not performed when i try to find my entities by Where clause.
Are any suggestions related to that issue ? I found a lot of answers but didn't found their helpful for me.
If your management studio has been able to connect successfully to the database
then do this. Run your management studio and before you connect to the database
copy out the full "Server name" and use this to replace your "host" and confirm that the actual name of the particular database you are trying to connect to is called "database" and if not replace "database" with that name. Finally if you management studio requires a user name and password to connect to the database then you have to include those as well. From the error you are getting the problem is connecting to your database. Let me know how it goes.
You want to implement a proper method of connecting to your database that disposes the connection, or uses a term called "dependency injection" - creating an interface and instance of the DataContext that is able to be accessed at any time by any function in the controller. Here are examples of the 2 methods:
Dispose Architecture
This architecture requires that you declare your DataContext before any other function, and add a Dispose function in the Controller at the end.
Some specific oddities I found and fixed in the code:
You should be returning a ViewModel or a data model that represents a database table or view you are querying, in this case MyDbSet, not Model.
You should be using the db instance declared generically for the class.
You should define what filter is. An array? A model? A string? I'm going to assume it's a list of strings like this: string[] filter = { "Fruits", "Meats", "Dairy" };
The Where clause might not be correct on your query, but again, I don't know what your filter looks like. There's other examples here: https://learn.microsoft.com/en-us/dotnet/api/system.linq.queryable.where?view=netframework-4.8
MyDbContext db = new MyDbContext();
public async Task<IList<MyDbSet>> GetEntities(filter f)
{
using (db)
{
var query = null;
if (f != null && f.field.Any())
{
query = db.MyDbSet.AsQueryable().Where((a, index) => a == f);
}
return await query.ToListAsync();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
Dependency Injection
I describe this process thoroughly in another SO post, here:
How to inject my dbContext with Unity
The main difference is you would do something like this in your controller's GetEntities function to get the data:
query = repository.GetMyDbSet.Where((a, index) => a == f);
Mostly the same as above, but you don't need the using(db) { ... } statement, anymore.
You would have an interface:
public interface IDataRepository
{
void SaveChanges();
IEnumerable<MyDbSet> GetMyDbSet();
}
and a repository class function to return the data to you:
public IEnumerable<MyDbSet> GetMyDbSet()
{
return context.MyDbSet;
}
And context is defined in a Repository class:
public class DataRepository : IDataRepository
{
private bool disposing;
private readonly MyDbContext context;
public virtual void Dispose()
{
if (disposing)
{
return;
}
disposing = true;
if (context != null)
{
context.Dispose();
}
}
public void SaveChanges()
{
context.SaveChanges();
}
public DataRepository()
{
context = new MyDbContext();
context.Configuration.ProxyCreationEnabled = false;
}
public IEnumerable<MyDbSet> GetMyDbSet()
{
return context.MyDbSet;
}
}
Hopefully this helps you or someone.

Entity Framework - Load Reference Keys after disposing entity object context

I am using ASP.Net / WebForms / Entity Model / Framework 3.5
Here is my project's simple structure
Forms > BLL > DAL ( uses entity model )
Here is my DAL's snippet
public class MyDAL : IDisposable
{
private MyEntities db;
public BaseDAL()
{
db = new MyEntities();
}
public User GetUserByID(int userId)
{
try
{
IQueryable<User> objUser = null;
objUser = from res in db.Users
where res.UserId == userId
select res;
return objUser.FirstOrDefault();
}
catch
{
throw;
}
}
public void Dispose()
{
db.Dispose();
}
}
I call the DAL's function from my BLL like this
public class MyBLL
{
public User GetUserByID(int userId)
{
try
{
using (MyDAL objMyDAL = new MyDAL())
{
return objMyDAL.GetUserByID(userId);
}
}
catch
{
throw;
}
}
}
I am calling the DAL through using block so MyDAL's Dispose event will fire soon after the BLL returns the User object. So at this point ObjectContext instance gets disposed.
Now in my Web Form, I am calling this function like this to get user information and Group details which is a foreign key of user_Group Table in User table
protected void Page_Load(object sender, EventArgs e)
{
MyBLL objMyBll = new MyBLL();
User objUser = objMyBll.GetUserByID(123);
objUser.User_GroupReference.Load(); // ERROR LINE
int groupId = objUser.User_Group.Group_Id;
}
When the ode comes on line objUser.User_GroupReference.Load(); I get this exception
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
How to resolve this? If I do not do db.Dispose(); in my DAL's dispose method, it works fine and no exceptions comes. But if I do not dispose the db object there, when & where should I dispose it?
And how to access Reference Keys after disposed object context?
The exception is fired because lazy loading is fired when you access that navigation property but lazy loading works only within scope of context used to load the entity. If you dispose the context you will lose lazy loading ability. There is no way to use lazy loading after context disposal (except attaching the entity to the new context but it will only work if you detach it from the original context before you dispose it).
In your architecture you must use Include to explicitly load every relation you will need in upper layers. If you want to use lazy loading your context must live for the whole duration of the request. In case of web forms it can be handled for example in BeginRequest and EndRequest event handlers where you create context in BeginRequest and dispose it in EndRequest. The context will be stored in HttpContext.Items. You should get the context from this collection (you can make helper method for that) and pass it to constructor of BLL which will in turn pass it to DAL. Don't access HttpContext.Items from BLL or DAL.

Categories

Resources