I'm having trouble getting my backing fields to work, i've tried my way through using this documentation: https://learn.microsoft.com/en-us/ef/core/modeling/backing-field with no luck.
When i try to add the migration i'm greeted with this error:
The property 'Workflow._step1' is of type 'Step1' 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'.
Multiple workflows can use the same step, so i want it to be saved like this
Workflow
{
Id,
Step1Id
Step2Id
}
Example that does not work code:
public class Workflow
{
private Step1 _step1;
private Step2 _step2;
public Guid Id { get; set; } = Guid.NewGuid();
public bool Step1Enabled => true;
public Step1 Step1 => Step1Enabled ? _step1 : null;
public bool Step2Enabled => _step1.Completed;
public Step2 Step2 => _step2Enabled ? _step2 : null;
}
public class Step1
{
public Guid Id { get; set; } = Guid.NewGuid();
public bool StatusUniqueToStep1 { get; set; }
public bool Completed {get; set; }
}
public class Step2
{
public Guid Id { get; private set; } = Guid.NewGuid();
public bool StatusUniqueToStep2 { get; set; }
public bool Completed {get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) :base(options)
{}
public DbSet<Workflow> Workflows { get; set; }
// Tried adding these, does not work.
// public DbSet<Step1> Step1 { get; set; }
// public DbSet<Step2> Step2 { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Tried adding these, does not work.
// modelBuilder.Entity<Step1>();
// modelBuilder.Entity<Step2>();
modelBuilder.Entity<Workflow>()
.Property<Step1>("_step1");
modelBuilder.Entity<Workflow>()
.Property<Step2>("_step2");
}
}
Here is the case. By EF Core terminology these are not properties, but navigation properties, so they cannot be configured with Property
fluent API (and in general are not returned by any metadata/entry method having Property
/ Properties
in the name).
Instead, they are configured through relationship related fluent APIs. The problem with mapping the backing field though is that there is no natural fluent API for that similar to "properties", so you have to use directly the metadata.
The configuration could be like this:
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step1)
.WithMany()
.Metadata.DependentToPrincipal.SetField("_step1");
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step2)
.WithMany()
.Metadata.DependentToPrincipal.SetField("_step2");
or taking into account that the backing field names follow one of the EF Core naming conventions:
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step1)
.WithMany()
.Metadata.DependentToPrincipal.SetPropertyAccessMode(PropertyAccessMode.Field);
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step2)
.WithMany()
.Metadata.DependentToPrincipal.SetPropertyAccessMode(PropertyAccessMode.Field);
But this is also the EF Core default behavior. So the actual problem is not the backing field, but the fact that EF Core by default does not include read only (no setter) properties (simple or navigation, doesn't matter). Hence the minimal configuration to make this work is like this:
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step1);
modelBuilder.Entity<Workflow>()
.HasOne(e => e.Step2);
Update: In order to force EF Core to use the backing field for both set
(when reading from database) and get
(change tracking, storing to database), use the second configuration - with .Metadata.DependentToPrincipal.SetPropertyAccessMode(PropertyAccessMode.Field)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With