Скажем, у меня есть модель сущности ..
public class MyEntity
{
public DateTime TransactionDate {get; set;}
public decimal TotalSales {get; set;}
public long TotalPurchases {get; set;}
}
, и я довольно легко заполняю список этой сущности из базы данных SQLServer для диапазона дат транзакции.
var results = await _dbContext.StoresDailyTransactions
.Where(t => t.TransactionDate > = rangeStartDate && t.TransactionDate <= rangeEndDate)
.GroupBy(o => o.TransactionDate)
.Select(g => new MyEntity
{
TransactionDate = g.Key,
TotalSales = g.Sum(c => c.Sales),
TotalPurchases = g.Sum(c => c.Purchases),
})
.DefaultIfEmpty()
.ToListAsync();
Теперь необходимо добавить AverageSale к сущности ...
public class MyEntity
{
public DateTime TransactionDate {get; set;}
public decimal TotalSales {get; set;}
public long TotalPurchases {get; set;}
public decimal AverageSale {get; set;}
}
Это достаточно просто, потому что у меня есть значения, уже выбранные в моем запросе, просто нужно вычислить среднее и присвоить его новое свойство AverageSale ....
AverageSale = TotalSales / TotalPurchases
Я рассматривал возможность сделать это двумя разными способами.
1) Просто повторите мой существующий список результатов, полученных ранее, и установите новое свойство перед возвратом результаты.
foreach(MyEntity currentEntity in results)
{
currentEntity.AverageSales = currentEntity.TotalSales / currentEntity.TotalPurchases;
}
2) Сделайте все в операторе linq, выбрав промежуточную анонимную проекцию, а затем окончательную проекцию MyEntity с новым AverageSale.
var results = await _dbContext.StoresDailyTransactions
.Where(t => t.TransactionDate > = rangeStartDate && t.TransactionDate <= rangeEndDate)
.GroupBy(o => o.TransactionDate)
.Select(g => new
{
TransactionDate = g.Key,
TotalSales = g.Sum(c => c.Sales),
TotalPurchases = g.Sum(c => c.Purchases),
})
.Select(a => new
{
TransactionDate = a.TransactionDate,
TotalSales = a.TotalSales,
TotalPurchases = a.TotalPurchases,
AverageSale = a.TotalSales / a.TotalPurchases,
})
.DefaultIfEmpty()
.ToListAsync();
Первая вариант очень легко читать и понимать, что происходит, но от него есть какой-то запах, и кажется, что его может легко сломать неосторожный разработчик, потому что он отделен от всего остального ... например, потеряно в конфликте слияния, случайно удалено или закомментировано.
Второй вариант действительно кажется менее подверженным ошибкам, потому что либо все работает, либо нет. Хотя он кажется менее читаемым, и требуется секунда, чтобы понять, почему он проецируется двумя операторами select один за другим.
Есть ли что-нибудь еще, что делает один из них объективно лучше, например, производительность et c или может быть, даже лучший способ, который я не рассматривал?