简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM