使用 lambda 表达式查询


  自从 LINQ 出现以后,越来越多的框架采用了 lambda 表达式进行查询,因而出来了之如 linq to sqllinq to entitieslinq to xml 等等很多新名词。其核心离不开对 lambda 表达式的解析并执行查询,最终返回结果。

  与 Entity Framework 或其他框架一样,在 Fireasy 中可以使用 lambda 表达式查询语法。Fireasy 的 lambda 解析引擎支持大部份复杂的语法,但个别语法受可能受数据库的支持影响。

  下面的示例是对 LINQ 查询的支持:

[TestMethod]
public void TestQuery()
{
    using (var db = new DbContext())
    {
        var stuff = (
            from o in db.Orders
            where o.Customers.Country == "USA"
            where o.Customers.City != "Seattle"
            select new { CustomerName = o.Customers.CustomerName, OrderDate = o.OrderDate }
            ).ToList();
        Assert.AreEqual(108, stuff.Count);
    }
}

  同样也是支持比较流行的 Fluent 方式(点方法):

[TestMethod]
public void TestQuery()
{
    using (var db = new DbContext())
    {
        var stuff = db.Orders
            .Where(o => o.Customers.Country == "USA")
            .Where(o => o.Customers.City != "Seattle")
            .Select(o => new { CustomerName = o.Customers.CustomerName, OrderDate = o.OrderDate })
            .ToList();
        Assert.AreEqual(108, stuff.Count);
    }
}

  所支持的标准的 Fluent 方法有以下这些:

  • Where
  • Select / SelectMany
  • First / FirstAsync
  • FirstOrDefault / FirstOrDefaultAsync
  • Last / LastAsync
  • LastOrDefault / LastOrDefaultAsync
  • Single / SingleAsync
  • SingleOrDefault / SingleOrDefaultAsync
  • Skip / Take
  • GroupBy
  • Join
  • Orderby / OrderbyDescending
  • Thenby / ThenbyDescending
  • Any / All
  • Sum / SumAsync
  • Count / CountAsync
  • Average / AverageAsync
  • Min / MinAsync
  • Max / MaxAsync
  • ToListAsync
  • ToArrayAsync
  • ToDictionaryAsync

  这些方法的使用就不在赘述了,可参考相关的技术文档,以下仅对一些特殊的用法进行举例说明。


1、关联查询

  一旦实体间建立了关系,就可以使用关联查询,并且在返回时顺便将关联的实体或集合带回。如下所示:

[TestMethod]
public void TestReferenceQuery()
{
    using (var db = new DbContext())
    {
        var list = db.Orders
                .Where(s => s.OrderDetails.Count() > 0)
                .Where(s => s.Customer.City == "kunming")
                .Select(s => new { Order = s, Customer = s.Customer, Details = s.OrderDetails })
                .ToList()
    }
}

2、返回相关

  Select 语法与 Entity Framework 不同这处在于,这里可以返回任意的对象类型,表达式中可以应用内部或外部的任意方法,而 Entity Framework 是不支持的。如下所示:

private class OrderResult
{
    public string Date { get; set; }
    public string RecorderLevel { get; set; }
    public string Phone { get; set; }
    public string FirstName { get; set; }
    public string ShortDate { get; set; }
}

[TestMethod]
public void TestComplexSelect()
{
    using (var db = new DbContext())
    {
        var func = new Func<string, string>(s => s.Substring(0, 1));

        var list = db.OrderDetails
            .Take(2)
            .Select(s => new OrderResult
            {
                Date = s.Orders.OrderDate.Value.ToLongDateString(),
                RecorderLevel = s.Products.ReorderLevel.GetDescription(),
                Phone = s.Orders.Customers.Phone,
                FirstName = func(s.Orders.Customers.CompanyName),
                ShortDate = s.Orders.OrderDate.Value.ToString("G")
            });
    }
}

  在此例中,成员绑定分别应用了成员方法 DateTime.ToLongDateString() 和 ToString("G")、扩展方法Enum.GetDescription()、委托进行绑定。

  也可以直接将外部方法或委托应用于 Select 表达式。如下所示:

[TestMethod]
public void TestInvokeSelect()
{
    using (var db = new DbContext())
    {
        var func = new Func<OrderDetails, object>(s => new { s.ProductID, s.OrderID });

        var list = db.OrderDetails
            .Take(2)
            .Select(func);
    }
}

3、分组相关

  分组语法 GroupBy 除了支持 Sum、Max 等聚合函数外,还支持 string.Join 方法。如下所示:

[TestMethod]
public void TestGroupby()
{
    using (var db = new DbContext())
    {
        var list = db.Orders.GroupBy(s => s.CustomerID,
            (s, t) => new
            {
                CustomerID = s,
                Count = t.Count(),
                FreightAvg = t.Average(v => v.Freight),
                ShipNames = string.Join(", ", t.Select(v => v.ShipName))
            })
            .ToList();
    }
}

  注意 string.Join 的用法,Select 暂时不能在其后使用其他的方法,比如 OrderBy、Distinct 等等。另外,SQLServer 2017 以下的版本是不支持此用法的。

  如果分组紧跟后面使用 Where 表达式中有聚合函数产生的列,则会自动转换为 Having 表达式。如下所示:

[TestMethod]
public void TestGroupbyHaving()
{
    using (var db = new DbContext())
    {
        var list = db.Orders.GroupBy(s => s.CustomerID,
            (s, t) => new
            {
                CustomerID = s,
                Count = t.Count(),
                FreightAvg = t.Average(v => v.Freight)
            })
            .Where(s => s.Count > 0 && s.FreightAvg > 0)
            .ToList();
    }
}