Reputation: 491
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.
Upvotes: 1
Views: 2313
Reputation: 205589
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.
Upvotes: 1