【问题标题】:Converting SQL query to LINQ or LINQ fluent Syntax将 SQL 查询转换为 LINQ 或 LINQ fluent 语法
【发布时间】:2019-10-23 21:02:00
【问题描述】:

我有这样的 SQL 查询

SELECT T.*
FROM 
(
SELECT ServiceRecords.DistrictId, Districts.Name as DistrictName, COUNT(Distinct(NsepServiceRecords.ClientRegNo)) AS ClientsServedCount 
FROM ServiceRecords
INNER JOIN Districts ON ServiceRecords.DistrictId = Districts.ID
INNER JOIN NsepServiceRecords ON NsepServiceRecords.ServiceRecordId = ServiceRecords.Id
WHERE ServiceRecords.CreatedAtUtc >= @StartDate
AND ServiceRecords.CreatedAtUtc <= @EndDate
AND ServiceRecords.DistrictId = @DistrictId
GROUP BY ServiceRecords.DistrictId, Districts.Name
) AS T
ORDER BY T.DistrictName ASC, T.DistrictId

查询结果:

DistrictId                              DistrictName    ClientsServedCount
8d059005-1e6b-44ad-bc2c-0b3264fb4567    Bahawalpur      117
27ab6e24-50a6-4722-8115-dc31cd3127fa    Gujrat          492
14b648f3-4912-450e-81f9-bf630a3dfc72    Jhelum          214
8c602b99-3308-45b5-808b-3375d61fdca0    Lodhran         23
059ffbea-7787-43e8-bd97-cab7cb77f6f6    Muzafarghar     22
580ee42b-3516-4546-841c-0bd8cef04df9    Peshawar        211

我正在努力将其转换为 LINQ 到实体查询。我想使用 LINQ 获得相同的结果(District Id 列除外)。

我试过这样,但没有按预期工作。谁能告诉我我做错了什么?

_dbContext.ServiceRecords
.Include(x => x.District)
.Include(x=>x.NsepServiceRecords)
.GroupBy(x => x.DistrictId)
.Select(x => new DistrictClientsLookUpModel
{
    DistrictName = x.Select(record => record.District.Name).FirstOrDefault(),
    ClientsServedCount = x.Sum(t=> t.NsepServiceRecords.Count)
});

模型类是这样的

public class BaseEntity
{
    public Guid Id { get; set; }
}

public class NsepServiceRecord : BaseEntity
{
    public DateTime CreatedAtUtc { get; set; }

    public Guid ServiceRecordId { get; set; }

    public string ClientRegNo { get; set; }
    // other prop .......

    public virtual ServiceRecord ServiceRecord { get; set; }
}

public class ServiceRecord : BaseEntity
{
    public DateTime CreatedAtUtc { get; set; }
    public string DistrictId { get; set; }

    public virtual District District { get; set; }
    public virtual ICollection<NsepServiceRecord> NsepServiceRecords { get; set; }

}

public class DistrictClientsLookUpModel
{
    public string DistrictName { get; set; }
    public int ClientsServedCount { get; set; }
}

我正在使用Microsoft.EntityFrameworkCore, Version 2.2.4

编辑 我也试过这样

var startUniversalTime = DateTime.SpecifyKind(request.StartDate, DateTimeKind.Utc);
var endUniversalTime = DateTime.SpecifyKind(request.EndDate, DateTimeKind.Utc);
return _dbContext.NsepServiceRecords
.Join(_dbContext.ServiceRecords, s => s.ServiceRecordId,
    r => r.Id, (s, r) => r)
.Include(i => i.District)
.Where(x => x.DistrictId == request.DistrictId
            && x.CreatedAtUtc.Date >= startUniversalTime
            && x.CreatedAtUtc.Date <= endUniversalTime)
.OrderBy(x => x.DistrictId)
.GroupBy(result => result.DistrictId)

.Select(r => new DistrictClientsLookUpModel
{
    DistrictName = r.Select(x=>x.District.Name).FirstOrDefault(),
    ClientsServedCount = r.Sum(x=>x.NsepServiceRecords.Count())
});

