Exception of type 'System.ObjectDisposedException' - c#

I have following EF code first code. It is working fine. But when I look for the value of 'club2.Members', it says following:
'club2.Members' threw an exception of type 'System.ObjectDisposedException'.
Message is:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I have not set value for Members property. That’s okay. However it should not cause any exception internally.
Is there a way to overcome this exception?
Does removal of this exception help in improving performance?
Why this exception did not cause termination of program execution?
Note: I don't need the Members data in club entity (at the point where I have called). Even there is no members added to the club at that time. All i need is to get rid of the exception; not to load data using eager load. How to avoid the exception?
namespace LijosEF
{
public class Person
{
public int PersonId { get; set; }
public string PersonName { get; set; }
public virtual ICollection<Club> Clubs { get; set; }
}
public class Club
{
public int ClubId { get; set; }
public string ClubName { get; set; }
public virtual ICollection<Person> Members { get; set; }
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
//Fluent API - Plural Removal
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Person> Persons { get; set; }
public DbSet<Club> Clubs { get; set; }
}
}
static void Main(string[] args)
{
Database.SetInitializer<NerdDinners>(new MyInitializer());
CreateClubs();
CreatePersons();
}
public static void CreateClubs()
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
Club club1 = new Club();
club1.ClubName = "club1";
Club club2 = new Club();
club2.ClubName = "club2";
Club club3 = new Club();
club3.ClubName = "club3";
db.Clubs.Add(club1);
db.Clubs.Add(club2);
db.Clubs.Add(club3);
int recordsAffected = db.SaveChanges();
}
}
public static Club GetClubs(string clubName)
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
var query = db.Clubs.SingleOrDefault(p => p.ClubName == clubName);
return query;
}
}
public static void CreatePersons()
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
Club club1 = GetClubs("club1");
Club club2 = GetClubs("club2");
Club club3 = GetClubs("club3");
//More code to be added (using db) to add person to context and save
}
}

You're getting this error because you are disposing the dbcontext after you execute the query. If you want to use the data in "members" then load it with the query:
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
If you don't need lazy loading, which means that only data you're loading in the query is loaded (this will eliminate the exception), set LazyLoadingEnabled = false. Look here:
Disable lazy loading by default in Entity Framework 4

APPROACH 1
There is no need for the following method. This can be done in the present context. It is only a select.
GetClubs("club1");
APPROACH 2
Attaching the entities resolved the problem:
Club club1 = GetClubs("club1");
Club club2 = GetClubs("club2");
Club club3 = GetClubs("club3");
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club1);
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club2);
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club3);
var test = club2.Members;
I should not have tried to refer club2.Members (of Club class), since it is not needed for creating Person object.
Members is related data in Club class. Since it is virtual, it will do lazy loading.
In a different scenario, if I had to retrieve Members object (which is a related data) from a different context, I should rely on eager loading (in the place which provides you with Club object).
Refer following also:
Entity Framework: Duplicate Records in Many-to-Many relationship
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
ObjectContext instance has been disposed while binding

Related

I get an error when I want to select in constructor in ASP.NET Core Web API

I get an error in my ASP.NET Core Web API project when want to select all content:
System.InvalidOperationException: 'A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext
This is my contentService:
private IContentRepository contentRepository;
private DbContext_CMS dbContext;
public ContentService(IContentRepository contentRepository, DbContext_CMS dbContext )
{
this.contentRepository = contentRepository;
this.dbContext = dbContext;
}
public IEnumerable<ContentInfoDTO> GetAllContent()
{
try
{
IQueryable<Content> contents = contentRepository.GetAll();
IEnumerable<ContentInfoDTO> result = contents.Select(content => new ContentInfoDTO(content)).ToList();
return result;
}
catch (Exception ex)
{
return null;
}
}
This is ContentInfoDTO.cs:
public class ContentInfoDTO
{
public ContentInfoDTO(Content content)
{
try
{
this.Id = content.Id;
this.Title = content.Title;
this.Description = content.Description;
this.BodyHtml = content.BodyHtml;
this.ContentCategories = content.ContentCategories.Select(category => new CategoryInfoDTO(category.FkCmsCategory)).ToList(); //this line error
}
catch (Exception ex)
{
throw;
}
}
public int Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public string? BodyHtml { get; set; }
public ICollection<CategoryInfoDTO>? ContentCategories { get; set; }
}
This is my CategoryInfoDTO:
public class CategoryInfoDTO
{
public CategoryInfoDTO(Category? category)
{
if (category != null)
{
this.Id = category.Id;
this.Title = category.Title;
this.Description = category.Description;
this.FkParentId = category.FkParentId;
}
}
public int Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public int? FkParentId { get; set; }
}
And this is my dbContext configured:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("connectionString");
}
}
I also fixed dependency injection, but it didn't work.
service.AddDbContext<DbContext_CMS>();
In contentService:
IEnumerable<ContentInfoDTO> result = contents.Select(content => new ContentInfoDTO(content)).ToList();
in the Select method you start by getting Content entities from the database. At the moment you start getting your first Content, the DbContext instance provided to you by Dependency Injection is in use.
Next, while still inside the Select method, you take the Content entity, create a new ContentInfoDTO object and pass the entity into the constructor. In the constructor of ContentInfoDTO, specifically at the line you get your error,
this.ContentCategories = content.ContentCategories.Select(category => new CategoryInfoDTO(category.FkCmsCategory)).ToList();
you access the ContentCategories property, which is a navigation property belonging to Content. Since the Content entity you passed into the constructor was provided by EF Core, it is being tracked by EF Core, which means when you tried to perform an operation on ContentCategories property, EF Core tried to perform a lazy loading of all the Category entities related to that Content instance. However, since we're still inside the first Select method and the DbContext instance is still in use there, we tried to access the same DbContext from different places at the same time, hence the error.
You could force the retrieval of the ContentCategories collection earlier using the Include method and see if that solves the problem - see eager loading.
Also, I don't think you need to inject your DbContext in contentService, since you seem to use the repository pattern and retrieve your entities from there, I assume the DbContext is already injected into your repository instance.
Edit : it should be lazy loading instead of explicit loading. Also that's assuming that the lazy loading related package is installed and configuration has been done and that the navigation property is virtual. If your situation doesn't cover any of the requirements, your error may be from something else

C# Entity Framework (Code first), Implementing CRUD Operations on Model

I just wondering if you can store a function in a model (CRUD transactions)
that will look something like this:
My Existing code:
public class tbluser
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
using (var db = new sample())
{
var user = db.tbluser.ToList();
return user;
}
}
}
What i want:
public class tbluser:DbContext
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
return this.toList();
}
}
I just want to ask also if that method of implementing Entity Framework is ok.
Here is a quick example of how you might setup a simple Code First implementation to get started.
First, define your User model. The Key attribute on an integer type automatically configures the identity property for you. Then, you may want an index on username if you plan to do frequent lookups by username (to get user details or to validate a password).
public class User
{
[Key] // Becomes identity by default
public int Id { get; set; }
[Index("IX_User_Username", IsUnique = true)]
public string Username { get; set; }
public string Password { get; set; }
}
Then, you can define
public class AppDataContext : DbContext
{
public AppDataContext() : base("name=DBConnection") { }
public DbSet<User> Users { get; set; }
}
You will just need to be sure there is a connection string in your config file to match the name passed there.
<connectionStrings>
<add name="DBConnection" providerName="System.Data.SqlClient"
connectionString="Data Source=instancePath;Initial Catalog=dbName;Integrated Security=true;MultipleActiveResultSets=True" />
</connectionStrings>
This would now allow you to create repos such as this:
public class UserRepo : IDisposable
{
public Lazy<AppDataContext> _db = new Lazy<AppDataContext>(() => new AppDataContext());
public IQueryable<User> Get() => _db.Value.Users.AsQueryable();
public IList<User> GetAll() => _db.Value.Users.ToList();
public void Dispose()
{
if (_db.IsValueCreated)
_db.Value.Dispose();
}
}
So then you can either use the repo or the context directly.
// Use the repo
using (var userRepo = new UserRepo())
{
var allUsers = userRepo.GetAll();
var user = userRepo.Get().FirstOrDefault(m => m.Username == "myUsername");
}
// Or just use the data context
using (var db = new AppDataContext())
{
var allUsers = db.Users.ToList(); // Get all users
var user = db.Users.FirstOrDefault(m => m.Username == "myUsername");
}
For more information, here are some useful links with great details:
Simple Example
Data Annotations
Initializer Config
Migrations
Code like this is going to be heavily problematic.
In the first example you are tightly coupling an instance of a DbContext to an entity. Calling tblUser.list() will return a list of User entities, but these will now be outside of the scope of a DbContext. (Due to the using() block closure) This means that any lazy load calls to retrieve related entities will fail and you cannot persist any changes to the entities until they are re-attached to another DbContext. This gets very messy, very fast.
In the second example you would be extending a DbContext, meaning each "entity" is effectively scoping a DbContext use to populate instances of itself. You can't just "static" wrap the method because that wouldn't have visibility to the non-static DbSets inherited from DbContext.
This would be horrible in terms of performance, and from a code perspective would look plain weird:
I.e.
using (var user = new tbluser)
{
var users = user.list(); // not static.
// .. Do stuff..
}
To make it static would be problematic because a DbContext would need to be static-scoped inside tbluser
public class tbluser
{
private static MyContext _context = new MyContext();
// ...
public static List<tbluser> list()
{
return _context.tblusers.ToList();
}
}
And this may still have issues, such as how the static instance is disposed, before it was remotely functional but I certainly cannot recommend an approach like this.
Instead, use the DbContext as it is intended. Look at IoC containers like Unity or Autofac to manage the lifetime scope for for the DbContext and inject an instance as a dependency into classes that need it, or at a minimum wrap it in a using() {} block and treat it like an repository with it's DbSets.
There are lots of examples of using the DbContext effectively, using Repositories and Unit of Work patterns with dependency injection. Master these before attempting to spin up something unique. Future developers looking at your code will thank you. :)
There is one famous principle called "Separation of Concerns" that will get very angry if you do this. My advice is to keep the code simple, meaningful and loosely coupled.

