首页 > 解决方案 > 开发 LINQ 查询,其中列值可以硬编码但行标题是动态的

问题描述

在我公司,我们共有 41 种产品(U 型盆、T 型座、W 型盆、小便池、白色 W 型盆、粉色 W 型盆……),但分销商的数量每月都在变化。

我们必须开发一份报告,其中我们必须显示每个分销商分发的产品数量。因此格式将是产品名称将形成列标题,分销商名称将形成行标题。我们已经知道产品名称,因此我们可以在代码中进行硬编码。但是分销商的名称正在改变,所以我们不能对它们进行硬编码。

下面是模型类和样本数据集。

public class DistributionScale
    {
        public int DistributionScaleId { get; set; }
        [Required]
        public decimal Quantity { get; set; }       
        
        [ForeignKey("ProductId")]
        public Product Products { get; set; } 
        public int ProductId { get; set; }

        [ForeignKey("DistributorsId")]
        public Distributors Distributors { get; set; }
        public int DistributorsId { get; set; }
    }
    
public class Product 
    {
        public int ProductId { get; set; }        
        public string ProductName { get; set; }        
    }
    
 public class Distributors
    {
        public int DistributorsId { get; set; }
        public string DistributorsName { get; set; }                 
    }
    
I have provided some sample data:
List<Distributors> Distributors = new List<Distributors>(){
    new Distributors{DistributorsId = 1, DistributorsName = "S S SUPPLIERS"},
    new Distributors{DistributorsId = 2, DistributorsName = "HIND SUPPLIERS"},
    new Distributors{DistributorsId = 3, DistributorsName = "NEXT SUPPLIERS"}
};
List<Product> Product = new List<Product>{
    new Product{ ProductId = 1, ProductName = "U basins" },
    new Product{ ProductId = 2, ProductName = "Urinals" },
    new Product{ ProductId = 3, ProductName = "White W basins"}
};
List<DistributionScale> DistributionScale = new List<DistributionScale>(){
    new DistributionScale{DistributionScaleId = 1, Quantity = 1000 ,ProductId =1 ,DistributorsId =1    },
    new DistributionScale{DistributionScaleId = 2, Quantity = 1500,ProductId =2 ,DistributorsId =  2 },
    new DistributionScale{DistributionScaleId = 3, Quantity = 2500,ProductId =3 ,DistributorsId =  3 }
    new DistributionScale{DistributionScaleId = 4, Quantity = 1200,ProductId =1 ,DistributorsId =  3 },
    new DistributionScale{DistributionScaleId = 5, Quantity = 1300,ProductId =2 ,DistributorsId =  1 }
};

下面是我想要做的简单查询。我不知道如何将产品名称作为列标题并显示图像中附加的分布数据。

public JsonResult ReportDistributionScale()
        {
                var res = (from n in db.DistributionScale
                           join s in db.Product on n.ProductId equals s.ProductId
                           join k in db.Distributors on n.DistributorsId equals k.DistributorsId
                           orderby n.ProductId
                           select new DistributionScaleViewModel
                           {
                                ProductName = s.ProductName
                               ,DistributorsName = k.DistributorsName
                               ,Quantity = n.Quantity
                           }).ToList();

                return Json(res, JsonRequestBehavior.AllowGet);           
        } 
 
//// another way I am trying 

var res = from n in db.DistributionScale
join s in db.Product on n.ProductId equals s.ProductId
join k in db.Distributors on n.DistributorsId equals k.DistributorsId
  group new
  {
        s.ProductName
       ,k.DistributorsName
       ,n.Quantity
  } by ProductName into g
  select new {
    ///// how to proceed further    
  }).ToList();

附上图片。如图所示,如何编写 LINQ 以获得最终输出。

在此处输入图像描述

标签: c#asp.netasp.net-mvclinqasp.net-mvc-4

解决方案


因此,Products 和 DistributionScales 之间存在一对多的关系:每个 Product 都有零个或多个 DistributionScales;每个 DistributionScale 只属于一个 Product,即外键 ProductId 所指的 Product。

同样,Distributors 和 DistributionScales 之间也存在一对多的关系:每个 Distributor 都有零个或多个 DistributionScales,每个 DistributionScale 都只属于一个 Distributor,即外键 DistributorId 所指的 Distributor。

事实上,这使得关系 Products - DistributionScales 与具有额外值的联结表的多对多关系:数量。

