【问题标题】:Join in Linq query not working加入 Linq 查询不起作用
【发布时间】:2023-04-01 00:04:01
【问题描述】:

现在我必须通过对客户进行分组来总结结果。

我已经进行了查询,但它根本不起作用。有谁能够帮助我?这是我的查询:

![var query=from p in context.Delivery
            join o in
                (from o1 in context.OrderTables select o1)
            on p.OrderID equals o.OrderID
            into go from po in go.DefaultIfEmpty()
            join d in (from d1 in context.Diagram select d1)
            on po.DiagramID equals d.DiagramID into gd
            from pd in gd.Distinct()
            group pd by pd.CustomerID into groupCustomer
                join cu in context.CustomerCompanyTables
                on groupCustomer.Key equals cu.CustomerID
           select new { cu.CompanyName, SumNoTax = p.Sum(t => t.OrderID!=0 ? p.Price:d.Price)};][2]

【问题讨论】:

  • 请详细说明它是如何不工作的。例如。给出一些数据,你得到了什么与你期望得到什么?
  • @Richard,在屏幕截图中可以看到错误。
  • 不,那是错误所在,而不是什么错误(可能有很多错误)。将错误文本添加到 Q 中。
  • @Richard,我最终无法获取订单表和 pdtdelivery 表的值。
  • 这不是编译器错误:剪切并粘贴(编辑以匹配您引用的代码)编译器输出。 (直言不讳,查询似乎不连贯,有很多多余的内容:例如,您为什么要加入子查询而不是直接加入 DbSet<…>?)

标签: linq entity-framework c#-4.0 linq-to-entities


【解决方案1】:

正如我在 cmets 中指出的,您的查询似乎过于复杂:

  • 使用

    …
    join x in (from z in context.Xx select z) on …
    

    而不仅仅是:

    …
    join x in context.Xx on …
    
  • 那些检查非现有数据 (go.DefaultIfEmpty()):表单 您正在使用的joininner 连接:将返回数据 仅当条件两边都存在匹配对象时。

但最后,您的问题是引用原始集合 在最后的select 子句中而不是group by 的结果中。

当然PdtDeliveryTable 中也没有SellingPrice 在最后的 select 子句中使用。


我处理此类查询的方法是逐步构建, 确保我了解每一步我在做什么。

所以第一步是进行连接。为此,我定义了 一个稍微简单的结构来尝试保持清晰(参见 这个答案的底部定义)和一些测试数据。我正在使用 LINQ to Objects,但 LINQ 运算符的语法和语义 是一样的(而且它节省了创建更复杂的项目和数据库)。

一位客户有多个订单,每个订单都有一个 SKU(库存控制 单位——产品)。订单可以选择覆盖默认价格 sku(因此使用Nullable<decimal>)。还有一些 测试数据。

第一步是检查我的加入是否正确,然后检查 我正在处理价格覆盖:

var ctx = GetTestData();

var query = from c in ctx.Customers
            join o in ctx.Orders on c.CustomerId equals o.CustomerId
            join s in ctx.Skus on o.SkuId equals s.SkuId
            select new { Customer = c, Order = o, Sku = s };

Console.WriteLine("CustId\tCust\t\tOrder\tSku\tPaid");
foreach (var v in query) {
    Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                      v.Customer.CustomerId,
                      v.Customer.Name,
                      v.Order.OrderId,
                      v.Sku.SkuId,
                      v.Order.SpecificPrice ?? v.Sku.DefaultPrice);
}

导致

CustId 客户订单 SKU 已支付 1 Acme 公司 1 1 10.0 1 Acme 公司 2 2 15.0 1 Acme 公司 3 3 30.0 2 贝塔公司 4 1 9.99

请注意,唯一的数据是每种类型都有匹配的对象,所以 第三个客户什么都没有(没有订单),只有一行 来自第二个客户(没有任何其他 SKU 的订单):那里 没有要删除的空对象。

第二步是进行分组。这将导致 一个相当不同的数据结构,例如:

class AnonOfGroup : IEnumerable<AnonOfRow> {
   KeyType Key;
}

其中AnonOfRow 是您要分组的任何类型。

因此:

