In Entity Framework Core, is it possible to use a navigation property combined with backing field?

c# entity-framework-core

Question

Take the following entities:

public sealed class Employee
{
    private Supervisor supervisor;

    public long Id { get; set; }

    public long? SupervisorId { get; set; }

    public Supervisor Supervisor
    {
        get => this.Department?.Supervisor ?? this.supervisor;
        set => this.supervisor = value;
    }

    public long? DepartmentId { get; set; }
    public Department Department { get; set; }
}

public sealed class Department
{
    public long Id { get; set; }

    public long? SupervisorId { get; set; }
    public Supervisor Supervisor { get; set;}
}

And the following DbContext:

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet<Employee> Employee { get; set; }
    public DbSet<Supervisor> Supervisor { get; set; }
    public DbSet<Department> Department { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Employee>()
            .Property(e => e.Supervisor)
            .HasField("supervisor")
            .UsePropertyAccessMode(PropertyAccessMode.Field);
    }
}

When running the update database command:

dotnet tool run dotnet-ef database update

The following error is generated:

The property 'Employee.Supervisor' is of type 'Supervisor' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

How do I combine both the backing field concept and the navigation property concept? My goal is to allow the application to access employee.Supervisor and determine their supervisor if they have a direct report, or if they belong to a department use the department's supervisor instead.

I know I can add a new property to Employee named ComputedSupervisor with this logic, but I am trying to avoid having to refactor a bunch of code.

Thanks for any tips in advance!

1
1
3/10/2020 2:44:44 PM

Accepted Answer

The documentation does not reflect the actual rules, because <camel-cased property name> (the "standard" C# backing field naming convention) is definitely supported, and probably even with highest priority.

But let say your naming convention is not supported. You can still map the backing field, but you can't do that with Property fluent API, because by EF Core terminology navigation properties are not "properties", but "navigations". This applies to all fluent, change tracking etc. APIs.

In order to configure navigation, you need to get access to the relationship builder. Then you can use the PrincipalToDependent and DependentToPrrncipal properties of the associated metadata to access/configure the two ends of the relationship.

Or use directly the metadata APIs (currently there is no dedicated fluent API for that anyway).

For instance:

modelBuilder.Entity<Rack>()
    .FindNavigation(nameof(Rack.AssignedTrays))
    .SetField("assignedTrays");
2
3/10/2020 1:36:32 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow