How to store a conditional/calculated property in ef core 6
Hello,
I have the following entities
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
private readonly List<Child> _children;
public IReadOnlyCollection<Child> Children => _children;
public Status Status { get; private set; }
private int _statusId;
}
public class Child
{
private string _description;
private bool _isValid;
// other data fields
public bool IsValid()
{
return _isValid == true;
}
protected Child() { }
}
public class Status : Enumeration
{
// Enumeration is from https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs
public static Status Pending = new(1, nameof(Pending));
public static Status InValid = new(2, nameof(InValid));
public static Status Valid = new(3, nameof(Valid));
public Status(int id, string name)
: base(id, name)
{ }
public static IEnumerable<Status> List() =>
new[] { Pending, InValid, Valid };
}
My question is how to configure the Status property and the _statusId in Parent Entity such that,
When _children collection is empty Status needs to be set to Pending, or
When all objects in _children collection returns true from IsValid() method, Status needs to be set to Valid, else
Status needs to be set to InValid.
Basically the Status is set from the state of the _children collection
Regards,
You could simply make Status a regular C# property which looks at Children and and calculates the return value from that. Configure that property as unmapped in order to make sure it isn't persisted to the database.
You could simply make Status a regular C# property which looks at Children and and calculates the return value from that. Configure that property as unmapped in order to make sure it isn't persisted to the database.
Thanks for the quick reply,
I did consider this, but in the database i need to store the statuses, currently my entity config for parent looks like the following
public class ParentEntityTypeConfiguration : IEntityTypeConfiguration<Parent>
{
public void Configure(EntityTypeBuilder<Parent> parentConfiguration)
{
parentConfiguration.ToTable("parents", AppDbContext.DEFAULT_SCHEMA);
parentConfiguration.HasKey(t => t.Id);
parentConfiguration.Property(t => t.Id)
.HasColumnName("id")
.UseHiLo("parentseq", AppDbContext.DEFAULT_SCHEMA);
parentConfiguration.Property(t => t.Name)
.HasColumnName("name")
.HasMaxLength(64)
.IsRequired();
parentConfiguration.Property<int>("_statusId")
.UsePropertyAccessMode(PropertyAccessMode.Field)
.HasColumnName("status_id")
.IsRequired();
parentConfiguration.HasOne(o => o.Status)
.WithMany()
.HasForeignKey("_statusId");
var navigation = parentConfiguration.Metadata.FindNavigation(nameof(Parent.Children));
navigation?.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
So is there a way to make the status logic work while also mapping the status in ef core?
I did try the following,
public Status Status => _children.Count() == 0
? Status.Pending
: (_children.All(a => a.IsValid()) ? Status.Valid : Status.InValid);
private int _statusId;
but then ef fails the migration with the error No backing field could be found for property 'Parent.Status' and the property does not have a setter.
Duplicate of #13316. For now, use an empty setter as shown in that issue.
thanks for the tip @ajcvickers, have the following getter and setter for the field,
public Status Status
{
get
{
return _children.Count() == 0
? Status.Pending
: (_children.All(a => a.IsValid()) ? Status.Valid : Status.InValid);
}
private set { }
}
private int _statusId;
and it is working fine for the most part,
that is, the first db row insert to parent table works, but any consequent inserts fails with the following exception Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_status'. Cannot insert duplicate key in object 'dbo.status'. The duplicate key value is (1)
As far as i know, ef core is trying to insert the status for each parent save, but i am unsure how to prevent that.
@mdhthahmd This issue is lacking enough information for us to be able to fully understand what is happening. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.
Hi, @ajcvickers i have created a repro as instructed, i hope this provides a full picture. Hope this clarifies the issue.
Regards
@mdhthahmd The Status.Id is configured as .ValueGeneratedNever() and always has the value of 1 when inserting.