通常在这种情况下,DistributionScales 中 [ProductId, DistributorId] 的组合是唯一的。您不希望 DistributorScales 中有以下内容:

  • [Id:1,ProductId 10(=小便池),DistributorId 20(=下一个供应商),数量 30]
  • [Id:2,ProductId 10(=小便池),DistributorId 20(=下一个供应商),数量 40]

Next Suppliers 有多少个小便池?30?40?70?

如果 Next Suppliers 不再提供小便器,您将如何解决问题?删除这两个后,您仍然需要检查整个表以查看是否没有第三个组合 [10, 20]

如果你将组合[ProductId, DistributorId]作为主键,你就不会有这个问题,你不能有多个[ProductId, DistributorId]。获取/更新/删除会快得多。

回到你的问题

对于每个分销商,请给我该分销商分销的所有产品的数量,以及该分销商不分销的所有产品的零数量。

从 DistributionScales 开始是没有用的,如果这样做,您将永远不会得到根本未分发的产品,也不会得到不分发任何东西的分销商。

您需要从产品端或分销商端开始:给我所有分销商,每个分销商都有他们分销的产品和他们不分销的产品。

var distributorsAndTheirProducts = dbContext.Distributors.Select(distributor => new
{
   Name = distributor.Name,

   DistributedProducts = dbContext.DistributionScales
       .Where(distributionScale => distributionScale.DistributorId == distributor.Id)
       .Select(distributionScale => new
       {
           ProductName = dbContext.Products
               .Where(product => product.Id == distributionScale.ProductId)
               .Select(product => product.Name)
               .FirstOrDefault(),
           Quantity = distributionScale.Quantity,
       },
   NonDistributedProducts = ... // TODO

您知道只有一个具有此 ProductId 的产品,因此如果您愿意,可以使用 Single 而不是 FirstOrDefault。一些实体框架系统不会接受这个,他们想要 SingleOrDefault 或 FirstOrDefault。

如果您认为您可能有多个 [Urinals, Next Suppliers] 组合,则必须将数量相加。

现在要获取此 Distributor 未分发的所有产品,即使是没有人分发且因此不在 DistributionScales 表中的产品,请从 Products 一侧开始。

从产品表中只保留那些没有该产品的 DistributionScale 和 DistributorId 的产品。从剩余的产品中获得名称和数量为零:

    NonDistributedProducts = dbContext.Products
    .Where(product => !dbContect.DistributionScale
        .Where(distributionScale => distributionScale.ProductId == product.Id
                                 && distributionScale.DistributorId == distributor.Id)
        .Any())

所以它说:不存在任何具有组合[ProductId,DistributorId]的DistributionScale

   .Select(product => new
   {
       ProductName = product.Name,
       Quantity = 0,
   })

所以现在你有每个分销商,即使对于不分发任何东西的分销商:

  • 姓名,
  • DistributedProducts:一系列 ProductName / Quantity 组合
  • NonDistributedProducts:一系列 ProductName / 0 组合

很容易看出,如果您合并 DistributedProducts 和 NonDistributedProducts,您将获得每个分销商的所有产品及其数量,而该分销商未分销的产品数量为零。

所以把它们放在一起,抓住你的马,因为这将是一个大的 LINQ:

var distributorsAndTheirProducts = dbContext.Distributors.Select(distributor => new
{
   Name = distributor.Name,

   Products = dbContext.DistributionScales
       .Where(distributionScale => distributionScale.DistributorId == distributor.Id)
       .Select(distributionScale => new
       {
           ProductName = dbContext.Products
               .Where(product => product.Id == distributionScale.ProductId)
               .Select(product => product.Name)
               .FirstOrDefault(),
           Quantity = distributionScale.Quantity,
       })

       // UNION with the nonDistributedProducts
       Union(dbContext.Products
           .Where(product => !dbContect.DistributionScale
               .Where(distributionScale => 
                   distributionScale.ProductId == product.Id
                && distributionScale.DistributorId == distributor.Id)
           .Any())
           .Select(product => new
           {
               ProductName = product.Name,
               Quantity = 0,
           }))
           .ToList(),
    });
     

所以现在你有每个分销商:它的名称和所有产品,他分销的产品和他不分销的产品。对于这些产品中的每一个:名称和数量,如果产品不是由该分销商分销,则为零。这甚至适用于不分销任何东西的分销商,以及无人分销的产品。


推荐阅读