【问题标题】:Entity Framework average with grouping实体框架平均值与分组
【发布时间】:2020-12-21 04:41:06
【问题描述】:

尝试为以下列添加平均值:经度和纬度

这是我的 SQL 查询:

SELECT
    ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ID, 
    r.StateProvinceRegion, 
    AVG(s.Longitude) AS Longitude, 
    AVG(s.Latitude) AS Latitude
FROM
    RestaurantAddress r 
INNER JOIN
    Restaurant s ON s.RestaurantId = r.RestaurantId
GROUP BY
    StateProvinceRegion

实体框架代码:

public VersionResponse GetRegions()
{
    var regionList = from restaurantAddress in _context.RestaurantAddress
                     join restaurant in _context.Restaurant on restaurantAddress.RestaurantId equals restaurant.RestaurantId
                     select new { restaurantAddress.StateProvinceRegion, 
                                  restaurant.Longitude, restaurant.Latitude, 
                                  average = (new double?[] { restaurant.Longitude, restaurant.Latitude }).Average() 
                                };

    return new VersionResponse()
               {
                    Data = regionList
               };
}

目前,当我尝试获取经度和纬度的平均值时,我收到一个 http 500 错误,指出处理 LINQ 表达式时出现问题AsQueryable<Nullable<double>>(new Nullable<double>[]

有没有一种方法可以在 Entity Framework 中的查询中添加平均?

【问题讨论】:

  • 代码没有做分组。
  • 是的,我知道我并不是在努力获取平均值和分组

标签: c# sql entity-framework linq entity-framework-6


【解决方案1】:

您需要在..中添加 GroupBy LINQ 调用。

我通常使用方法语法而不是查询语法/感觉更舒服,所以这可能有点偏离:

select ( ... your existing query ...)
group by StateProvinceRegion into g
select new {
  StateProvinceRegion = g.Key,
  AvgLat = g.Average(x => x.Latitude),
  AvgLong = g.Average(x => xLongitude)
}

当您在 LINQ 中对某些内容进行分组时,您不会得到 SQL 意义上的组。 SQL 采用 GROUP BY 中指定的键列上的详细信息行和组,然后运行 ​​select 中指定的聚合并仅返回键和聚合结果。 LINQ 比这更早停止并保留 SQL 丢弃的细节,但您必须直接处理细节以运行聚合 - LINQ 所做的只是将事物列表划分为事物列表,因此您的原始地址/纬度/经度列表就像Dictionary<(address), List<(address, last, long)>> - 也就是说,对于每个唯一地址,都有一个关联的列表具有该地址的所有地址/纬度/经度。

地址存储在g.Key中,列表详情为g。这意味着您从密钥中检索地址,然后使用典型的list.Average(element => element.Property) 模式计算例如平均纬度

【讨论】:

  • StateProvinceRegion 会是什么?因为它当前存储在 RestaurantAddress dto
  • 如果这样更有意义的话,我也很乐意更改为查询语法
  • 能够解决类似的问题并解决问题,谢谢大家:)!
  • StateProvinceRegion 将是以前的样子;我假设它是一个字符串,因为您在 SQL 中编写了一个列名(因此它不是一个更复杂的对象),并且它最合理的存储容器似乎是字符串
  • 当您使用new { FirstName = "John", LastName = x.Surname, SSN = x.SSN, x.Age } 创建一个匿名类型时,您要么给属性一个名称(就像我对 FirstName、Lastname 和 SSN 所做的那样),要么你不(就像我对 Age 所做的那样)。如果你不提供名字,编译器会使用你使用的属性的名字(我没有给Age起名字,所以编译器会使用Age。我给SSN和SSN一样的名字,太浪费了努力,我应该只写 x.SSN)。如果因为你使用了一个常量而没有名字(就像我对“John”所做的那样),那么你必须给一个名字
【解决方案2】:

在我看来,每家餐厅都只有一个地址,您需要每个州省地区所有餐厅的平均位置。如果您的餐厅有多个地址,则代码不会更改。

我的建议是获取每个餐厅及其地址,然后是 GroupBy StateProvinceRegion。使用overload of GroupBy that has a parameter resultSelector

在 StateProvinceRegion 的 resultSelector 中计算平均经度/纬度。

var result = dbContext.Restaurant.Join(dbContext.Address,

    restaurant => restaurant.RestaurantId, // from every Restaurant take the primary key
    address => address.RestaurantId,       // from every Address take the foreign key
                                           // to the Restaurant

    (restaurant, address) => new           // when they match, use the Restaurant and 
    {                                      // the Address to make one new object
        
        StateProvinceRegion = restaurant.StateProvinceRegion,
        Longitude = address.Longitude,
        Latitude = address.Latitude,
    })

    // Result: per restaurant its StateProvinceRegion and the Longitude and Latitude
    // of the Restaurant's Address

    // from the joinResult make groups that have equal value for StateProvinceRegion
    .GroupBy(joinResult => joinResult.StateProvinceRegion,

        // parameter resultSelector: take the StateProvinceRegion
        // and all Longitudes / Latitudes that have this StateProvinceRegion
        // to make one new object:
        (stateProvinceRegion, joinResultsWithThisStateProvinceRegion) => new
        {
            StateProvinceRegion = stateProvinceRegion,

            Longitude = joinResultsWithThisStateProvinceRegion
                .Select(joinResult => joinResult.Longitude)
                .Average(),

            Latitude = joinResultsWithThisStateProvinceRegion
                .Select(joinResult => joinResult.Latitude)
                .Average(),
        });

简单的祝你好运!

【讨论】:

  • 感谢您的帖子!我不太明白您创建新对象时的那一行? StateProvinceRegion = restaurant.StateProvinceRegion 你如何创建 StateProvinceRegion 对象??
  • 您所指的行是anonymous type 的新对象中的一个属性。如果使用new 不带类名,则生成匿名类型,并创建此匿名类型的对象。匿名类型是一个只有{get} 属性的简单类。 StateProvinceRegion = ... 行不会创建新对象,它只是将restaurant.State.ProvinceRegion 的值分配给属性anonymousObject.StateProvinceRegion。 Google for anonymous type 可查看此类型的其他功能,其中包括:按值比较的相等比较器,而不是参考
猜你喜欢
  • 2021-02-27
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多