Reputation: 6435
I am facing an issue with the Fluent API mapping in EF6. It is all set up, but for some reason, anytime I select an object, it is missing the child objects.
Lets start with the DbContext
:
public partial class PMSContext : DbContext
{
public PMSContext() : base(nameOrConnectionString: "PmsDb")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Employee> employees { get; set; }
public DbSet<Project> projects { get; set; }
public DbSet<ProjectStep> projectSteps { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Employee>().HasMany(e => e.Projects).WithMany(t => t.EmployeesWorkingOnProject).Map(m =>
{
m.MapLeftKey("EmployeeId");
m.MapRightKey("ProjectId");
m.ToTable("employee_project");
});
modelBuilder.Entity<ProjectStep>().HasRequired(p => p.Project).WithMany(s => s.ProjectSteps).Map(m => m.MapKey("Project")).WillCascadeOnDelete(true);
//modelBuilder.Entity<Project>().HasMany(p => p.ProjectSteps).WithRequired(ps => ps.Project).WillCascadeOnDelete(true);
modelBuilder.Entity<Employee>().HasOptional<Project>(e => e.LeaderOfProject).WithOptionalPrincipal(p => p.ProjectLeader).Map(m => m.MapKey("ProjectLeader"));
}
public Project FindProjectById(int id)
{
return this.projects.Find(id);
}
}
This is pretty much everything needed which plays into my issue.
I have set up a total of 3 model classes:
[DataContract(Namespace = "Shared")]
public class Employee
{
public Employee()
{
this.Projects = new List<Project>();
}
[DataMember]
public int ID { get; set; }
[DataMember]
public String Name { get; set; }
[DataMember]
public String JobDescription { get; set; }
[DataMember]
public String Department { get; set; }
[DataMember]
public String DirectDialing { get; set; }
[DataMember]
public bool Status { get; set; }
public virtual Project LeaderOfProject { get; set; }
[DataMember]
public virtual List<Project> Projects { get; set; }
}
[DataContract(Namespace = "Shared")]
public class Project
{
public Project()
{
this.EmployeesWorkingOnProject = new List<Employee>();
this.ProjectSteps = new List<ProjectStep>();
}
[DataMember]
public int ID { get; set; }
[DataMember]
public String Titel { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public DateTime EndDate { get; set; }
[DataMember]
public String Description { get; set; }
[DataMember]
public Employee ProjectLeader { get; set; }
[DataMember]
public bool Status { get; set; }
[DataMember]
public virtual List<Employee> EmployeesWorkingOnProject { get; set; }
[DataMember]
public virtual List<ProjectStep> ProjectSteps { get; set; }
}
[DataContract(Namespace = "Shared")]
[Table("project_step")]
public class ProjectStep
{
[DataMember]
public int ID { get; set; }
[DataMember]
public String Description { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public DateTime EndDate { get; set; }
[DataMember]
public Project Project { get; set; }
}
And the corresponding database setup:
Now to my problem. Whenever I execute the FindProjectById
method it does return the proper object, but it is missing any reference to the childs. this means that
ProjectSteps
EmployeesWorkingOnProject
ProjectLeader
are not set. This also causes issues on my delete methods. I assume that this is an error in my OnModelCreating
method, but I am not 100% sure.
Can anyone tell me what I am missing to fetch the child objects as well?
Upvotes: 0
Views: 167
Reputation: 34708
You have disabled lazy loading and proxies, but if you do not .Include() child entities then EF doesn't know to load them. To use .Include() you will need to use .SingleOrDefault() rather than Find. Otherwise you will need to go to the context to Load child collections/references.
Your FindProjectById() would look something like:
var project = this.Projects.Include(x=>x.ProjectLeader)
.Include(x=>x.EmployeesWorkingOnProject)
.Include(x=>x.ProjectSteps)
.SingleOrDefault(x=>x.ID == id);
return project;
A caveat around using SingleOrDefault vs. Find is that where Find will search local store then go to DB, Single/First/ etc. will go to DB. This means that a query is executed each time where Find may find an entity in the local memory store. if you're inserting records into the DB Context (and prior to save changes) searching for that entity will not return an entity that is in the local store, but hasn't been committed to the DB yet. (prior to SaveChanges())
Typically you do not want to return entities outside of the scope of the DbContext as lazy load proxies won't work so anything you don't pre-load will be #null. I generally rely on deferred execution, returning IQueryable then .Select() the various bits I care about into DTO/ViewModel POCO classes.
Upvotes: 1
Reputation: 83
You can try change the lazy loading into true if you want to automatically load the child objects.
this.Configuration.LazyLoadingEnabled = true;
if the lazy loading is false, then you need to load the reference before accessing it.
Upvotes: 0