Lazy loading not working when adding detached entity - c#

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();

Related

Why are collection navigation properties not initialized for lazy loading proxy classes

In one of my projects I use Entity Framework with virtual navigation properties on the entities. This means the entities are loaded from the database or created with IDbSet<T>.Create() a DynamicProxy is returned. Because I only make navigation properties virtual, this proxy does lazy loading and no change tracking (all properties need to be virtual to get a change tracking proxy).
My assumption was that the DynamicProxy takes care of initializing virtual ICollection<T> properties, as it does when the entity is loaded from the database. But when I create a new entity using IDbSet<T>.Create(), these navigation properties remain null.
Then I tried to make all properties virtual so I get a DynamicProxy with change tracking and to my surprise these navigation properties are initialized.
See the following example:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
static class Program
{
static void Main()
{
using (var db = new BloggingContext())
{
var changeTrackingBlog = db.ChangeTrackingBlogs
.Create(); // returns a DynamicProxy
var changeTrackingBlogPostCount = changeTrackingBlog
.Posts
.Count; // Posts has type EntityCollection<Post>
var lazyLoadingBlog = db.LazyLoadingBlogs
.Create(); // returns a DynamicProxy
var lazyLoadingBlogPostCount = lazyLoadingBlog.Posts
.Count; // Posts == null
}
}
}
public class BloggingContext : DbContext
{
public IDbSet<Post> Posts { get; set; }
public IDbSet<ChangeTrackingBlog> ChangeTrackingBlogs { get; set; }
public IDbSet<LazyLoadingBlog> LazyLoadingBlogs { get; set; }
}
public class Post
{
[Key]
public int PostId { get; set; }
public virtual ChangeTrackingBlog ChangeTrackingBlog { get; set; }
public virtual LazyLoadingBlog LazyLoadingBlog { get; set; }
}
public class ChangeTrackingBlog
{
[Key]
public virtual int BlogId { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class LazyLoadingBlog
{
// Not all properties are virtual, so no Change tracking, just lazy loading
[Key]
public int BlogId { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
I hope someone can explain what's happening here.
The usual way to avoid the null reference is to initialize the collection in the constructor:
public LazyLoading()
{
Posts = new List();
}
I believe it may be better practise to use a backing field - something to do with anonymous constructors not being called in certain circumstances (serialization or something - apologies for vagueness). So I do this:
public class LazyLoadingBlog
{
private ICollection<Post> _Posts = new List<Post>();
public virtual ICollection<Post> Posts
{
get { return _Posts ; }
//protected set lets EF override for lazy loading
protected set { _Posts = value;
}
}
Unfortunately I can't explain why you don't get an error when you mark all the properties virtual...

Entity Framework v5 loading relational data

I have two very simple POCO that i want to connect through a one to many relation
public class Menu
{
public int MenuId { get; set; }
public bool IsActive { get; set; }
public ICollection<MenuMember> MenuMembers { get; set; }
}
public class MenuMember
{
public int MenuMemberId { get; set; }
public int MenuId { get; set; }
public string ViewRoute { get; set; }
public bool IsActive{ get; set; }
}
public class EFDbContext : DbContext
{
public DbSet<Page> Pages { get; set; }
public DbSet<Menu > Menus { get; set; }
public DbSet<MenuMember> MenuMembers{ get; set; }
}
Now what I have to do is very simple , but I all the resources on the internet are suprisingly so vague (or i am too dumb)
I want to write an lambda expression for
SELECT *
FROM Menu INNER JOIN MenuMembers
ON Menu.MenuId = MenuMembers.MenuId
WHERE Menu.MenuId = 1
I have used this
IEnumerable<Menu> menu
= repository.Menus.Where(x => x.MenuId == menuId);
but when I iterate over it, menu.MenuNumbers stays null. I believe it is some sort of lazyloading issue.
Either Include() the relation manually for eager loading:
Entity Framework Loading Related Entities:
Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query. Eager loading is achieved by use of the Include method.
IEnumerable<Menu> menu = repository.Menus
.Include(m => m.MenuMembers)
.Where(x => x.MenuId == menuId);
Or mark the property as virtual so Entity Framework will lazy-load it:
Lazy loading is the process whereby an entity or collection of entities is automatically loaded from the database the first time that a property referring to the entity/entities is accessed. When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook.
public class Menu
{
public int MenuId { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<MenuMember> MenuMembers { get; set; }
}
And there's a few other options, be sure to check out the documentation.
You need to declare MenuMembers as virtual
public virtual ICollection<MenuMember> MenuMembers { get; set; }

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

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"));

Populate relational objects in Entity

I have a class that looks like this :
public partial class Defect
{
public int DefectID { get; set; }
public string Title { get; set; }
public virtual DefectStatus DefectStatus { get; set; }
public virtual Developer Developer { get; set; }
public virtual Project Project { get; set; }
}
When I call this :
var defect = db.Defects.Find(id);
How can I make it fill Project with the relational data from the Project table?
Currently I am doing the following defect.Project to populate defect.Project
var defect = db.Defects.Find(id);
defect.Project = db.Projects.Find(defect.ProjectID);
But I want to know if there is a better way to accomplish the same thing?
If you are still inside the db context when you ask for the Project, then (because you have marked the property Virtual), Project will be lazy loaded. If you are outside the db context, then you need to load it (as you are doing) while in the context.
However, you can eager load the Project entity by not using the Virtual keyword or by using Include in your query:
db.Defects.Include("Project").Where(d => d.DefectId.Equals(id));
This looks like the best reference for loading entities.

Lazy loading doesn't work for me inEntity Framework 4

I'm trying to enable Lazy Loading on an EF4 context.
The code that is trying to load the data is:
using (IUnitOfWork uw = new EFUnitOfWork())
{
foreach (Document doc in uw.Documents.All)
{
Console.WriteLine("Name: {0} Description: {1} Category: {2}", doc.Name, doc.Description, doc.DocumentCategory.Name);
}
}
I am experimenting with the Repository and Unit Of Work patterns but as I understand it, the command below should work.
ctx.ContextOptions.LazyLoadingEnabled = true;
The problem I have is when accessing doc.DocumentCategory.Name, I get a NullReferenceException.
Why isn't this data being loaded lazily?
If I have DocumentCategories loaded, the DocumentCategory property is resolved.
My Document class is defined as follows:
public class Document
{
public Document()
{
}
public Document(int id)
{
Id = id;
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual int DocumentCategoryId { get; set; }
public virtual bool Deleted { get; set; }
public DocumentCategory DocumentCategory { get; set; }
public override string ToString()
{
return Name;
}
}
The DocumentCategory property also needs to be marked as virtual in order for lazy loading to be supported. Have a look at http://msdn.microsoft.com/en-us/library/dd468057.aspx
Is Document.DocumentCategory declared as virtual? EF requires this, to generate a proxy type, that will actually perform the lazy loading when you access the property. (Otherwise EF does not know, when you access the property's value)
Also, if DocumentCategory is already virtual, there might be other properties that prevend EF from generating a proxy-type. Inspect a "Document" instance with the debugger to see, whether it is actually a proxy-type.

Categories