вычитайте страховую сумму после каждого планового осмотра для каждого сотрудника

#c# #asp.net #asp.net-mvc #linq #asp.net-mvc-5

#c# #asp.net #asp.net-mvc #linq #asp.net-mvc-5

Вопрос:

Вот пример данных для моей проблемы,

 List<EMPINSURED> lstEmpIns = new List<EMPINSURED>{
    new EMPINSURED{ EmpID = 558, TOTAL_INSURED_AMT = 50000},
    new EMPINSURED{ EmpID = 559, TOTAL_INSURED_AMT = 75000} 
};

List<EMP_ROUTINE_CHECKUP_HISTORY> lstEmpRoutineChkHist = new List<EMP_ROUTINE_CHECKUP_HISTORY>{
    new EMP_ROUTINE_CHECKUP_HISTORY{ ID = 1, EmpID = 558, ROUTINE_CHECKUP = 1, CHECK_UP_CHARGE = 20000},
    new EMP_ROUTINE_CHECKUP_HISTORY{ ID = 2, EmpID = 559, ROUTINE_CHECKUP = 1, CHECK_UP_CHARGE = 35000},
    new EMP_ROUTINE_CHECKUP_HISTORY{ ID = 3, EmpID = 558, ROUTINE_CHECKUP = 2, CHECK_UP_CHARGE = 10000},
    new EMP_ROUTINE_CHECKUP_HISTORY{ ID = 4, EmpID = 559, ROUTINE_CHECKUP = 2, CHECK_UP_CHARGE = 10000},
    new EMP_ROUTINE_CHECKUP_HISTORY{ ID = 5, EmpID = 558, ROUTINE_CHECKUP = 3, CHECK_UP_CHARGE = 20000}
};
  

В год сотруднику разрешается только три плановых осмотра. Когда сотрудник проходит плановую проверку, его / ее расходы на проверку должны быть вычтены из общей страховой суммы. При следующем плановом осмотре сотруднику будут начислены расходы на проверку только на оставшуюся у него сумму от общей страховой суммы, какие-либо расходы, превышающие эту сумму, компания нести не будет.
Итак, логика расчета REMAINING AMOUNT будет примерно такой ::

     int var = 0;
if(ROUTINE_CHECKUP == 1)
{
    REMAINING_AMOUNT = TOTAL_INSURED_AMT - CHECK_UP_CHARGE;
    var = CHECK_UP_CHARGE;
} 
else if (ROUTINE_CHECKUP == 2)
{
    REMAINING_AMOUNT = TOTAL_INSURED_AMT - (CHECK_UP_CHARGE   var);
    var = CHECK_UP_CHARGE   var;
}
else if (ROUTINE_CHECKUP == 3)
{
    REMAINING_AMOUNT = TOTAL_INSURED_AMT - (CHECK_UP_CHARGE   var);
    var = CHECK_UP_CHARGE   var;
}
  

Как разработать запрос LINQ, чтобы получить приведенные ниже данные отчета, используя эту логику, или может быть какой-то лучший способ сделать это.
Пожалуйста, смотрите прикрепленное изображение для формата отчета.

введите описание изображения здесь

Комментарии:

1. Как насчет сотрудников, которые еще не прошли проверку? Вы хотите, чтобы они также были в списке с RoutineChekup = 0, CheckupCharge=0, RemainingAmount = TotalInsuredAmount ?

2. Да, я также должен их показать.

Ответ №1:

Используя некоторые System.Linq методы расширения, вы можете Join преобразовать lstEmpIns список с lstEmpRoutineChkHist списком в EmpId поле в анонимный тип, который имеет значения для столбцов из соответствующих полей в каждом списке, и упорядочить результаты с помощью RoutineCheckup использования OrderBy .

Неприятная часть заключается в определении RemainingBalance , потому что мы должны искать все записи для этого сотрудника, где RoutineCheckup меньше или равно текущему элементу, а затем суммировать CheckupCharge , чтобы мы могли вычесть его из RemainingBalance . Вероятно, есть лучший способ сделать это, но это то, что я придумал.

Вот пример, основанный на вашем коде, но использующий современные соглашения об именовании:

 var results = empInsureds
    .Join(empRoutineCheckupHistories,
        emp => emp.EmpId,
        hist => hist.EmpId,
        (emp, hist) => new
        {
            emp.EmpId,
            emp.TotalInsuredAmt,
            hist.RoutineCheckup,
            hist.CheckupCharge,
            RemainingBalance =
                emp.TotalInsuredAmt -
                empRoutineCheckupHistories
                    .Where(h => h.EmpId == hist.EmpId amp;amp;
                                h.RoutineCheckup <= hist.RoutineCheckup)
                    .Sum(h => h.CheckupCharge)
        })
    .OrderBy(r => r.RoutineCheckup)
    .ToList();
  

Некоторый код для генерации выходных данных:

 Console.WindowWidth = 81;

Console.WriteLine(GetRow("EmpID", "Total Insured Amt", "Routine Checkup", 
    "Checkup Charge", "Remaining Amount"));

Console.WriteLine(new string('-', 80));

