Create helper methods which returns desired results. Everything should play around IQueryable:
public class CustomerIdWithBalance
{
public int CustomerId { get; set; }
public decimal Balance { get; set; }
}
public class CustomerWithBalance
{
public Customer Customer { get; set; }
public decimal Balance { get; set; }
}
public static class BusinessLogicExtensions
{
public static IQueryable<CustomerIdWithBalance> GetCustomerIdAndBalance(this IQueryable<Customer> customers)
{
var grouped =
from c in customers
from si in c.SalesInvoices
from sid in si.SalesInvoiceDetails
group sid by new { c.CustomerId } into g
select new CustomerIdWithBalance
{
g.Key.CustomerId,
Balance = x.Sum(x => x.Qty * x.UnitPrice)
}
return grouped;
}
public static IQueryable<CustomerWithBalance> GetCustomerAndBalance(this IQueryable<CustomerIdWithBalance> customerBalances, IQueryable<Customer> customers)
{
var query =
from b in customerBalances
join c in customers on b.CustomerId equals c.CustomerId
select new CustomerWithBalance
{
Customer = c,
Balance = b.Balance
};
return query;
}
}
Later when you need to return that with API call (hypothetic samples)
var qustomerIdsWithHighBalance =
from c in ctx.Customers.GetCustomerIdAndBalance()
where c.Balance > 1000
select c.CustomerId;
var qustomersWithHighBalance =
ctx.Customers.GetCustomerIdAndBalance()
.Where(c => c.Balance > 1000)
.GetCustomerAndBalance(ctx.Customers);
var customersByMatchAndPagination = ctx.Customers
.Where(c => c.Name.StartsWith("John"))
.OrderBy(c => c.Name)
.Skip(100)
.Take(50)
.GetCustomerAndBalance(ctx.Customers);
You will get desired results without additional database roundtrips. With properties you may load too much data into the memory.
It is everything about using EF with its limitations. But world is not stopped because EF team is too busy to create performance effective things.
Let's install https://github.com/axelheer/nein-linq
And create extension methods around Customer
public static class CustomerExtensions
{
[InjectLambda]
public static TotalBalance(this Customer customer)
=> throw new NotImplmentedException();
static Expression<Func<Customer, decimal>> TotalBalance()
{
return customer =>
(from si in customer.SalesInvoices
from sid in si.SalesInvoiceDetails
select sid)
.Sum(x => x.Qty * x.UnitPrice));
}
}
And everything become handy:
var customersWithHighBalance =
from c in ctx.Customers.ToInjectable()
where c.TotalBalance() > 1000
select c;
var customersWithHighBalance =
from c in ctx.Customers.ToInjectable()
let balance = c.TotalBalance()
where balance = balance > 1000
select new CustomerWithBalance
{
Customer = c,
Balance = balance
};
var customersWithBalance =
from c in ctx.Customers.ToInjectable()
where c.Name.StartsWith("John")
select new CustomerWithBalance
{
Customer = c,
Balance = c.TotalBalance()
};
var paginated =
.OrderBy(c => c.Name)
.Skip(100)
.Take(50);
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…