I have two models in EF Code First:
public class Book
{
public int Id { get; set; }
public virtual IEnumerable<Page> Pages { get; set; }
}
public class Page
{
public int Id { get; set; }
public int BookId { get; set; }
public Book Book { get; set; }
}
When loads a Book model from DB, Pages property is Null. But when replace IEnumerable => ICollection, lazy loading works and Pages fills from DB. How use IEnumerable and lazy loading together?
IEnumerable is immutable collection which you cannot modify (add or remove). EF does not support this type because internally EF need to modify collection in model.
Use ICollection instead, ICollection inherits from IEnumerable so it not only still get deferred execution (lazy loading) purpose but also has more methods to modify collection.
Related
Is this the correct way I can get linked tables using where or is there another way to return the data.
GetMethod
public IEnumerable<Model.MeetingPollingQuestion> GetMeetingPollingQuestion(int MeetingPollingId)
{
using (var db = new NccnEcommerceEntities())
{
using (DbContextTransaction dbTran = db.Database.BeginTransaction())
{
var ListOfMeetingPollingQuestions = (from mpq in db.MeetingPollingQuestions
where (mpq.MeetingPollingId == MeetingPollingId)
select new Model.MeetingPollingQuestion
{
MeetingPollingId = mpq.MeetingPollingId.Value,
MeetingPollingQuestionType = mpq.MeetingPollingQuestionType,
MeetingPollingParts = (from mp in db.MeetingPollingParts
where mp.MeetingPollingPartsId == mpq.MeetingPollingId
select new Model.MeetingPollingParts
{
Type= mp.Type
}).ToList(),
}).ToList();
return ListOfMeetingPollingQuestions;
}
}
}
EF Model
namespace Repository.EFModel
{
using System;
using System.Collections.Generic;
public partial class MeetingPolling
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MeetingPolling()
{
this.MeetingPollingQuestions = new HashSet<MeetingPollingQuestion>();
}
public int MeetingPollingId { get; set; }
public Nullable<int> MeetingId { get; set; }
public Nullable<System.DateTime> StartDate { get; set; }
public Nullable<System.DateTime> EndDate { get; set; }
public string PollingTitle { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MeetingPollingQuestion> MeetingPollingQuestions { get; set; }
}
}
namespace Repository.EFModel
{
using System;
using System.Collections.Generic;
public partial class MeetingPollingQuestion
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MeetingPollingQuestion()
{
this.MeetingPollingParts = new HashSet<MeetingPollingPart>();
}
public int MeetingPollingQuestionId { get; set; }
public string MeetingPollingQuestionType { get; set; }
public Nullable<int> MeetingPollingId { get; set; }
public Nullable<int> SequenceOrder { get; set; }
public virtual MeetingPolling MeetingPolling { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MeetingPollingPart> MeetingPollingParts { get; set; }
}
}
namespace Repository.EFModel
{
using System;
using System.Collections.Generic;
public partial class MeetingPollingPart
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MeetingPollingPart()
{
this.MeetingPollingPartsValues = new HashSet<MeetingPollingPartsValue>();
}
public int MeetingPollingPartsId { get; set; }
public string Type { get; set; }
public Nullable<int> MeetingPollingQuestionId { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MeetingPollingPartsValue> MeetingPollingPartsValues { get; set; }
public virtual MeetingPollingQuestion MeetingPollingQuestion { get; set; }
}
}
Your entities have navigation properties for the related entities, so if you want to return entities and their relatives, just eager load the related entities using Include.
public IEnumerable<Model.MeetingPollingQuestion> GetMeetingPollingQuestion(int MeetingPollingId)
{
using (var db = new NccnEcommerceEntities())
{
var questions = db.MeetingPollingQuestions
.AsNoTracking()
.Include(mpq => mpq.MeetingPollingType)
.Include(mpq => mpq.MeetingPollingParts)
.Where(mpq => mpq.MeetingPollingId == MeetingPollingId)
.ToList();
return questions;
}
}
... and that's it. The important details here is the use of Include to eager load related data, and the use of AsNoTracking to ensure that the loaded entities are not tracked by the DbContext. These will be detached entities, copies of the data but otherwise not tracking changes or an association with a DbContext. These are suitable for read-only access to the data they contain.
Whenever returning "entities" outside of the scope of a DbContext you should ensure that they are non-tracked or detached. This is to avoid errors that can come up with potential lazy load scenarios complaining about that an associated DbContext has been disposed, or errors about references being tracked by another DbContext if you try associating these entities to a new DbContext. Your code performing a Select with a new class instance does the same thing, just more code.
Personally I do not recommend working with detached entities such as ever returning entities outside of the scope of the DbContext that they were read from. Especially if you decide you only need a subset of data that the entity ultimately can provide. Entities reflect the data state, and should always be considered as Complete, or Complete-able. (i.e. Lazy loading enabled) If the code base has some code that works with tracked entities within the scope of a DbContext, vs. detached entities, vs. copies of entity classes that might be partially filled or deserialized, it makes for buggy, unreliable, and un-reusable code. Take a utility method that accepts a MeetingPollingQuestion as a parameter. As far as that method is concerned it should always get a complete MeetingPollingQuestion. The behaviour of this method could change depending on whether it was given a tracked vs. detached, vs. partially filled in copy of a MeetingPollingQuestion class. Methods like this would need to inspect the entity being passed in, and how reliable can that logic be for determining why a related entity/collection might be missing or #null?
If you need to pass entity data outside the scope of the DbContext where you cannot count on that data being complete or related data being lazy loaded as a last resort, then I recommend using POCO DTOs or ViewModels for those detached or partial representations of the data state. With a separate POCO (Populated by Select or Automapper) there is no confusion between that representation and a tracked Entity.
The problem is that i cannot understand what is the meaning of the Dbset used in it.
Is it a way to initialize a list or is it a part of Entity Framework??
Have you read Microsofts documentation?
"A DbSet represents the collection of all entities in the context, or that can be queried from the database, of a given type."
An easy way to think about it is that a DbSet represents a table in your database. It is almost always used together with something called a DbContext, which essentially is a representation of a database connection.
Example code that shows how several DbSets are used together with a DbContext:
public class User
{
public string Name { get; set; }
}
public class UserGroup
{
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class ExampleDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
}
Please refer to this tutorial on how to get started with Entity Framework.
I have an issue with the lazy loading behavior in EntityFramework 5. Here are my two models
public class Person {
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int? OfficeID { get; set; }
[ForeignKey("OfficeID ")]
public virtual Offices OfficeID_Offices { get; set; }
}
public class Offices
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
//Navigation Properties
public virtual ICollection<Person> Person_OfficeID { get; set; }
}
Then I have the following function in my Controller
[HttpPost]
public Person Read(int intID)
{
Person objData = (from obj in objDB.Persons
where obj.ID == intID && !obj.Deleted
select obj).FirstOrDefault();
}
This controller method is called through a jquery $.Ajax call, which returns a JSON object. Since my foreign key OfficeID_Offices is virtual, I'd expect to only be loaded when I demand it explicitely. However when I look at my returned JSON Object, I can see that the entire Offices object is returned as well.
Lazy loading seems to be enabled in my DbContext, so I'm wondering how I could avoid having the whole Office object returned.
Thank you!
Serialization of the entity object(s) accesses the property which triggers lazy loading. To disable lazy loading, set the objDB.Configuration.LazyLoadingEnabled property to False
I'm having some issues with the Entity Framework using POCOs and I hope someone can tell me at a high level if the behaviour I'm seeing is expected or I need to dig deeper into why it's happening.
I have a class Customer and another CustomerType, so Customer has a property Type (of type CustomerType indicating the type) and CustomerType has property Customers which is a collection of Customers (All Customers that have that type) So these are basically the Navigation properties on both ends of an association, resulting in POCO code something like:
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int TypeId { get; set; }
public CustomerType Type { get; set; }
}
public partial class CustomerType
{
public CustomerType()
{
this.Customers = new HashSet<CustomerType>();
}
public int Id { get; set; }
public string TypeName { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
}
I have turned off Proxy creation and LazyLoading (i.e. both DbContext.Configuration.ProxyCreationEnabled=false and DbContext.Configuration.LazyLoadingEnabled=false) because they make Serialization a pain.
As expected when I get instances from the Customer set, the Type property on them is null by default.
But if I get instances from the Customer set with a .Include("Type") not only is it loading the Type properties, but it's also loading the children - i.e. the collection of Customers on each of these.
Is this expected?
It is semi expected. The Include extension affects the SQL that is run. Those CustomerTypes that ARE loaded (by virtue of being included in the Customer query) will be built into the object tree according to the CustomerType.ParentId column.
So if by some fluke both a parent and a child is loaded in the same query, the child will be stuffed into the parent.
Is there a way to tamper with the DbContext in order to auto eager load a specific Navigation property when the entity is requested in a query? (no lazy loading).
Entity Framework 5
Example:
var supremeEmployee = context.Employees.FirstOrDefault(x => x.EmployeeId == 42);
and the returned model would come back pre-populated with the "Department" navigation property.
Depends on what your model looks like. If you're using interfaces or inheritance you could add a function to your DbContext class with a generic constraint on that type that always includes the navigation property.
In my experience though you're usually better off not doing that, performance wise. I prefer to load into anonymous types just the fields i need in the moment.
In the most basic way you could do this:
public class Department
{
public int Id { get; set; }
}
public class Employee
{
public int Id { get; set; }
public Department Department { get; set; }
}
public class MyContext : DbContext
{
protected DbSet<Employee> Employees { get; set; }
public IQueryable<Employee> LoadEmployees()
{
return Employees.Include(p => p.Department);
}
}