EF Core: No backing field could be found for property of entity type and the property does not have a getter

Viewed 3184

I`m gettng the following error in EF Core: No backing field could be found for property 'ParentId' of entity type 'Child' and the property does not have a getter.

This is my configuration for the Child entity:

            // create shadow property for Parent
            builder.Property<int>("ParentId").IsRequired();

            // make shadow property and foreign key the PK as well
            // i know this may not make the most sense here 
            // in this scenario but it's what I want to do.
            builder.HasKey("ParentId");

            // configure FK
            builder.HasOne(o => o.Parent)
                .WithOne()
                .HasForeignKey<Child>("ParentId");

And my entities:

public class Parent
{
    public int Id { get; set; }
}

public class Child 
{
    public Parent Parent { get; private set; }

    public void SetParent(Parent p) => Parent = p;
}

The error occurs when I call dbContext.Children.Contains(myChild):

var child = new Child();
child.Parent = new Parent();

dbContext.Children.Add(child);
dbContext.SaveChanges();

// works fine
Assert.True(dbContext.Children.Any());

// throws InvalidOperationException
Assert.True(dbContext.Children.Contains(myChild)):

If I add the shadow property as a real property to the model as such:

public class Child 
{
    public int ParentId { get; set;} 

    public Parent Parent { get; private set; }

    public void SetParent(Parent p) => Parent = p;
}

then everything works. But I'd like to keep it a shadow property if possible.

2 Answers

Update:

Just checked the EF Core 3.1.7 released 3 days ago, and the exception is gone, which means it has been reported/identified and fixed. Hence you can simply upgrade to that version.

Original:

The configuration of the shadow property is fine. Unfortunately you are hitting one of the EF Core 3.x bugs.

In order to translate Contains method to SQL, EF Core has to convert it to PK equality comparison, something like (in pseudo code)

dbContext.Children.Any(c => PK(c) == PK(myChild))

which can be seen from the exception stack trace, and apparently is failing to do so when the PK is shadow property.

I've checked the latest at this time EF 5 preview and it appears to be fixed (the issue is gone). So until it gets released, the workaround is to replace implicit entity equalty comparisons (e.g. Contains(entity), Equals(entity), == entity etc.) with the corresponding explicit PK comparisons.

In this case, instead of

dbContext.Children.Contains(child)

you can use either

dbContext.Children.Any(c => c.Parent.Id == child.Parent.Id)

(worse SQL translation due to another v3.x defect)

or

dbContext.Children.Any(c => EF.Property<int>(c, "ParentId") == child.Parent.Id)

(proper SQL translation as in v5.0, but uses "magic" strings and requires explicitly specifying the shadow property name and type)

public class Child 
{
    public int ParentId{get;set;}
    public Parent Parent { get; private set; }

    public void SetParent(Parent p) => Parent = p;
}

you should define your foreign key variable and remember that private set doesn't create relationship in db

Related