EF first database change connection string

Hi I have server with some databases that have the same schema. I use EF6 Database/Model First code and I do not want to create deterrent DbContext for them. for example my generated DbContext is :
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities()
: base("name=TEST_Rev5_FINALEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Header> tbl_Headers { get; set; }
public virtual DbSet<Output> tbl_Output { get; set; }
public virtual DbSet<Run> tbl_Run { get; set; }
}
and I created a partial class to set the connection string
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities(DbConnection dbConnection)
: base(dbConnection, true)
{
}
}
And I have the following method to create the connection with deterrent connection string:
public DbConnection GetConnectionString()
{
DbConnection conn;
SqlConnectionStringBuilder sqlConnectionStringBuilder = new SqlConnectionStringBuilder
{
DataSource = DataSource,
IntegratedSecurity = false,
UserID = User,
Password = Password,
MultipleActiveResultSets = true
};
SqlConnectionFactory sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilder.ConnectionString);
conn = sqlConnectionFactory.CreateConnection(DatabaseName);
return conn;
}
Finally I try to run it like this:
using (var context = new TEST_Rev5_FINALEntities(_dal.Connector.GetConnectionString()))
{
return context.tbl_Headers.FirstOrDefault();
}
but I get this error :
System.Data.Entity.Infrastructure.UnintentionalCodeFirstException
HResult=0x80131509 Message=The context is being used in Code First
mode with code that was generated from an EDMX file for either
Database First or Model First development.
How can I do it?
The behavior EF uses depends on the way your connection string looks. If it includes a metadata attribute like this:
metadata=res://*/model.csdl|res://*/model.ssdl|res://*/model.msl;
It will presume you are using Database or Model first development.
To make sure Code First is used, remove metadata part of the connection string.

EF6 lazy loading doesn't work while eager loading works fine

