Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding virtual and calculated properties in Entity Framework

I have a Job model that contains a number of properties, and a set of linked entities called Quotes:

public class Job
{
    ....
    public virtual ICollection<Quote> Quotes { get; set; }
}

In my Job class I have the following calculated property:

public decimal QuotesAwarded
{
    get
    {
        if (Quotes == null || !Quotes.Any())
        {
            return 0;
        }

        var totalUnapprovedQuotes = Quotes.Where(x => x.Status != "Approved");

        return 1 - (totalUnapprovedQuotes.Count() / Quotes.Count());

    }
    set
    {

    }

}

I have 2 questions:

  1. When I debug this property, Quotes is null (even though there are attached quotes to this entity). I thought that using virtual means that this shouldn't occur? How can I ensure that whenever the model is constructed, the related Quote entities are attached?

  2. The reason I'm doing this is that the property value is stored in the database, so it reduces compute time as it's pre-calculated, is this correct?

Follow up:

In most cases I'm not using Include<Quotes> when retrieving the job object. I'm using Include only when I need the QuotesAwarded value.

However if I don't use Include (say db.jobs.find(id)), and Quotes is null, and the QuotesAwarded value will be 0. So this will get saved to the database when I save the job object, I've really confused myself here.

like image 586
Robbie Mills Avatar asked Mar 19 '26 16:03

Robbie Mills


1 Answers

For your first question, the virtual keyword is used as an indication to Entity Framework to lazily load this. However, it appears that you have disabled lazy loading so you always need to .Include(...) it. As your property is reliant on the quotes being loaded, it will always return 0.

What you are doing is almost right, you just need to let Entity Framework know that your property is a computed column. To do this, you just need to annotate it with an attribute:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string QuotesAwarded
{
    get
    {
        if (Quotes == null || !Quotes.Any())
        {
            return 0;
        }

        var totalUnapprovedQuotes = Quotes.Where(x => x.Status != "Approved");

        return 1 - (totalUnapprovedQuotes.Count() / Quotes.Count());
    }

    private set
    {
        //Make this private so there's no temptation to set it
    }
}
like image 149
DavidG Avatar answered Mar 21 '26 06:03

DavidG



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!