How to map an owned property to a private field

c# ef-core-3.0 entity-framework-core

Question

I have a value object, which means that its properties have getters but not setters. Then I have an entity with a public getter and a private field. Something like the following:

public class Settings {
  private string _instance;
  private string _domain;

  public Settings(string instance, string domain) {
    _instance = instance;
    _domain = domain;
  }
  private Settings() {
    _instance = default!;
    _domain = default!;
  }

  public string Instance => _instance;
  public string Domain => _domain;
}

public class Tenant {
  private Guid _id;
  private string _name;
  private Settings _settings;

  public Tenant(string name) {
    _id = Guid.Empty;
    _name = name;
    _settings = new Settings("etc", "etc");
  }
  private Tenant() {
    _id = default;
    _name = default!;
    _settings = default!;
  }

  public Guid Id => _id;
  public string Name => _name;
  public Settings => _settings;

  protected void OnSettingsChanged() { 
    // do stuff like adding domain events and sending notifications 
  }
  public void ChangeSetting(Settings newSettings) {
    // do some model validation
    _settings = newSettings;
    OnSettingsChanged();
  }
}

I'm using C# 8, .NET Core 3 and Entity Frmework Core 3. Now in my implementation of IEntityTypeConfiguration<Tenant>, for configuring say Name I have no problem, as I can use HasField and pass the name of the private property. But that is not working for Settings. There, I use OwnsOne, but there is no HasField property available.

builder.Property(x => x.Name)
  .HasField("_name")
  .HasMaxLength(100);
builder.OwnsOne(x.Settings, y => {
  y.Property(z => z.Instance)
   .HasField("_instance")
   .IsRequired(true);
  y.Property(z => z.Domain)
   .HasField("_domain")
   .IsRequired(true);
  //y.HasField() ??
});

However I can't find a way to tell EF that the Settings property should be mapped to the private _settings variable. I tried adding a call to Property before the call to OwnsOne:

...
builder.Property(x => x.Settings)
  .HasField("_settings");
builder.OwnsOne(x.Settings, y => {
  y.Property(z => z.Instance)
   .HasField("_instance")
   .IsRequired(true);
  y.Property(z => z.Domain)
   .HasField("_domain")
   .IsRequired(true);
});

But then when I add a migration, I get an error: The property or navigation 'Settings' cannot be added to the entity type 'Tenant' because a property or navigation with the same name already exists on entity type 'Tenant', which I suppose happens because I'm configuring the property Settings twice.

So how could I achieve this, what's the equivalent of HasField when using OwnsOne? Thanks in advance.

1
1
11/7/2019 8:10:59 PM

Accepted Answer

You don't need all these HasField calls because in EF Core 3.0 Backing fields are used by default and also your backing fields follow one of the EF Core property backing field naming conventions, so EF Core will automatically discover and use them (you still need Property calls though because get only properties are not discovered by convention).

But let say you need to specify the backing field of the property in question. Property method cannot be used because by EF Core terminology these are not properties, but navigations. There is no fluent API similar to HasField for property builders, so you have to use directly the mutable metadata services:

builder.OwnsOne(x => x.Settings, y =>
{
    // ...
    y.Metadata.PrincipalToDependent.SetField("_settings");
});

It's similar to relationship navigation properties. The Metadata property of the corresponding builder returns IMutableForegnKey object. PrincipalToDependent and DependentToPrincipal properties return IMutableNavigation objects which can be used to configure the corresponding navigation property.

1
11/7/2019 8:10:34 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