【问题标题】:Simplify process with linq query使用 linq 查询简化流程
【发布时间】:2015-09-04 11:24:32
【问题描述】:

这是我的桌子:

瞳孔营养

Id     PupilId   NutritionId
1       10          100  
2       10          101

我的另一张桌子营养

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          

这是一个存储最终Rate的字段:

decimal Rate= 0;

案例 1值为 0 且 Batch Id 为 1 的操作字段

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

案例 2现在用于值为 1 和 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).

案例 3现在用于值为 0 和 Batch Id 2 的操作字段

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

案例 4现在用于值为 2 和 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).

我的班级文件:

 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; }
    }

这就是我所做的:

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();

我试过这样,但这是一个劳动过程:

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

同样适用于操作 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.

对于操作 2 也是如此:

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

我认为我已经完成了一个劳动过程,因为这甚至可以通过我猜的 linq 查询以更好的方式完成。

那么任何人都可以帮助我简化 linq 查询中的整个过程,甚至是一些更好的方法,并为剩下的 Operation Value 2 提供解决方案吗?

【问题讨论】:

  • 请澄清 - 你想要 4 个不同的结果,还是一个结果,即 case1+case2+case3+case4 ?
  • @Rob:最终结果如下:案例1(利率)+案例2(利率)+案例3(利率)。只有3个条件是0,1,2跨度>

标签: c# asp.net-mvc performance linq


【解决方案1】:

最短的并不总是最好的。

就个人而言,我认为单个复杂的 LINQ 语句可能难以阅读和调试。

我会这样做:

    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");
        }
    }

那么你可以如下使用它:

var rateForFirstBatch = CalculateRate(10, 1);
var rateForSecondBatch = CalculateRate(10, 2);

【讨论】:

  • 不允许我这样使用:.Select(CalculateRateForOperationGroup)
  • 你能指定确切的错误信息吗?我能够在我的机器上运行这段代码。您可以尝试将.Select(CalculateRateForOperationGroup) 替换为.Select(x =&gt; CalculateRateForOperationGroup(x))
【解决方案2】:

组群更容易:

int sumMax = 10000;
var group = from n in nutrition
    group n by new { n.Operation, n.BatchId } into g
    select new { BatchId = g.Key.BatchId, 
        Operation = g.Key.Operation, 
        RateSum = g.ToList().Sum(nn => nn.NutritionRate), 
        RateSumMax = g.ToList().Sum(nn => nn.NutritionRate) > sumMax ? sumMax : g.ToList().Sum(nn => nn.NutritionRate), 
        RateMax = g.ToList().Max(nn => nn.NutritionRate) };
var result = from g in group
    select new {
        BatchId = g.BatchId,
        Operation = g.Operation,
        Rate = g.Operation == 1 ? g.RateSumMax :
               g.Operation == 2 ? g.RateMax :
               g.RateSum //default is operation 0
    };
var totalRate = result.Sum(g=>g.Rate);

【讨论】:

  • 您没有在查询中考虑我的条件(0,1,2),例如 0 何时会出现,1 会出现,2 会出现(最大值)
  • 我在答案中添加了条件,但我不确定我是否理解您的处理要求(这就是为什么我首先忽略了这些条件)。
  • 您在最后一个查询中检查的条件需要在您的第一个查询中检查。就像在求和或选择最大值之前,您需要检查运算值是 0,1 还是 2。对于例如:RateSum = g.Condition==1 ? g.ToList().Sum(nn => nn.NutritionRate).
  • OP说你需要减去最大值,而不是添加它
【解决方案3】:

更新 此查询看起来应该为您完成工作。注意,我还没有测试过,所以可能有一两个语法错误,但这个想法似乎是合理的。

var rate = db.Nutrition
        .GroupBy(n => new { n.BatchId, n.Operation })
        .Select(g => g.Sum(n => n.NutritionRate) > 10000 ? 10000 : g.Sum(n => n.NutritionRate))
        .Sum();

或者,如果您想查询每个“案例”:

rate = db.Nutrition
            .Where(n => n.BatchId == 1 && n.Operation == 0)
            .Sum(n => n.NutritionRate); //Case 1

rate += Math.Min(
            10000,  //Either return 10,000, or the calculation if it's less than 10,000
            db.Nutrition
            .Where(n => n.BatchId == 1 && 
                        n.Operation == 1) //filter based on batch and operation
            .Sum(n => n.NutritionRate) //take the sum of the nutrition rates for that filter
        ); //Case 2

rate += db.Nutrition
            .Where(n => n.BatchId == 2 && n.Operation == 0)
            .Sum(n => n.NutritionRate); //Case 3

rate += Math.Min(
            10000,  //Either return 10,000, or the calculation if it's less than 10,000
            db.Nutrition
            .Where(n => n.BatchId == 2 && 
                        n.Operation == 2) //filter based on batch and operation
            .Sum(n => n.NutritionRate) //take the sum of the nutrition rates for that filter
        ); //Case 4

【讨论】:

  • 不能像 Niels V 那样在单个 linq 查询中完成,因为我想尽量减少计算步骤
【解决方案4】:

试试这个。我打破了查询以使其清楚,但您只能将其组合在一个语句中。

        //// Join the collection to get the BatchId
        var query = nutritionColl.Join(pupilNutrition, n => n.NutritionId, p => p.NutritionId, (n, p) => new { BatchId = p.Id, Nutrition = n });

        //// Group by BatchId, Operation
        var query1 = query.GroupBy(i => new { i.BatchId, i.Nutrition.Operation });


        //// Execute Used cases by BatchId, Operation
        var query2 = query1.Select(grp =>
                {
                    decimal rate = decimal.MinValue;
                    if (grp.Count() == 1)
                    {
                        rate = grp.First().Nutrition.NutritionRate;
                    }
                    else if (grp.First().BatchId == 1)
                    {
                        var sum = grp.Sum(i => i.Nutrition.NutritionRate);
                        rate = sum > 10000 ? 10000 : sum;
                    }
                    else
                    {
                        rate = grp.Max(i => i.Nutrition.NutritionRate);
                    }

                    return new { BatchId = grp.First().BatchId, Operation = grp.First().Nutrition.Operation, Rate = rate };
                }
        );

        //// try out put
        foreach (var item in query2)
        {
            Console.WriteLine("{0}  {1} {2} ", item.Operation, item.BatchId, item.Rate);
        }

希望对你有帮助

【讨论】:

  • 你应该使用grp.Key.BatchId而不是grp.First().BatchId
  • OP 说你需要减去最大值,而不是添加它。应该是rate = -grp.Max(i =&gt; i.Nutrition.NutritionRate);
  • @Kaspars Ozols 我建议仔细阅读声明。 “如果有 2 条记录,我将从营养率字段中选择最大值:Rate=Rate -(最大值来自 6000 和 7000,因此将取 7000)”他从哪里采摘 7000?
  • 我再次阅读了声明,这完全有道理。 batchId=2 和 option=2 有两条记录匹配。一个具有 6000 和其他 7000 的速率。选择 Max 并从总速率中减去。例如。率 =-7000
  • 您是否期望价格在 - ve.其次,我认为它是连字符- 而不是减号。
猜你喜欢
  • 2018-02-16
  • 1970-01-01
  • 2021-08-16
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
  • 2021-08-14
  • 1970-01-01
  • 2012-12-19
相关资源
最近更新 更多