c# - 开发 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 以获得最终输出。
解决方案
因此,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(),
});
所以现在你有每个分销商:它的名称和所有产品,他分销的产品和他不分销的产品。对于这些产品中的每一个:名称和数量,如果产品不是由该分销商分销,则为零。这甚至适用于不分销任何东西的分销商,以及无人分销的产品。
推荐阅读
- xml - 使用 XSLT 将 XML 转换为 CSV,用于在单个标记中以空格分隔的多个记录
- java - 我们可以使用 Spring-data-elasticsearch 获取现有文档的元数据“_version”吗?
- javascript - 如何动态地将输入元素放入我的表单中,而不是使用按钮触发冗余添加?
- swiftui - 隐藏 SwiftUI DisclosureGroup 箭头并删除默认填充
- amazon-web-services - AWS Cognito 中用于后端应用程序的“服务账户”用户
- node.js - User.find({}) 未在 Node.js 中正确返回控制台
- powerpoint - 如何将自定义数据添加到 OOXML for powerpoint?
- reactjs - useEffect 进入无限循环
- c - 在 C 中无效的用户输入应该返回哪个 errno?
- sql - 在 rails 中保存来自“SELECT name AS example”的信息