加入Linq查询无法正常工作

时间:2013-11-26 09:42:33

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

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

我已经提出了一个查询,但它根本没有用。有谁能够帮我?这是我的疑问:

![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]

1 个答案:

答案 0 :(得分:2)

正如我在评论中指出的那样,您的查询似乎过于复杂:

  • 使用

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

    而不仅仅是:

    …
    join x in context.Xx on …
    
  • 对非出口数据(go.DefaultIfEmpty())的检查:表格 您正在使用的join是一个内部连接:将返回数据 只有当条件的两边都存在匹配的对象时才会出现。

但最终,你的问题是回到原始馆藏 在最终的select子句中,而不是group by的结果。

当然SellingPrice中也没有PdtDeliveryTable 用于最终的select条款。


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

所以第一步是加入。为此,我已经定义了 一个稍微简单的结构,试图保持清晰(参见 这个定义的答案的底部)和一些测试数据。我正在使用 LINQ to Objects,但是LINQ运算符的synatax和语义 是相同的(它可以节省创建更复杂的项目和数据库)。

客户有多个订单,每个订单都有一个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  Cust            Order   Sku     Paid
1       Acme Corp       1       1       10.0
1       Acme Corp       2       2       15.0
1       Acme Corp       3       3       30.0
2       Beta Corp       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);
    }
}

给出:

Customer
        Order   Sku     Price
1: Acme Corp
        1       1       10.0
        2       2       15.0
        3       3       30.0
2: Beta Corp
        4       1       9.99

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

最后一步是对群组中的每个订单求和。这个可以 在最终select cluase中为每个组完成。这将被处理 每个 一次,但在该子句中我们可以汇总 在该组中的行:

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

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

Cust    Name            Total
1       Acme Corp       55.0
2       Beta Corp       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
    };
}