Navigation property not getting filled on lazy loading - asp.net-mvc

I am trying my hands-on on mvc examples given on Mvc Offical Site.
Here i have 3 Models Student,Course and Enrollment where there is an one to many relationship on Course and Enrollment entities and many to one relationship on Enrollment and student.
The models for Student,Course and Enrollment are as follows with the navigation properties marked as "virtual" since i need to perform lazy binding
public class Student
{
public int StudentID { get;set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual IEnumerable<Enrollment> Enrollments { get; set; }
}
In the same way i have my course model
My Enrollment Model
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
// public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
I am using Code-First Technique With EF 5.My DB Context Class is as follows
public class SchoolContext:DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
By Scaffolding I have generated all my views .On Click of my details Action link I have a controller function that is called
public ActionResult Details(int id = 0)
{
db.Configuration.LazyLoadingEnabled = true;
db.Configuration.ProxyCreationEnabled = true;
Student student = db.Students.Find(id);
// db.Entry(student).Reference(p => p.Enrollments).Load();
IEnumerable<Enrollment> s= student.Enrollments;
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
Here the problem is when the Find() method is called in the controller the navigation property in Student Class model is null.But there is data in DB corresponding to the id that is being passed.In short,the navigation property is not returning data(null).

You have to materialize the entities to get a rid of the current lazy loading in the query:
Student student = db.Students.Find(id).ToList();
This will fix your problem without ToList() after db.Students.Find(id); = will returns the generated object from the dynamic proxy.
Second problem you have a casting bug:
IEnumerable<Enrollment> enrollments = student.Enrollments; // is wrong
should be:
ICollection<State> enrollments = student.Enrollments;
or
var enrollments = student.Enrollments;

Related

Insert data in another table through entity framework MVC

I want to save student's data in student table and their address in student details table i can't insert data in second table.
Error is Object reference not set to an instance of an object.
Student Model
public partial class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
}
StudentDetails
public partial class StudentDetail
{
public int Id { get; set; }
public string Address { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
}
View Model
public class StudentViewModel
{
public Student Students { get; set; }
public StudentDetail StudentDetails { get; set; }
}
DbSet
public partial class SampleEntities : DbContext
{
public SampleEntities()
: base("name=SampleEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public System.Data.Entity.DbSet<Basics.Models.Student> Students { get; set; }
public System.Data.Entity.DbSet<Basics.Models.StudentDetail> StudentDetails { get; set; }
}
Controller
private SampleEntities db = new SampleEntities();
Action
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(StudentViewModel model)
{
if (ModelState.IsValid)
{
model.Students.CreatedDate = DateTime.Now;
db.Students.Add(model.Students);
db.SaveChanges();
//The Newly created Id gets created and when saving that Id in anothr table the exception comes
model.StudentDetails.Id = model.Students.Id;
//For Time Being in real time it won't be hard coded
model.StudentDetails.Address = "New Jersey";
db.StudentDetails.Add(model.StudentDetails);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model.Students);
}
Why not have
public StudentDetail StudentDetails { get; set; }
in Student class and change Id for
public int StudentId { get; set; }
and then change Id in StudentDetail for also
public int StudentId { get; set; }
Like that Entity Framework will be able to join them in a 1 to 1 relationship. You'll have to add some primary keys/foreign keys depending on your needs.

how to query against a many to many relation with entity framework 6

I have those 2 Models
public class BranchEmployees
{
public int ID { get; set; }
[Required, Column(Order = 0), Key]
public string ApplicationUserID { get; set; }
[Required, Column(Order = 1), Key]
public int BranchID { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<Branch> Branch { get; set; }
}
public class Branch
{
public int ID { get; set; }
public string BranchName { get; set; }
[Required]
public string ApplicationUserID { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<BranchEmployees> BranchEmployees { get; set; }
}
public class ApplicationUser
{
//rest of the code
}
UPDATE
I have everything set up but what I want is the query that gets me the Employees whose IDs are in the branch employees table
, I'm using entity framework code first with MVC 5 , how do I do it ?
Assuming that your ApplicationUser class will have a navigational property called BranchEmployees, here is the query that gets me the Employees whose IDs are in the branch employees table
List<ApplicationUsers> employeeNames =
dbContext
.ApplicationUsers
.Where(au => au.BranchEmployees
.Count() > 0).ToList();
Also, can you provide whole model including ApplicationUser? I also wonder why you do not prefer BranchEmployees to inherit from ApplicationUser.
You don't need a class that indicates a many-to-many relation between two tables when you do code-first. The key here is to create virtual properties of those classes. Lets say you have a class Student and class Course. Students can be in many Courses and Courses can have many Students. To generate a database using these models the classes should look like this:
public class Student
{
private ICollection<Course> _courses;
public Student()
{
this._courses = new HashSet<Course>();
}
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Course> Courses
{
get { return this._courses; }
set { this._courses = value; }
}
}
And for Course:
public class Course
{
private ICollection<Student> _students;
public Course()
{
this._students = new HashSet<Student>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students
{
get { return this._students; }
set { this._students = value; }
}
}
I hope that this can help you solve your issue.

Creating related attributes when creating new instance of class MVC 4 - Entity Framework

I am just getting into MVC 4 and Entity Framework 5 and want to know if what I am doing is correct?
I have a UserObject and a JobObject, the jobObject has a reference to a User Object.
public class Job
{
public int id { get; set; }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
When I want to create an instance of the Job I am passing in the query string a parameter UserID, but the Job only deals with an instance of MyUser.
Is the following the correct way to associate the user to the job?
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
MyUser staffmember = db.MyUsers.Find(userid);
if (staffmember == null)
{
return View("StaffMemberNotFound");
}
job.User = staffmember;
db.Jobs.Add(job);
db.SaveChanges();
}
}
Or is there a better way to associate the user to the job?
Your way will work but I prefer to simply work with ids if possible.
What I would suggest is that you add a MyUserId property to your Job class (remember to update the database if you are using codefirst):
public class Job
{
public int id { get; set; }
[ForeignKey("User")]
public int MyUserId { get; set: }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
Then simply populate the MyUserId. You can also change your check to simply check if the id exists as apposed to finding an object and letting EF map that to a class before returning it to you
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
if (!db.MyUsers.Any(u => u.Id == userid)
{
return View("StaffMemberNotFound");
}
job.MyUserId = userid;
db.Jobs.Add(job);
db.SaveChanges();
}
}
EF will do the rest of the mapping for you when you next retrieve the record from the database.
Your approach works fine, the only small optmization you could make is not taking the "retrieval hit" of MyUser staffmember = db.MyUsers.Find(userid); since you already have the userid.
I am using ASP.NET MVC 4 and Entity Framework 5.0, and here is my code (different model objects, but same intent as what you are doing).
Note: I let EF generate my model classes by right-clicking on the Models folder and choosing Add->ADO.NET Entity Data Model in VS.NET 2012.
Store.Models.Product
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Product
{
public long Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public System.DateTime DateAdded { get; set; }
public Nullable<long> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
Store.Models.Category
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public long Id { get; set; }
public string CategoryName { get; set; }
public System.DateTime DateAdded { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
On my Create.cshtml page, I have the User select the CategoryId from the drop-down list. This Category Id is bound to Product.CategoryId. All I do in my method is this:
ProductController
public class ProductController : Controller
{
...
[HttpPost]
public ActionResult Create(Product product)
{
product.DateAdded = DateTime.Now;
if (dbContext != null)
{
dbContext.Products.Add(product);
dbContext.SaveChanges();
}
return RedirectToAction("Index");
}
...
}

LINQ to entities against EF in many to many relationship

I'm using ASP.NET MVC4 EF CodeFirst.
Need help to write LINQ (to entities) code in Index action to get collection of Courses which are attended by selected student. The relationship is many to many with join table with payload.
//StudentController
//-----------------------
public ActionResult Index(int? id)
{
var viewModel = new StudentIndexViewModel();
viewModel.Students = db.Students;
if (id != null)
{
ViewBag.StudentId = id.Value;
// *************PROBLEM IN LINE DOWN. HOW TO MAKE COURSES COLLECTION?
viewModel.Courses = db.Courses
.Include(i => i.StudentsToCourses.Where(t => t.ObjStudent.FkStudentId == id.Value));
}
return View(viewModel);
}
The error I got is:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I have modeles (the third one is for join table with payload):
//MODEL CLASSES
//-------------
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class StudentToCourse
{
public int StudentToCourseId { get; set; }
public int FkStudentId { get; set; }
public int FkCourseId { get; set; }
public string Classroom { get; set; }
public virtual Student ObjStudent { get; set; }
public virtual Course ObjCourse { get; set; }
}
Then, here is modelview I need to pass to view
//VIEWMODEL CLASS
//---------------
public class StudentIndexViewModel
{
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<StudentToCourse> StudentsToCourses { get; set; }
}
EF does not support conditional include's. You'll need to include all or nothing (ie no Whereinside the Include)
If you need to get the data for just certain relations, you can select it into an anonymous type, something like (the obviously untested);
var intermediary = (from course in db.Courses
from stc in course.StudentsToCourses
where stc.ObjStudent.FkStudentId == id.Value
select new {item, stc}).AsEnumerable();
Obviously, this will require some code changes, since it's no longer a straight forward Course with a StudentsToCourses collection.

Lazy loading does not work in entity framework 5

I have defined a (poco?) class in my domain project:
public class Club
{
public Club()
{
ContactPersons = new HashSet<ContactPerson>();
}
public int Id { get; set; }
[Required]
[StringLength(64)]
public string Name { get; set; }
public virtual ICollection<ContactPerson> ContactPersons { get; set; }
}
public class ContactPerson
{
public virtual int Id { get; set; }
[StringLength(64)]
public virtual string FirstName { get; set; }
[StringLength(64)]
public virtual string LastName { get; set; }
}
In my MVC project I have my clubcontroller:
public ActionResult Create(CreateClubViewModel model)
{
Club club = new Club();
model.Initialize(club);
IClubDb clubDb = DependencyResolverHelper.IClubDbService;
clubDb.Create(club); // create club in db
}
public ActionResult Display(string domain)
{
try
{
IClubDb clubDb = DependencyResolverHelper.IClubDbService;
Club club = clubDb.Get(domain);
return View(club);
}
catch (Exception) // user is not logged iin
{
return View();
}
}
Finally, in my DB project I create and retrieve the club,
public Club Get(string name)
{
return DataContext.Clubs
//.Include(x => x.ContactPersons)
.Single(r => r.Name == name);
}
public int Create(Club club)
{
DataContext.Clubs.Add(club);
return DataContext.SaveChanges();
}
I have tried everything to get EF to lazy load the ContactPersons of my club object when I call the Get club in the Display method but ContactPersons has always a length of zero. However, if I eager load contact persons using the .include (I have commented this part out), then obviously ContactPersons contains a number of contacts.
I am not sure what I am doing wrong:
I have followed the guidelines for defining poco classes: http://msdn.microsoft.com/en-us/library/dd468057.aspx
I have a public parameter less constructor (but not protected constructor)
I have lazyloading enabled
I think I am missing a concept, the poco club class is also my domain entity which I insert into DB. What am I doing wrong? Whay I can't get lazy loading to work?
try
ContactPersons.ToList();
this will force all entities to be loaded.
see Entity framework, when call ToList() it will load FK objects automatically?
It seems that your LazyLoading performs when your dbContext is closed. So it will not load.
You use ContactPerson in view, am i right?
Did you forget to include the foreign key in your entity?
public class ContactPerson
{
public virtual int Id { get; set; }
[StringLength(64)]
public virtual string FirstName { get; set; }
[StringLength(64)]
public virtual string LastName { get; set; }
public int ClubId { get; set; }
[ForeignKey("ClubId")]
public virtual Club Club { get; set; } // if you need
}

Resources