Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplify process with linq query

This is my Table:

PupilNutrition

Id     PupilId   NutritionId
1       10          100  
2       10          101

My another table Nutrition:

Id  Nutritioncategory  BatchId   NutritionRate   NutritionId   Operation
1       A                1         9000             100          1
2       B                1         5000             100          0
3       C                1         5000             100          1
4       D                2         6000             101          2           
5       E                2         7000             101          2         
6       F                2         8000             101          0          

This is one field to store final Rate:

decimal Rate= 0;

Case 1: Operation field with Value 0 and Batch Id 1

 Rate= Rate + NutritionRate(i.e 5000 because for batch id 1 with condition 0 only 1 record is there).

Case 2:Now for operation field with Value 1 and Batch Id 1

Here i want to sum up both the value i.e(10000 and 5000) but during addition if sum of both is greater than 10000 then just take 10000 else take sum like this:
if(9000 + 5000 > 10000)
  Rate= 10000
else
    Rate=Rate + (Addition of both if less than 10000).

Case 3:Now for operation field with Value 0 and Batch Id 2

 Rate= Rate + NutritionRate(i.e 8000 because for batch id 1 with condition 0 only 1 record is there).

Case 4:Now for operation field with Value 2 and Batch Id 2

Here I will select Maximum value from Nutrition Rate field if there are 2 records:
Rate=Rate - (Maximum value from 6000 and 7000 so will take 7000).

My Class file:

 public partial class PupilNutrition
    {
        public int Id { get; set; }
        public int PupilId { get; set; }
        public int NutritionId { get; set; }
    }

 public partial class Nutrition
    {
        public int Id { get; set; }
        public string Nutritioncategory { get; set; }
        public decimal NutritionRate  { get; set; }
        public Nullable<int> NutritionId { get; set; }
    }

This is how i have Done:

batchId=1 or 2;//
 List<int> NutritionId = db.PupilNutrition.Where(x => PupilId  == 10).Select(c => c.NutritionId).ToList();  //output 100,101
 var data = db.Nutrition.Where(x => x.BatchId == batchId && NutritionId.Contains(x.NutritionId.Value)).ToList();

I have tried like this but its a labour process:

var data1=data.where(t=>t.Operation==0);
Rate= Rate + data1.Sum(p => p.NutritionRate);

Similarly for Operation 1:

var data2=data.where(t=>t.Operation==1);
This is left because here i need to sum up 2 value if two records are there as shown in my above cases and if sum is greater than 10000 than select 10000 else select sum.

Similarly for Operation 2:

var data3=data.where(t=>t.Operation==2);
Rate= Rate - data3.Max(p => p.NutritionRate);

I think i have done a labour process as this can be done even in better way with linq query only i guess.

So can anybody help me to simplify this whole process in linq query or even some better way and provide solution for Operation Value 2 which is left?

like image 311
Learning-Overthinker-Confused Avatar asked Sep 04 '15 11:09

Learning-Overthinker-Confused


1 Answers

Shortest is not always the best.

Personally, I think that single complex LINQ statement can be hard to read and hard to debug.

Here is what I would do:

    public decimal CalculateRate(int pupilId, int batchId)
    {
        return db.Nutrition
            .Where(x => x.BatchId == batchId && db.PupilNutrition.Any(y => y.PupilId == pupilId && y.NutritionId == x.NutritionId))
            .GroupBy(x => x.Operation)
            .Select(CalculateRateForOperationGroup)
            .Sum();
    }

    public decimal CalculateRateForOperationGroup(IGrouping<int, Nutrition> group)
    {
        switch (group.Key)
        {
            case 0:
                // Rate = Sum(x)
                return group.Sum(x => x.NutritionRate);
            case 1:
                // Rate = Min(10000, Sum(x))
                return Math.Min(10000, group.Sum(x => x.NutritionRate));
            case 2:
                // Rate = -Max(x)
                return -group.Max(x => x.NutritionRate);
            default:
                throw new ArgumentException("operation");
        }
    }

Then you could use it as follows:

var rateForFirstBatch = CalculateRate(10, 1);
var rateForSecondBatch = CalculateRate(10, 2);
like image 92
Kaspars Ozols Avatar answered Oct 06 '22 00:10

Kaspars Ozols