I'm currently learning the Entity Framework (EF) through the Code First approach. I want to model a simple one-to-many relationship between Professor
s and Class
es on a newly created database (i.e. created by the EF).
In my model a Professor
has a collection of Class
es. A Class
always has one Professor
associated with it (i.e. the corresponding property is never null). I can create and store a Class
and an associated Professor
, but when I access the Classes
property of a Professor
retrieved from the database, it is empty. Navigating from a Class
to its Professor
, however, is possible.
Consider the following model:
class Class
{
public int Id { get; set; }
public string Title { get; set; }
public virtual Professor Professor { get; set; }
}
class Professor
{
public int Id { get; set; }
public string Name { get; set; }
private ICollection<Class> classes = null;
public virtual ICollection<Class> Classes
{
get { return classes ?? (classes = new HashSet<Class>()); }
set { classes = value; }
}
}
class ClassManager : DbContext
{
public DbSet<Professor> Professors { get; set; }
public DbSet<Class> Classes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Class>().HasRequired(c => c.Professor);
}
}
In my main method I create and access my data like so:
using (ClassManager db = new ClassManager())
{
Professor p = new Professor() { Name = "McTest" };
Class c = new Class() { Title = "Testing Tests", Professor = p };
p.Classes.Add(c);
db.Professors.Add(p);
db.Classes.Add(c);
Console.WriteLine(db.SaveChanges() + " change(s) saved.");
}
using (ClassManager db = new ClassManager())
{
foreach (Professor p in db.Professors)
Console.WriteLine("Prof. " + p.Name + " gives " + p.Classes.Count
+ " classes.");
foreach (Class c in db.Classes)
Console.WriteLine(c.Title + " (Prof. " + c.Professor.Name + ")");
}
The output is:
3 change(s) saved.
Prof. McTest gives 0 classes.
Testing Tests (Prof. McTest)
The generated DB schema is:
table Classes
column Id (PK, int, not null)
column Title (nvchar(max), null)
column Professor_Id (FK, int, not null)
table Professors
column Id (PK, int, not null)
column Name (nvchar(max), null)
As you can see, the Classes
collection property does not appear anywhere in the schema. Is that intended?
Can someone please shed some light into this? All the examples I've found wouldn't go into detail about collection properties.
The two separated using statements in my main code are intentional. If I merge them together, the Classes property suddenly is not empty, but I don't understand why.
If the 2 are merged into 1 then you "query" the local entities that are stored in-memory, not the database.
in your first context you do not need to add both professor
and class
to the context. just add the professor and the class
will be saved too.
check the DB schema and make sure the entities are properly mapped to the database.
finally, change the Professor
class to
class Professor
{
public Professor()
{
Classes = new List<Class>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Class> Classes {get; set;}
}
I suspect the lazy loading is being trumped by the is null logic to return a hashed set.
Ok, after hours of searching I found the solution. It seems like one has to instruct the EF explicitly to load the collection properties of an entity when querying the database:
foreach (Professor p in db.Professors.Include("Classes"))
Console.WriteLine("Prof. " + p.Name + " gives " + p.Classes.Count
+ " classes.");
The Include
method does that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With