Why navigation property doesn't populate when its class is private? - c#

I have this simple model:
class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
class MyContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>().HasRequired(s => s.Parent).WithMany(s => s.Children).HasForeignKey(s => s.ParentId);
base.OnModelCreating(modelBuilder);
}
}
And when I use MyContext as below, I get null reference exception because child.Parent is null
var context = new MyContext();
var child = context.Children.First();
var parentId = child.Parent.Id; // Parent == null
To solve this problem I have to change the access modifier of Parent and Child classes to be public.
Why is that required ? Or is this just a bug ?

That is not a bug, you are using a feature of Entity Framework called Lazy Loading, and to use it you need to meet some requirements that you can find in this link. One of those requirements is that your entity classes must be public. In that link you will find a proper explanation of why you should meet those requirements, but in summary your issue is because EF can't create a proxy class from your entity, due to that you can't use lazy loading. You already are meeting the main requirement for lazy loading which is your navigation properties must be virtual, but first you must meet the requirements that EF needs to create a proxy class.
As an additional resource I suggest you look this msdn page where you can find all the ways to load related entities using EF.

First, to clear something up: your classes aren't private, they're internal. You can't declare a top-level type (a non-nested type) to be private (see MSDN Access Modifiers for more info).
Second, Lazy Loading is enabled by default for code first. As octaviocci mentioned, if you're expecting to use Lazy Loading you need to declare them public: it's just part of the requirements. By declaring your Navigation properties as virtual you're telling EF that you expect to use lazy loading. If you don't want to, you can (should) remove the virtual keyword. Then, when getting your entities, use the Include method to eagerly load related entities.
var context = new MyContext();
var child = context.Children.Include(c => c.Parent).First();
var parentId = child.Parent.Id;

Related

Entity Framework Core 6 : properties virtual vs non virtual

Let's say I have a POCO class with only 2 properties; one is virtual, the other is not.
public class MyPocoClass
{
public string SomeProperty { get; set }
public virtual string SomePropertyTwo { get; set }
}
Does the virtual property (SomePropertyTwo) have any side effect? Does it reduce performance etc?
as far as i know virtual property only used when you want to use lazy loading
In this context, "virtual" keyword is only redundant keyword and should be removed.

Navigation property null with Entity Framework

