0

Given a couple of entities, Invoice and InvoiceRow, I want to calculate the total amount by summing up each row's amount.

public class Invoice
{
    [Key]
    public int? Id { get; set; }

    public List<InvoiceRow> Rows { get; } = new List<InvoiceRow>();

    [NotMapped]
    public decimal TotalAmount => Rows.Sum(row => row.RowAmount); 

}

public class InvoiceRow
{
    [Key]
    public int? Id { get; set; }
    public Invoice Invoice { get; set; }
    public decimal RowAmount { get; set; }
}

If I load the invoice this way:

Invoice invoice = dbContext.Invoices.Include(i=>i.Rows).FirstOrDefault();

the TotalAmount will be correct.

If I load the invoice this way:

Invoice invoice = dbContext.Invoices.FirstOrDefault();

the TotalAmount will be zero, unless somewhere in the code I loaded the rows as a second step.

This is something that comes with the introduction of an ORM like Entity Framework core. But in complex code, you may not know where your invoice object comes from and if Rows collection has been loaded or not. I would like to be sure that when TotalAmount gets called, the rows are loaded.
The obvious place to do it in OOP terms would be in the TotalAmount property.
That would be perfect. Unfortunately, usually we don't want to expose the datacontext to the entities, for valid reasons that I often fail to remember (but when I finally do, they seem very reasonable).
So each invoice actually cannot know if it has rows or not, only from the outside we can tell.
This sounds quite paradoxical to me because it seems obvious that TotalAmount is something that should be an exclusive responsibility of Invoice class.
So the first question is: which way should I update my mental OOP concepts to match this situation? I don't know any pattern that states this.

Then there is a more pragmatic question.
Given an Invoice object passed as a parameter, I don't know where it comes from, I want to be sure that its rows -if any- have been loaded without reloading them. This would allow for a great performance improvement.
That is because when processing multiple Invoices I could load them in a single database query.
How and where can I do this? I'm using a repository pattern but it's a place where entities are created, so still it doesn't solve the problem of an entity that you receive and you don't know where it has been created. Obviously I'm not considering the radical solution of creating only Invoices with Rows, that would solve the problem at the expense of performances.
Should I expand my horizons in terms of pattern used? And with what new pattern?

P.S. I'm using EFCore, but I imagine that most ORMs work this way and require these patterns.

2
  • 2
    How about Lazy Loading the Rows? – Robert Harvey Feb 12 at 0:01
  • Introduce two types one for invoice "base" information without rows and another for invoice with rows and total amount. – Fabio Feb 12 at 8:00
1

The answer

@RobertHarvey is correct, this problem is the precise use case for lazy loading, which EF happily supports.

Lazy loading is delaying the loading of related data, until you specifically request for it. It is the opposite of eager loading.

Other than the link I provided, there are plenty of MSDN docs and tutorials about how to lazily load data using Entity Framework.


The subsequent issue

However, there is a general good practice tendency to favor the more rigorous approach of enforcing eager loading (i.e. forcing you to Include your data right from the get go and disallowing additional lazy loads), because lazy loading can make your life significantly harder when debugging your application. Enforcing eager loading also avoids nasty bugs like the context going out of scope, which is an issue that you only uncover at runtime, not compile time.

Both approaches have their benefits and drawbacks.

Lazy loading

  • Pro You avoid loading data that you never use
  • Pro You don't have to declare in advance that you're going to use a specific dataset
  • Con Each time you ask for something new, that's a new trip to the database.
  • Con If the context gets closed, and you then try to access an unloaded property, you'll be hit with a runtime exception.
  • Con Debugging lazy loading is more difficult.

Eager loading

  • Pro Everything you need is loaded in a single trip to the database
  • Con You have to explicitly ask what you want to have loaded
  • Con You might end up loading more data than you end up using
  • Pro Your query logic explicitly shows which data is loaded/used for this query
  • Hidden pro Knowing what data you want helps with understanding the scope of your query. In other words, if you already knew what the end goal of your query was (down to every data field), then explicitly listing that data isn't particularly hard. By forcing you to list it, it forces you to understand your query before you write it, which is generally a good thing.
  • Con Failing to load data that you then try to access can lead to null reference exceptions
    • Pro This is easier to spot/debug/fix than the equivalent "context out of scope" exception that lazy loading entails.

In the end, it's mostly a matter of weighing whether you'd rather suffer the performance issues from loading too much data (by eager loading), or the performance issues from going to the database frequently (by lazy loading). No matter which loading strategy you choose, you'll always have to make sure that you avoid the relevant performance issues.

4
  • Thanks Flater. You talked about "forcing you to Include". Can this be enforced in some way like raising an error if rows are not loaded? I still see something not SOLID in EF and I think that EF designers are trying to defend their design by endlessly repeating the distinction eager/lazy, but never explaining how this principle should play together with other well established OOP patterns like SRP. The result is that here I should eager loading everything because there is no safe way to tell if an Invoice has been fully loaded or not. – AgostinoX Feb 13 at 1:44
  • @AgostinoX: To disable lazy loading, set your context's this.Configuration.LazyLoadingEnabled = false. Example here. Lazy/eager loading has little to do with SOLID or SRP in and of itself. "The result is that here I should eager loading everything because there is no safe way to tell if an Invoice has been fully loaded or not." Either I misunderstand, you misspoke, or it seems like you're not understanding that lazy loading specifically allows you to not need to know that the invoices have been loaded or not. – Flater Feb 13 at 9:02
  • Thankyou Flater. With lazy loading there is no need to know if a piece of data is loaded or not because the first time it's accessed (and only if it's accessed) it gets loaded. Right? Well so I've understood quite well. But the line of code : public decimal TotalAmount => Rows.Sum(row => row.RowAmount); gives a correct result only if either: a) you enable lazy loading for this collection b) you eager load the collection before you enter the method. The (b) seems to me an anti-pattern. So we are left only with the (a) option. No pro/cons assessment if the only possible solution is one. – AgostinoX Feb 13 at 11:12
  • I think that there should be something in between the options of eager loading and lazy loading. If you don't enable lazy loading, an Invoice with 0 rows is indistinguishable from one with 10 rows not-yet loaded. Their state is the same. If that class had the piece of information "AreRowsLoaded", the method could check and throw an error if not. This would allow for working with eager loading without introducing a further brittleness. One could tell that "AreRowsLoaded" is an information that belongs to Invoice class, that's why I'm talking about SRP even if in a rough way. – AgostinoX Feb 13 at 11:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.