Lazyloading entities, references are loading but entity properties aren't - c#

I am still in the EF learning process and I am trying to get more familiar with EF lazy loading.
Please consider the following class and test:
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public virtual string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<CaseType> CaseTypes { get; set; }
public Tenant()
{
Users = new List<User>();
CaseTypes = new List<CaseType>();
}
}
And the test:
[Test]
public void TenantLazyLoading()
{
var tenant = _applicationContext.Tenants.Create();
tenant.Guid = "d176dc7c-6b96-4ab6-bddf-ce5a12024c39";
_applicationContext.Tenants.Attach(tenant);
Assert.AreEqual(1, tenant.Users.Count); // Pass, the navigation property users was loaded (lazy)
Assert.AreEqual("localhost", tenant.Name); // Fail, the tenant name is not loaded
}
The lazy loading apparently only works on the Navigation properties, but not on the Tenant properties. I made both properties (Users and Name) virtual, but that doesn't seem to matter.
How can I lazy load the local properties of Tenant?

That's the way how it works. If you create an entity manually and Attach it to the context you are telling EF that you don't want to load the entity's scalar properties.
There is no lazy loading of scalar properties, you always must do it explicitly, either by adding...
_applicationContext.Entry(tenant).Reload();
...after Attach or by replacing the first three lines by:
var tenant = _applicationContext.Tenants
.Find(new Guid("d176dc7c-6b96-4ab6-bddf-ce5a12024c39"));

Related

EF LINQ - How to query 2 tables associated by foreign key?

Using EF 6 code first. I have an Entity class and a Name class, and want to query a list of all Entity objects with their respective Names so that I can show on a Gridview control.
These are my class definitions:
public class Entity
{
[Key]
public int EntityKey { get; set; }
public virtual ICollection<Name> Names { get; set; }
}
public class Name
{
[Key]
public int NameKey { get; set; }
public int EntityKey { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Entity Entity { get; set; }
}
In my Data Access Layer, I have a method that gets all entities from database:
public List<Entity> GetEntities()
{
SdmDBContext entityDbContext = new SdmDBContext();
return entityDbContext.Entities.ToList();
}
but with this query, I don't get the Names that are associated to the Entity class. I have tried LINQ and lambda expressions, as well as an .Include operation, but not sure how to formulate the syntax properly.
How can I change the DAL method to return the Names associated with all existing entities?
You code is loading the list of Entities in the Entity table, and with 'lazy loading' it should also return associated Name collections when you access them. You declare your ICollection as virtual to enable 'lazy loading' because a proxy object will be created to make DB requests when you access something that hasn't been eagerly loaded.
Include is just a request to eagerly load related entities by using the navigation properties defined on the object. You can call Include using a lambda or string representing the name of the navigational property you want to eagerly load.
For example:
public List<Entity> GetEntities()
{
SdmDBContext entityDbContext = new SdmDBContext();
return entityDbContext.Entities.Include("Names").ToList();
}

Entity EF6 opposite of include

Say the EF6 code first is used. Say we have a simple two table relationship.
Where a patient has one doctor associated.
class Patient
{
[Key]
public int id { get; set; }
public string Name { get; set; }
[ForeignKey("Doctor")]
public int DoctorId { get; set; }
public virtual MedicalPersonel Doctor { get; set; }
}
class MedicalPersonel
{
[Key]
public int id { get; set; }
public string Name { get; set; }
}
There is a function Include(string path) to load the Doctor, when I am loading the patient. But what would be the opposite of include if I don't want to load the Doctor if the doctor contains some big fields like images?
Thanks!
That's the fun part about lazy loading, you won't load the Doctor unless you access the property. To be entirely sure you don't 'accidentally' lazy load anything you can
Remove the virtual keyword all together from the properties you never want lazily loaded.
When you create your context, disable lazy loading for that instance of the DbContext:
myContext.Configuration.LazyLoadingEnabled = false;
I think option number 2 would be preferred as that leaves you the choice when to enable/disable lazy loading.
As docs you can use following code in your DbContext.OnModelCreating:
modelBuilder.Entity<Patient>()
.Ignore(p => p.Doctor);
Remove the virtual Key word which will Lazy load the entity. Then when you fetch the Patient entity from the database it will not load the related Doctor entity unless you 'Include' it in the query. https://msdn.microsoft.com/en-gb/data/jj574232.aspx

Entity Framework Loading Related Entities

I'm using code-first Entity Framework with a basic context that just consists of standard IDbSet collections where T is just a POCO class. On my context, I've disabled lazy loading. While there are "navigation properties" in my model classes, I've removed the virtual keyword from them.
The "Get All" methods in the repository do some custom filtering to ensure that the current user only sees the data that they own unless they are an administrator. I'm having a particular problem where I'm logged in as an administrator that also is associated with some records. Since the entity that I'm logged in as is loaded in the context, even though I have lazy loading disabled, virtual removed and am not using Include or Load, the objects in the results that have an association to my profile have the navigation property set automatically.
This is not code from my project, just an example to show the idea of what I'm doing. It probably has typos and syntax errors.
public class Record
{
public Guid Id { get; set; }
public string Name { get; set; }
public Owner Owner { get; set; } //No virtual keyword
public Guid OwnerId { get; set; }
}
public class Owner
{
public Guid Id { get; set; }
public string Name { get; set; }
public Collection<Record> Records { get; set; } //No virtual keyword
}
public class Context : DbContext
{
IDbSet<Owner> Owners { get; set; }
IDbSet<Record> Records { get; set; }
public static Context Create()
{
Context context = new Context();
context.Configuration.LazyLoadingEnabled = false; //Lazy loading disabled
return context;
}
}
public class Repository
{
private Context Context { get; set; }
public Owner CurrentOwner { get; private set; }
public Repository()
{
Context = Context.Create();
//Code here to get the application user and look up an associated "owner"
//entity if the user is an "owner" (they could just be an administrator)
//but the GetCurrentOwnerOrNull uses the Context to find the user
CurrentOwner = GetCurrentOwnerOrNull();
}
public IQueryable<Record> GetRecords(bool asAdmin)
{
IQueryable<Record> records = Context.Records; //Not including or loading Owner
if (asAdmin)
{
//Verify that the application user is an admin and throw exception otherwise
}
else
{
if (CurrentOwner == null)
{
//Throw a security exception
}
records = records.Where(r => r.OwnerId == CurrentOwner.Id);
}
return records;
}
}
So again, the problem with the above is that if I was to run that code as an Owner, whether administrator or not, then those Records that I Own will have the Owner property set instead of null. I want the entity framework to get out of my business and not automatically set this. It's causing problems downstream, especially when running the code as an administrator and an owner, so you get some records back with Owner = null and some with Owner set. It's annoying. Make it stop for me please.
This bug is actually a feature. Entity Framework will automagically wire-up associations within the same context even if the entities are loaded independent of each other.
Lets assume the following:
public class Person
{
public Person()
{
this.Pets = new List<Pet>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Pet> Pets { get; set; }
}
public class Pet
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
public virtual Person Owner { get; set; }
}
Lets also assume that these are properly associated DB-First or with Attributes/Fluent API code-first.
Database:
Persons
Id Name
1 Erik Philips
Pets
Id Name PersonId
1 Joe 1
Here is what will happen:
var pet = DbContext.Pets.FirstOrDefault(id => id == 1);
var person = DbContext.Persons.FirstOrDefault(id => id == 1);
Assert.AreEqual(person.Pets.Count(), 1);
Assert.IsNotNull(pet.Person);
Assert.AreEqual(pet.Person, person);
The reason this can occurs is because the context will hold onto the objects in it's cache. If you don't care about the context holding onto these objects, you will need to use AsNoTracking().

How to avoid Lazy Loading when accessing foreign key ID property through navigation property?

I'm in the process of converting a project from NHibernate to Entity Framework 6.
Given this simple model:
public class User
{
public int ID { get; set; }
public string FullName { get; set; }
public virtual Organization Organization { get; set; }
// [...]
}
public class Organization
{
public int ID { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
// [...]
}
Accessing the primary key (ID) through the Organization navigation property will cause the whole Organization entity to be loaded into the context:
foreach(var user in db.Users)
Console.WriteLine(user.Organization.ID);
Given that the OrganizationID foreign key is part of the User row, I should be able to access it without causing a Lazy Load of the whole entity (and indeed, NHibernate does this properly).
Short of adding properties for the foreign key IDs into all of my 100+ entities so I can access their values without loading the entities, is there anything to be done to avoid this behaviour?
EDIT: Furthermore, even doing a null check will cause a load of the Organization entity (not in NHibernate):
foreach(var user in db.Users)
Console.WriteLine(user.Organization != null);
I guess this is due to the fundamental differences in the way the entity proxy is implemented in these two frameworks. So I'll have to adapt all of my code to this new frustrating behaviour... Unless someone already had to go through this and could enlighten me?
Nope, you'll need to add them as property in your class (that is; if you want it strong typed) like this to access it directly.
public class User
{
public int ID { get; set; }
public string FullName { get; set; }
//added ID
public int OrganizationID { get; set; }
public virtual Organization Organization { get; set; }
// [...]
}
By accessing the int you'll prevent the lazy loading, EF will bind the ID through naming conventions. Having said that: 100+ classes... :|
UPDATE:
As I just realized; you might want to try:
db.Users
.Include("Organization.ID")
.Where(/*your stuff*/) //etc.;
I am not certain if it will fully load the nested property. If it doesn't, it might be a small performance gain.

Lazy loading not working when adding detached entity

I have an ASP.NET MVC 4 application using Entity Framework 5 under .NET 4.5. The problem I'm having is that when I insert a detached entity that was created on the front-end, the lazy loading is not working.
Here is my code to add (or update):
public static int PersistMergeEntity(EntityTwo entityTwo)
{
int entityId;
using (var _db = new EntityDb())
{
if (_db.EntityTwo.Any(e => e.EntityTwoId == entityTwo.EntityTwoId))
{
_db.Entry(entityTwo).State = EntityState.Modified;
}
else
{
_db.EntityTwo.Add(entityTwo);
}
_db.SaveChanges();
//_db.Entry(entityTwo).Reference(e => e.EntityOne).Load();
entityId = entityTwo.EntityOne.EntityId;
}
EntityBo.UpdateData(entityId);
return entityTwo.EntityTwoId;
}
Here are my entities:
public class EntityTwo
{
[Key]
[ForeignKey("EntityOne")]
public int EntityTwoId { get; set; }
public Decimal NbValue { get; set; }
public virtual EntityOne EntityOne { get; set; }
}
public class EntityOne
{
[Key]
[ForeignKey("EntityTwo")]
public int EntityOneId { get; set; }
[ForeignKey("Entity")]
public int EntityId { get; set; }
public CsMonthDomain CsMonth { get; set; }
public int NbYear { get; set; }
public Decimal NbValue { get; set; }
public virtual Entity Entity { get; set; }
public virtual EntityTwo EntityTwo { get; set; }
}
And Entity is another entity that I need to do calculation every time I update or add EntityTwo.
The code works when the commented line is uncommented. But if it is the way shown up there, lazy loading will not work and I'll get a null Exception.
Lazy loading is set to true and the entities are, supposedly, correct, since it works when I explicitly load the navigation property.
I'm sorry about the names, but unfortunately I cannot post the real code ;(
Lazy loading does not work because the entityTwo you pass into the method is (most likely) not a dynamic proxy which it has to be in order to make lazy loading work. The instance is probably created outside the method using entityTwo = new EntityTwo();. To create a proxy of an entity you would need a context instance available and then use
entityTwo = _db.EntityTwos.Create();
In my opinion using explicit loading (your commented line) is the best solution in this situation. It has the same costs of querying the database once per navigation property like lazy loading would have plus the additional benefit over lazy loading that you could project a selection of properties you only need from the related entity, for example:
entityId = _db.Entry(entityTwo).Reference(eTwo => eTwo.EntityOne).Query()
.Select(eOne => eOne.EntityId)
.Single();

Categories