I'm using Entity Framework with Code first. My Relationship properties keep breaking.
I have the object Element:
public class Element : IElement
{
// ... some event handlers (removed)
[Key]
public Guid ID { get; set; } = Guid.NewGuid();
public string Name { get; set; }
// navigation properties
public virtual ElementType ElementType { get; private set; }
public virtual NotifiableCollection<Property> Properties { get; private set; } = new NotifiableCollection<Property>();
// Parameterless constructor for serialization
private Element() { }
public Element(ElementType elementType) : base()
{
// loop through and create Properties for each Property Type
ElementType = elementType;
if (ElementType?.PropertyTypes != null)
{
ElementType.PropertyTypes.ToList().ForEach((property) =>
{
Properties.Add(new Property(property));
});
}
}
}
And ElementType:
public class ElementType : IElementType
{
// ... some event handlers (removed)
[Key]
public Guid ID { get; set; } = Guid.NewGuid();
public string Name { get; set; }
// navigation properties
public virtual NotifiableCollection<PropertyType> PropertyTypes { get; set; } = new NotifiableCollection<PropertyType>();
public virtual NotifiableCollection<Element> Elements { get; set; } = new NotifiableCollection<Element>();
public ElementType()
{
// ensure our Element's get updates
PropertyTypes.CollectionChanged += (e, a) =>
{
//update the database to send out renewal to interested entities
if (a.ChangeType == ChangeType.Added)
{
foreach (Element element in Elements)
{
element.Properties.Add(new Property(a.Item));
}
}
};
}
}
It works fine when I create these objects the first time (as I've explicitly set the navigation properties then saved):
However, when I then close everything and get these from the database:
The navigation properties are not resolved. The table definitions set up the foregn key relationship fine:
CREATE TABLE [dbo].[Elements] (
[ID] UNIQUEIDENTIFIER NOT NULL,
[Name] NVARCHAR (MAX) NULL,
[ElementType_ID] UNIQUEIDENTIFIER NULL,
CONSTRAINT [PK_dbo.Elements] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_dbo.Elements_dbo.ElementTypes_ElementType_ID] FOREIGN KEY ([ElementType_ID]) REFERENCES [dbo].[ElementTypes] ([ID])
);
GO
CREATE NONCLUSTERED INDEX [IX_ElementType_ID]
ON [dbo].[Elements]([ElementType_ID] ASC);
and I can see the data is all correct:
ID Name ElementType_ID
ff186746-62cb-4246-9c64-f2d007b23ac0 Aircon Test 27/03/2017 12:54:03 57d93ac1-ad3b-4718-a593-80639cc24907
which matches an ID in ElementType table.
I have this set in my repository:
context.Configuration.ProxyCreationEnabled = true;
context.Configuration.LazyLoadingEnabled = true;
And the context is still active at the time where I'm trying to resolve this property.
Everything was working, but I've had this problem multiple times with EF, where my navigation properties just break randomly. I don't remember touching any of the code associated with this element, just ran it and now it doesn't work. Can anyone help?
Edit: This is the repository code:
public sealed class Repository : IRepository
{
public event ObjectMaterializedEventHandler ObjectMaterialized;
public Repository() {
(context as IObjectContextAdapter).ObjectContext.ObjectMaterialized += ObjectContext_ObjectMaterialized; ;
context.Configuration.ProxyCreationEnabled = true;
context.Configuration.LazyLoadingEnabled = true;
}
// I do this to wire in some events later
private void ObjectContext_ObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
{
ObjectMaterialized?.Invoke(this, e);
}
private DataContext context = new DataContext(false);
public IEnumerable<T> GetAll<T>() where T : class
{
return context.Set<T>().ToList() as IEnumerable<T>;
}
public T GetItem<T>(Guid id) where T : class
{
return context.Set<T>().Find(id) as T;
}
...
}
the context stores them like this:
public class DataContext : DbContext
{
...
public DbSet<Element> Elements { get; set; }
public DbSet<ElementType> ElementTypes { get; set; }
}
I think it IS something to do with accessing. I'm accessing the Element with context.Set().Find(id) as T, and it fails. However, if I navagate through the ElementTypes, find it's list of Entities, then it works fine.
Found the answer with the help of Ivan in the comments.
The issue is with having a private constructor:
// Parameterless constructor for serialization
private Element() { }
One of the requirement for proxies is a public or protected constructor:
For either of these proxies to be created: A custom data class must be
declared with public access.
A custom data class must not be sealed (NotInheritable in Visual
Basic)
A custom data class must not be abstract (MustInherit in Visual
Basic).
A custom data class must have a public or protected constructor that
does not have parameters. Use a protected constructor without
parameters if you want the CreateObject method to be used to create a
proxy for the POCO entity. Calling the CreateObject method does not
guarantee the creation of the proxy: the POCO class must follow the
other requirements that are described in this topic.
The class cannot implement the IEntityWithChangeTracker or
IEntityWithRelationships interfaces because the proxy classes
implement these interfaces.
The ProxyCreationEnabled option must be set to true.
For lazy loading proxies: Each navigation property must be declared as
public, virtual (Overridable in Visual Basic), and not sealed
(NotOverridable in Visual Basic) get accessor.

What is the purpose of virtual in Entity Framework?

I am a beginner to Entity Framework. I have got some terms which are creating problems. I am considering code-first schema
1-to-1 is resolved by by making a property of the child class in parent class and in child class we marks the id of parent class as foreign key.
Like
public class Parent{
//code
public Child Child{get; set;}
}
public class Child{
[ForeignKey("Parent")]
public int ParentId{get; set;}
}
A 1-to-many relation we use
public class Parent {
//code
public IList<Child> Child { get; set; }
}
public class Child {
[ForeignKey("Parent")]
public int ParentId{get; set;}
}
Is this the correct approach?
\*-\* is resolved by adding IList<class> in both classes.
But I was solving a problem where I have 2 classes Categories and Products.
In Product class a property is defined as
public class Products {
public virtual Category Category { get; set; }
}
And in the Category class, products are called in this way
public class Categories {
public virtual ICollection<Product> Products { get; set; }
}
I am confused what is the purpose of virtual Category in product?
Anyone answer please to resolve my confusion
As others have pointed out in the comments, EF uses the virtual keyword to enable lazy loading. The way it does this is by using what is known as a dynamic proxy.
If you are debugging you might notice that the type of your entity is not what you think it is:
Proxy types have names that look something like this:
System.Data.Entity.DynamicProxies
.Blog_5E43C6C196972BF0754973E48C9C941092D86818CD94005E9A759B70BF6E48E6
Entity Framework sees your Entity has the virtual keyword, and will create a dynamic proxy by inheriting from your class and overriding the properties that are marked virtual to enable lazy-loading for those properties.
As mentioned in the msdn I linked to, you will not get a dynamic proxy when you create an instance of your entity using the new keyword (and therefore will not get lazy loading):
var blog1 = new Blog(); // not a dynamic proxy
var blog2 = db.Blogs.Create(); // this is a dynamic proxy
var blog3 = db.Blogs.Find(1); // this is a dynamic proxy

How would you setup a one to many relationship of different types in Entity Framework code first?

This is my design. I'm thinking of where a parent object has a collection of child objects, where each child object can be a different type of object stored in an independent table in the database.
In my design, each day is aggregated into a Report object assigned to a User. This Report object can have multiple Activities. However, these Activities can be different types of objects. Like one activity may be food and another activity may be exercise. I'm curious if there's another way around this relationship than my current EF design.
My Report object cannot navigate to the children. But each child, like DbSet or DbSet has a link to the parent Report object. It's important to have the Report object because it links to goal information and helps to establish a base for aggregating data against goals.
Is there a way to setup a parent object so that it can have a child collection with multiple child objects, or setup the mappings so that if the parent Report is deleted then this will cascade to the child activities? Or would you go about this in a different way? Thanks
Inheritance is what you can use to accommodate different types into a single collection. Create a base Activity type, put the common properties there (along with a navigation property to it's parent class) and then create one subtype for each different activity type and add it's specific properties.
I could not understand if the parent object and the report object are the same thing, so I will assume the aren't and define these two classes.
public class Parent
{
public int Id { get; set; }
public ICollection<Activity> Activities { get; set; }
}
public class Report
{
public int Id { get; set; }
}
public abstract class Activity
{
public int Id { get; set; }
public virtual Parent Parent { get; set; }
public virtual Report Report { get; set; }
// common activity properties
}
public class Food : Activity
{
// Food specific properties
}
public class Excercise : Activity
{
// Exercise specific properties
}
Then you can create a DbSet for the Parent, the Report and the Activity. There's no need to create dbsets for it's subtypes, but you can, if you wish so.
public DbSet<Parent> Parents { get; set; }
public DbSet<Report> Reports { get; set; }
public DbSet<Activity> Activities { get; set; }
You can query specific activity subtypes this way:
var indoorExercises = context.Activities.OfType<Exercise>().Where(e => e.IsIndoor);
You can get more information following this link: http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx

Is there a way to map to immutable entities in entity framework?

I would like to save some work by avoiding having 2 sets of entities in my code. As of now I have the first set which is just a bunch dummy surrogate entities for EF with default constructors and settable properties, so that it can map to them. The other one is a set of real entities that I use in my business code. The real ones are immutable and fully initialized at the time of being created by using initializing constructors.
Is there a way to avoid having surrogates and map straight to the real entities by using some sort of factories in EF that are able to deal with initializing constructors without using settable properies?
It isn't possible, EF require parameterless constructor and it must be able to set properties.
For better encapsulation you can make property setters protected. EF will still be able to set property values (via generated proxy) but from the outer point of view it will look immutable.
Can't add comment, so. Now it's possible, because EF now can map private properties. And in 6.1.3 doing it by default(not sure about previous releases).
Example below.
class Program
{
static void Main(string[] args)
{
using (var context = new MyContext())
{
context.MyImmutableClassObjects.Add(new MyImmutableClass(10));
context.MyImmutableClassObjects.Add(new MyImmutableClass(20));
context.SaveChanges();
var myImmutableClassObjects = context.MyImmutableClassObjects.ToList();
foreach (var item in myImmutableClassObjects)
{
Console.WriteLine(item.MyImmutableProperty);
}
}
Console.ReadKey();
}
}
public class MyContext : DbContext
{
public DbSet<MyImmutableClass> MyImmutableClassObjects { get; set; }
}
public class MyImmutableClass
{
[Key]
public int Key { get; private set; }
public int MyImmutableProperty { get; private set; }
private MyImmutableClass()
{
}
public MyImmutableClass(int myImmutableProperty)
{
MyImmutableProperty = myImmutableProperty;
}
}

Categories