C
C#3mo ago
TheKemo

Calculated Properties with EF Core

Hi everyone, I'm facing an issue with loading and calculating projected properties in my Entity Framework Core project using the EntityFramework.Projectables library. Here's the situation: I have a Product class with various properties and related entities, and each Product has a ProductMetrics class with several projectable properties. When I fetch a product using projection and map the properties, the values are correctly calculated. However, if I simply fetch all product entities, the values aren't calculated.
List<Product> products = await _productRepository.Query
.Include(x => x.Batches.Where(b => b.Active && batchesId.Contains(b.Id)))
.ThenInclude(x => x.Navigation.DocumentLine)
//.Include(x => x.StockMovements)
.Include(x => x.Metrics)
.Where(x => x.Active && productsId.Contains(x.Id))
.ToListAsync(cancellationToken);
List<Product> products = await _productRepository.Query
.Include(x => x.Batches.Where(b => b.Active && batchesId.Contains(b.Id)))
.ThenInclude(x => x.Navigation.DocumentLine)
//.Include(x => x.StockMovements)
.Include(x => x.Metrics)
.Where(x => x.Active && productsId.Contains(x.Id))
.ToListAsync(cancellationToken);
Example of Calculated Property in ProductMetrics
[Projectable]
public decimal LastCostPrice => Product.GetLastCostPrice();
[Projectable]
public decimal LastCostPrice => Product.GetLastCostPrice();
Product Extension where the Calculated Method is Implemented
[Projectable]
public static decimal GetLastCostPrice(this Product product) => product.StockMovements.Where(x => x.MovementCode < 50).OrderByDescending(x => x.CreatedAt).Select(x => x.UnitCost.GetValueOrDefault(0)).FirstOrDefault();
[Projectable]
public static decimal GetLastCostPrice(this Product product) => product.StockMovements.Where(x => x.MovementCode < 50).OrderByDescending(x => x.CreatedAt).Select(x => x.UnitCost.GetValueOrDefault(0)).FirstOrDefault();
In the LINQ query, if I explicitly include the StockMovements list, the values are calculated, but this results in fetching a large number of records, making the query slow and heavy. When I fetch using AutoMapper, for example, it works great, but in this case, I want the ChangeTracker to track the Product entity. Can anyone provide any expertise on this matter to guide me or tell me what I'm doing wrong? Thanks in advance!
8 Replies
Angius
Angius3mo ago
Calculated properties need data to calculate their value from That's why it requires you to fetch the necessary data, since the calculation is done on the client side Automapper generates SQL that calculates this value instead, so it does not require fetching everything The way to solve it is to stop using .Include() altogether, and always .Select() anything you query for into a DTO
TheKemo
TheKemo3mo ago
Thanks for reply But i need the ChangeTracker track my product Entity . If i do the .Select() i lose the tracking
Angius
Angius3mo ago
If you're doing fetch-update-save, why do you need the value of that calculated property?
TheKemo
TheKemo3mo ago
For example the document class has property that will define the type of Price of product will use. Product was 3 type of Price - Price - LastPrice (Calculated) - AveragePrice (Calculated) When saving the document, will create the stock Movements that are inside the product
Angius
Angius3mo ago
I'd maybe try fetching those separately, then, instead of using a computed property? Alternatively, use a calculated column
TheKemo
TheKemo3mo ago
Right now im using that approach but it feels whacky, the chance of me forgetting to fetch in another feature could happen Im gonna leave this open if someone with another idea comes along.
Angius
Angius3mo ago
Could also ask in #database
TheKemo
TheKemo3mo ago
Thanks for helping 👍
Want results from more Discord servers?
Add your server