Consider these simple classes. They belong to a simple application with Domain Driven Design (DDD) principles, and as such every Entity and ValueObject receives its property values through the constructor while hiding the default, parameter-less constructor. Properties will also be read-only.
public class MyClass
{
public Guid Id {get;}
public ValueObject ValueObject1 {get;}
public ValueObject ValueObject2 {get;}
public MyClass(ValueObject valueObject1, ValueObject valueObject2)
{
ValueObject1 = valueObject1;
ValueObject2 = valueObject2;
}
private MyClass(){}
}
public class ValueObject
{
public string Value {get;}
public ValueObject(string value)
{
Value = value;
}
private ValueObject(){}
}
I want to be able to create a database based on this model, using EntityFramework Core 2.2.6.
Apparently EF Core 2.2.6 can automatically feed property values for these classes through their parametrized constructors, as long as constructor parameters and class properties have the same name (case-insensitive). Great.
Now I want the ValueObjects to be stored in the same table as the MyClass. To make that happen, I am told, I should use modelBuilder.OwnsOne<>
in OnModelCreating
of the DBContext, instead of modelBuilder.Property<>
The DBContext configuration in OnModelCreating
would look like something this:
modelBuilder.Entity<MyClass>(b => b.HasKey(mc => mc.Id));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject1,rb =>
{
rb.Property(vo => vo.Value);
}));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject2, rb =>
{
rb.Property(vo => vo.Value);
}));
Now it seems modelBuilder.OwnsOne<>
and modelBuilder.Property<>
are mutually exclusive, meaning you can't use them both together because every time I try to Add-Migration
with both of them I get:
'ValueObject' cannot be used as a property on entity type 'MyClass' because it is configured as a navigation.
But if I don't use modelBuilder.Property<>
and only use modelBuilder.OwnsOne<>
, I get:
No suitable constructor found for entity type 'MyClass'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'valueObject1', 'valueObject2' in 'MyClass(ValueObject valueObject1, ValueObject valueObject2)'.
Which means the constructor to property binding pattern only works only if I use modelBuilder.Property<>
to configure the properties on MyClass
.
So my question is: how should I configure the DBContext to allow EF Core to both set property values through the parametrized constructor, and store ValueObjects in the same table as the Entity?
context.Database.EnsureCreated()
alright. The only thing is, I have to addprivate set
to the properties, just as is done here. If I don't, I get an exception that a field "is readonly and so cannot be set" when EF tries to build the model. Which is not the perfect implementation of a value object but seems necessary anyway. – Gert ArnoldMyClass
object now you have to jump through hoops to get it done, if it can be done at all. An EF class model should be mutable because the whole architecture is based on tracking changed properties. – Gert Arnold