results.ForEach(r => Console.WriteLine(GetRow($"{r.EmpId}", $"{r.TotalInsuredAmt:C}", 
    $"{r.RoutineCheckup}", $"{r.CheckupCharge:C}", $"{r.RemainingBalance:C}")));

GetKeyFromUser("nDone! Press any key to exit...");
  

Которая использует вспомогательную функцию:

 public static string GetRow(string col1, string col2, string col3, string col4, 
    string col5)
{
    return col1.PadRight(5)   " | "  
           col2.PadLeft(17)   " | "  
           col3.PadRight(16)   " | "  
           col4.PadLeft(14)   " | "  
           col5.PadLeft(16);
}
  

Вывод

введите описание изображения здесь

Комментарии:

1. Могу ли я использовать group предложение с этим для group by EmpID, RoutineCheckup ?

Ответ №2:

Моим советом было бы использовать одну из перегрузок запрашиваемых.GroupJoin или его эквивалент IEnumerable, если ваши источники не IQueryable, но IEnumerable.

С помощью GroupJoin вы можете получить «Всех сотрудников, каждого со всеми его нулевыми или более плановыми проверками». Используя параметр resultSelector, вы можете создать желаемый результат.

Преимущество группового объединения в том, что вы также получите сотрудников, которые до сих пор не проходили проверку. Если бы вы использовали объединение, вы бы вообще не получили этих сотрудников.

 var employeesWithTheirCheckups = dbContext.Employees.GroupJoin(
    dbContext.RoutineCheckUps,

    employee => employee.EmpId,     // from every Employee take the primary key
    checkup => checkup.EmpId,       // from every RoutineCheckup take the foreign key
                                   // to the Employee

    // parameter resultSelector: for every Employee and his zero or more Checkups,
    // make one new object:
    (employee, checkupsOfThisEmployee) => new
    {
        EmpId = employee.Id,
        TotalInsuredAmount = employee.TotalInsuredAmount,

        Checkups = checkupsOfThisEmployee.Select(checkup => new
        {
            RoutineCheckup = checkup.RoutineCheckup,
            CheckupCharge = checkup.CheckupCharge,

            // TODO: Remaining Amount   
        })
        .ToList(),
    });
  

Оставшиеся расходы на осмотр равны общей сумме этого сотрудника за вычетом «общих расходов на осмотр этого сотрудника до этого осмотра».

Итак, для истории с RoutineCheckup 2 вам нужно сложить все начисленные суммы всех историй с RoutineCheckups <= 2.

Это рассчитывается следующим образом:

 var chargedUntilNow = checkupsOfThisEmployee
    .Where(checkupOfThisEmployee => checkupOfThisEmployee <= checkup)
    .Select(checkupOfThisEmployee => checkupOfThisEmployee.CheckUpCharge)
    .Sum();
  

Оставшаяся сумма — это сумма, начисленная этому сотруднику до настоящего времени.

Вернуться к групповому соединению:

 (employee, checkupsOfThisEmployee) => new
{
    EmpId = employee.Id,
    TotalInsuredAmount = employee.TotalInsuredAmount,

    Checkups = checkupsOfThisEmployee.Select(checkup => new
    {
        RoutineCheckup = checkup.RoutineCheckup,
        CheckupCharge = checkup.CheckupCharge,

        // remaining is the total of this employee - chargedUntilNow.
        RemainingAmount = employee.TotalInsured -
            checkupsOfThisEmployee
            .Where(checkupOfThisEmployee => checkupOfThisEmployee <= checkup)
            .Select(checkupOfThisEmployee => checkupOfThisEmployee.CheckUpCharge)
            .Sum();
    })
    .ToList(),
  

Результатом является последовательность всех сотрудников (ну, только его идентификатор и его общая страховая сумма), каждый сотрудник с нулевыми или более проверками. Каждая проверка будет иметь номер обычной проверки, количество проверок и оставшееся количество.

Хорошая вещь: у вас также будут сотрудники, у которых еще не было проверки!

Кроме того: [EmpID, TotalInsuredAmount], будет переведен только один раз для каждого сотрудника. Если бы вы использовали стандартное объединение, то один и тот же [EmpID, TotalInsuredAmount] отправлялся бы один раз за проверку.

Если вы хотите, чтобы она была плоской, как ваш конечный результат, используйте SelectMany. Продолжение LINQ:

 .SelectMany(groupJoinResult, groupJoinResult.Checkups, 

// parameter resultSelector: take the GroupJoinResult, and each one of its Checkups
// to make one new:
(groupJoinResult, checkupOfThisGroupJoinResult) => new
{
    EmpId = groupJoinResult.EmpId,
    TotalInsuredAmount = groupJoinResult.TotalInsuredAmount,

    RoutineCheckUp = checkupOfThisGroupJoinResult.RoutineCheckup,
    CheckupCharge = checkupOfThisGroupJoinResult.CheckupCharge,
    RemainingAmount = checkupOfThisGroupJoinResult.RemainingAmount,
});
  

Проблема в том, что вы потеряете сотрудника, у которого еще не было проверок. Чтобы решить эту проблему, вам нужно будет указать значение по умолчанию