var query = from c in ctx.Customers
            join o in ctx.Orders on c.CustomerId equals o.CustomerId
            join s in ctx.Skus on o.SkuId equals s.SkuId
            select new { Customer = c, Order = o, Sku = s } into joinRes
            group joinRes by joinRes.Customer.CustomerId into g
            select g;

使用上述术语,Key 的类型是 CustomerId 的类型,并且 AnonOfRowjoinRes 的类型,这是第一个类型 select 子句。

这可以用双循环显示:

  • 每个不同组的外部(使用相同的键)
  • 每个组中每个对象的内部(由第一个 select 创建 子句)

所以:

Console.WriteLine("Customer");
Console.WriteLine("\tOrder\tSku\tPrice");
foreach (var grp in query) {
    Console.WriteLine("{0}: {1}", grp.Key, grp.First().Customer.Name);
    foreach (var row in grp) {
        Console.WriteLine("\t{0}\t{1}\t{2}",
                          row.Order.OrderId,
                          row.Sku.SkuId,
                          row.Order.SpecificPrice ?? row.Sku.DefaultPrice);
    }
}

给予:

顾客 订购 SKU 价格 1:Acme 公司 1 1 10.0 2 2 15.0 3 3 30.0 2:贝塔公司 4 1 9.99

还请注意,我可以从外部循环访问“内部数据” 通过执行一些枚举(在这种情况下,我知道客户是 每个内部对象都相同,所以我将使用第一个)。这是安全的 因为底层连接是内连接。

最后一步是对组内的每个订单求和。这个可以 在最后的select cluase 中为每个组完成。这将被处理 once 对于每个 group,但在该子句中我们可以聚合 在该组中的行上:

var query = from c in ctx.Customers
            join o in ctx.Orders on c.CustomerId equals o.CustomerId
            join s in ctx.Skus on o.SkuId equals s.SkuId
            select new { Customer = c, Order = o, Sku = s } into joinRes
            group joinRes by joinRes.Customer.CustomerId into g
            select new {
                CustomerId = g.Key,
                CustomerName = g.First().Customer.Name,
                TotalPrice = g.Sum(r => r.Order.SpecificPrice ?? r.Sku.DefaultPrice)
            };

Console.WriteLine("Cust\tName\t\tTotal");
foreach (var row in query) {
    Console.WriteLine("{0}\t{1}\t{2}", row.CustomerId, row.CustomerName, row.TotalPrice);
}

在这种情况下,聚合将列表列表更改为(平面) 列表,因此只需要一个循环即可查看所有数据。指某东西的用途 求和中的空值检查意味着没有空值:

客户名称总计 1 Acme 公司 55.0 2 Beta 公司 9.99

这对于输入数据显然是正确的。

您的解决方案应该只是替换您的类型,添加第四个 作为一个额外的连接,并适应略有不同的类型。


class Customer {
    public int CustomerId;
    public string Name;
}

class Sku {
    public int SkuId;
    public decimal DefaultPrice;
}

class Order {
    public int OrderId;
    public int CustomerId;
    public int SkuId;
    public decimal? SpecificPrice;
}

class Context {
    public List<Customer> Customers;
    public List<Sku> Skus;
    public List<Order> Orders;
}

static Context GetTestData() {
    var customers = new List<Customer> {
        new Customer { CustomerId = 1, Name = "Acme Corp" },
        new Customer { CustomerId = 2, Name = "Beta Corp" },
        new Customer { CustomerId = 3, Name = "Gamma Corp" }
    };

    var skus = new List<Sku> {
        new Sku { SkuId = 1, DefaultPrice = 10.0m },
        new Sku { SkuId = 2, DefaultPrice = 20.0m },
        new Sku { SkuId = 3, DefaultPrice = 30.0m }
    };

    var orders = new List<Order> {
        new Order { OrderId = 1, CustomerId = 1, SkuId = 1 },
        new Order { OrderId = 2, CustomerId = 1, SkuId = 2, SpecificPrice = 15.0m },
        new Order { OrderId = 3, CustomerId = 1, SkuId = 3 },
        new Order { OrderId = 4, CustomerId = 2, SkuId = 1, SpecificPrice = 9.99m }
    };

    return new Context {
        Customers = customers,
        Skus = skus,
        Orders = orders
    };
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 2015-11-06
    相关资源
    最近更新 更多