再试一次,

from s in _dbContext.ServiceRecords
join record in _dbContext.NsepServiceRecords on s.Id equals record.ServiceRecordId
join district in _dbContext.Districts on s.DistrictId equals district.Id
group s by new
{
    s.DistrictId,
    s.District.Name
}
into grp
select new DistrictClientsLookUpModel
{
    DistrictName = grp.Key.Name,
    ClientsServedCount = grp.Sum(x => x.NsepServiceRecords.Count)
};

太长了,我等了两分钟才终止请求。

更新

EF 核心有 issuesGroupBy 查询翻译到服务器端

【问题讨论】:

  • 旁注:为什么要加入对最终结果没有贡献的实体?另一个注意事项:如果你需要这些实体,不要加入,而是使用你拥有的这些漂亮的导航属性。然后:较长的执行时间可能意味着 EF 正在自动切换到客户端评估,这意味着它从数据库中提取了太多数据。检查执行的SQL。
  • @GetArnold EF 正在本地评估我的第一个示例查询。收到类似The LINQ expression 'Sum()' could not be translated and will be evaluated locally. 的警告
  • 是的,给你。 EF 核心仍然无法将有些复杂的 LINQ 查询转换为完全服务器端评估的 SQL。除了通过运行原始 SQL 或为 EF 无法正确处理的任何内容创建视图/存储过程等丑陋的解决方案来解决此问题之外,您无能为力。
  • @GetArnold 我将Query Types 用于使用 Raw sql 的解决方案。但我并不满意。我希望这可以在 EF 核心 3.0 中解决。
  • 不要屏住呼吸...

标签: c# linq asp.net-core ef-code-first


【解决方案1】:

假设District 有一个集合导航属性到ServiceRecord,因为它应该,例如像

public virtual ICollection<ServiceRecord> ServiceRecords { get; set; }

您可以通过简单地从District 开始查询并在导航后使用简单的投影Select 来避免GroupBy

var query = _dbContext.Districts
    .Select(d => new DistrictClientsLookUpModel
    {
        DistrictName = d.Name,
        ClientsServedCount = d.ServiceRecords
            .Where(s => s.CreatedAtUtc >= startUniversalTime && s.CreatedAtUtc <= endUniversalTime)
            .SelectMany(s => s.NsepServiceRecords)
            .Select(r => r.ClientRegNo).Distinct().Count()
    });

【讨论】:

  • 我在District 中确实有ServiceRecord 的导航属性。我完全忘记了这个导航属性:D.
【解决方案2】:

您似乎没有正确地进行连接。

看看这个: Join/Where with LINQ and Lambda

这是 linq 查询的开始,我不确定这是否会给你想要的东西,但它是一个好的开始。

基本上,在 .Join 方法中,您需要首先提供将要连接的实体。然后你需要决定他们将加入什么,在本例中是 District=> District.Id,serviceRecord=>serviceRecord.Id。

_dbContext.ServiceRecords
.Join( _dbContext.District,district=> district.Id, serviceRecord=> serviceRecord.Id)
.Join(_dbContext.NsepServiceRecords, Nsep=> Nsep.ServiceRecord.Id,district=>district.Id)
.GroupBy(x => x.DistrictId)
.Select(x => new DistrictClientsLookUpModel
{
  DistrictName = x.Select(record => record.District.Name).FirstOrDefault(),
  ClientsServedCount = x.Sum(t=> t.NsepServiceRecords.Count)
});

【讨论】:

  • 我在.Join(x =&gt; x.District,district=&gt; district.Id, serviceRecord=&gt; serviceRecord.Id) 遇到异常。 No overload for method 'Join' takes 3 arguments。我需要添加对任何程序集的引用吗?
  • Join 上仍然遇到同样的异常。
猜你喜欢
  • 2018-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-17
  • 2017-02-23
  • 2020-07-19
相关资源
最近更新 更多