Refactor LINQ ... умножение свойства и дочернего свойства из списка - PullRequest
2 голосов
/ 24 августа 2011

Я все еще прихожу к соглашению с LINQ и пытаюсь преобразовать следующий цикл foreach в его эквивалент LINQ. Вот цикл foreach, который я пытаюсь преобразовать;

var NetTotal = 0M;

foreach (var cheque in ListOfCheques)
{
    var exchangeRate = (from exc in cheque.ExchangeRates
                        where exc.Type == EnumExchangeRate.ForCheque
                        select exc).FirstOrDefault();

    NetTotal = exchangeRate != null ? NetTotal + cheque.NetAmount * exchangeRate.Rate : NetTotal + cheque.NetAmount;
}

return NetTotal ;

и код LINQ, который я придумал;

var NetTotal = (from cheque in ListOfCheques
                join exc in ListOfCheques.SelectMany(b => b.ExchangeRates) on cheque.ID equals exrate.Cheque.ID into chequeWithRate
                where income.ExchangeRates.Select(x => x.Type).Equals(EnumExchangeRate.ForCheque)
                from ur in chequeWithRate.DefaultIfEmpty()
                select ur).Sum(x => x.Cheque.NetAmount * x.Rate);

return NetTotal;

Важные моменты, с которыми я борюсь;

  1. Возможно, что список "ExchangeRates" в классе Check не существует, то есть ему не нужен обменный курс.
  2. Если обменный курс не найден, по умолчанию он должен равняться 1. Как я могу установить это ... Я надеялся установить его в DefaultIfEmpty (1).

Любая помощь очень ценится.

Ответы [ 3 ]

1 голос
/ 24 августа 2011

Как и любой рефакторинг, просто отсеивать по кусочкам. Во-первых, вместо foreach просто используйте Sum(), например, так.

return ListOfCheques.Sum(c =>
{
    var exchangeRate = (from exc in c.ExchangeRates
        where exc.Type == EnumExchangeRate.ForCheque
        select exc).FirstOrDefault(); 
    return c.NetAmount * (exchangeRate ?? new ExchangeRate(){ Rate = 1 }).Rate;
});

(было бы неплохо, если бы значением по умолчанию для свойства ExchangeRate.Rate было 1)

Я бы переписал функцию exchangeRate в простой формат. Вы уверены, что хотите FirstOrDefault, а не SingleOrDefault?

var exchangeRate = c.ExchangeRates.
    FirstOrDefault(ex => ex.Type == EnumExchangeRate.ForCheque);

и затем это может быть заменено на первое утверждение, оставляя конечный продукт ниже.

Один лайнер, если хотите, чтобы он был!

return ListOfCheques.Sum(c => c.NetAmount * 
    (c.ExchangeRates.FirstOrDefault(ex => ex.Type == EnumExchangeRate.ForCheque)
        ?? new ExchangeRate() { Rate = 1 }).Rate);

редактировать

Разъяснение по поводу новый ExchangeRate ()

Вместо того, чтобы делать != null ? (amount * rate) : (rate), я предпочитаю объединять объект ExchangeRate с новым таким объектом с Rate = 1. Я думаю, что это обеспечивает более гладкую и чистую часть кода. Я настоятельно рекомендую вам сделать ставку по умолчанию равной 1,0, а затем вы можете просто объединиться с new ExchangeRate(), без необходимости устанавливать свойство Rate.

Чтобы установить значение по умолчанию для Rate в новом объекте ExchangeRate, просто поместите инициализатор внутри конструктора

class ExchangeRate
{
    public ExchangeRate()
    {
        this.Rate = 1.0;
    }    
    // other stuff
}
0 голосов
/ 24 августа 2011

Как насчет этого?

var query =
    from cheque in ListOfCheques
    let rate =
        cheque.ExchangeRates
            .Where(exc => exc.Type == EnumExchangeRate.ForCheque)
            .Select(exc => exc.Rate)
            .DefaultIfEmpty(1.0M)
            .First()
    select rate * cheque.NetAmount;

var NetTotal = query.Sum();

Ваш пример запроса LINQ, приведенный в вашем вопросе, содержит "лишние" вещи, которые вы не объяснили, поэтому я включил только элементы из вашего цикла foreach.

0 голосов
/ 24 августа 2011

Вам это нужно?

           var query = from cheque in ListOfCheques
                        let excRates = cheque.ExchangeRates ?? Enumerable.Empty()
                        let rate = excRates.Where(x => x.Type == Something).Select(x => x.Rate).FirstOrDefault() ?? 1
                        select cheque.NetAmount * rate;

            var netTotal = query.Sum();

Если значение Rate равно nulllable, вы можете либо принять это в выражении let, сделав его обнуляемым (например, Select (x => new int? (X.Rate)) или удалите ?? 1 и адептируйте его по вашему выбору. который составит:

           var query = from cheque in ListOfCheques
                        let excRates = cheque.ExchangeRates ?? Enumerable.Empty()
                        let rate = excRates.Where(x => x.Type == Something).Select(x => x.Rate).FirstOrDefault()
                        select cheque.NetAmount * (rate != 0 ? rate : 1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...