I updated my app from EF4.1 to EF6 and now I've got lazy loading issue. I used EF6.x DbContext Generator to generate new DbContext. All of the advices from this article are also applied.
My classes are public
Not sealed, not abstract
Have a public constructor
Don't implement neither IEntityWithChangeTracker nor IEntityWithRelationships
Both ProxyCreationEnabled and LazyLoadingEnabled are set to true
Navigation properties are virtual
What also looks wierd to me is that if I explicitly include navigation property with Include("...") it gets loaded.
Simplified version of my POCOs and DbContext:
public partial class Ideation
{
public Ideation()
{
}
public long Id { get; set; }
public Nullable<long> ChallengeId { get; set; }
public virtual Challenge Challenge { get; set; }
}
public partial class Challenge
{
public Challenge()
{
this.Ideations = new HashSet<Ideation>();
}
public long Id { get; set; }
public virtual ICollection<Ideation> Ideations { get; set; }
}
public partial class BoxEntities : DbContext
{
public TIBoxEntities()
: base("name=BoxEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Ideation> Ideations { get; set; }
public virtual DbSet<Challenge> Challenges { get; set; }
}
Also I tried to set ProxyCreationEnabled and LazyLoadingEnabled explicitly without no luck. The entity isn't loaded as a dynamic proxy as this debug session screenshot shows:
What else am I missing?
A situation where this could happen is that the entity you are trying to load with Find is already attached to the context as a non-proxy object. For example:
using (var context = new MyContext())
{
var ideation = new Ideation { Id = 1 }; // this is NOT a proxy
context.Ideations.Attach(ideation);
// other stuff maybe ...
var anotherIdeation = context.Ideations.Find(1);
}
anotherIdeation will be the non-proxy that is already attached and it is not capable of lazy loading. It even wouldn't help to run a DB query with var anotherIdeation = context.Ideations.SingleOrDefault(i => i.Id == 1); because the default merge option for queries is AppendOnly, i.e. the new entity would only be added if there isn't already an attached entity with that key. So, anotherIdeation would still be a non-proxy.
You can check if the entity is already attached by using Local before you call Find in your GetById method:
bool isIdeationAttached = context.Ideations.Local.Any(i => i.Id == id);
Per #ken2k's comment, the default for new models starting with EF 4 was to enable lazy loading by default. With EF 1, it was not allowed. If you migrated your model from 1 to 4, it is kept off by default. You would need to modify the context to turn that on.
That being said, you indicate through debugging that it is true. In that case, check your usage scenarios. Is it possible your context has been disposed prior to fetching the child objects. Typically we see this when data binding to the LINQ query where the context is configured in a Using block and the actual iteration doesn't happen until after the using block's scope has passed.

Using Base Entity Context for code first Entity Framework

TLDR: Why is Entity Framework appending extra path to the end of my datasource? What do I need to do in order to use this framework? Is there another way to handle this?
I am writing a basic SQL Server CE winforms program to store some data. I originally had 1 context to be used to interact with 1 class. Now, I wish to add another class, and so I refactored out a base context that the original context could be derived from. I have the code running, although I can't create any database with it.
I take a path in from the user to the database and set the |DataDirectory|, which I use for the data source.
<add name="MonsterContext"
connectionString="Data Source=|DataDirectory|; Persist Security Info=False"
providerName="System.Data.SqlServerCe.4.0" />
I intend for the data source to look like
C:Path\To\DatabaseFile.sdf
but it comes out as
C:\Path\To\DatabaseFile.sdf\Namespace.BaseContext`1[Namespace.ModelClass].sdf ]"}
I think this path demonstrates that it is building its own source based on EF defaults.
My base Context is:
public class EntityContext<T> : DbContext where T : class
{
public string TableName { get; set; }
public EntityContext()
: base("name=MonsterContext")
{
}
public EntityContext(string tableName)
{
this.TableName = tableName;
}
public DbSet<T> Entities { get; set; }
}
And My Derived Context is:
public class MonsterReader : Reader<Monster>
{
private List<Monster> monsters;
public List<Monster> Monsters
{
get
{
if (monsters == null)
{
monsters = ReadAll();
}
return monsters;
}
private set { monsters = value; }
}
public MonsterReader(string file)
: base(file)
{ }
public MonsterReader(Stream reader)
: base(reader)
{ }
public void CreateMonsterDatabase(IEnumerable<Monster> monsters)
{
CreateDatabase(monsters, TableName);
}
private string TableName { get; set; }
}
In this problem, it was a simple omission from the base context.
In the Base Entity Context, the first constructor is:
public EntityContext()
: base("name=MonsterContext")
{
}
However, the constructor that takes a table name is
public EntityContext(string tableName)
{
this.TableName = tableName;
}
which is missing the : base("name=MonsterContext")
This call is necessary if you want to call a specific connection string from the app.config.
In this case, the connection string you want to call is "Monster Context". Without specifying which context, Entity Framework defaults using SQL Server CE to
|DataDirectory|\Namespace.NameofContext.sdf

Categories

Resources