优化Web API调用

时间:2017-11-12 06:09:07

标签: c# performance entity-framework linq asp.net-web-api

我在Web Api 2项目中有以下控制器:

[EnableQuery()]
public IQueryable<ItemDTO> Get()
{
    var items = from i in db.Items
        select new ItemDTO()
        {
            Id = i.Id,
            ...
        };
    return items.AsQueryable();
}

此请求平均需要30 ms才能执行。

但是,因为我想要包含一个函数调用,要进行一些计算,在我的控制器中,我不得不将它移出LINQ to Entities,现在有了这个;

[EnableQuery()]
public IQueryable<ItemDTO> Get()
{
    var sourceItems = db.Items.Include(x => x.locations).ToList();
    var items = sourceItems
        .Select(i => new ItemDTO()
                {
            Id = i.Id,
            ...
            Coordinates = GetCentralGeoCoordinate(i.locations
                        .Select(p => new GeoCoordinate()
                        {
                            Latitude = Double.Parse(p.cX, CultureInfo.InvariantCulture),
                            Longitude = Double.Parse(p.cY, CultureInfo.InvariantCulture)
                        })
                        .ToList())
        });
    return items.AsQueryable();
}

但这平均需要6000毫秒才能执行,这是很长的路要走。 我尝试删除函数调用,但这没有任何区别 - 所以它不是函数本身使它变慢。

关于如何优化第二个例子的任何想法,与第一个例子一样快?

正在调用的函数(但我尝试完全删除它,它的执行时间仍为6000毫秒)

public static Coordinate GetCentralGeoCoordinate(IList<GeoCoordinate> geoCoordinates)
        {
            var result = new Coordinate();

            if (geoCoordinates.Count == 1)
            {
                return geoCoordinates.Single()
            }

            double x = 0;
            double y = 0;
            double z = 0;

            foreach (var geoCoordinate in geoCoordinates)
            {
                var latitude = geoCoordinate.Latitude * Math.PI / 180;
                var longitude = geoCoordinate.Longitude * Math.PI / 180;

                x += Math.Cos(latitude) * Math.Cos(longitude);
                y += Math.Cos(latitude) * Math.Sin(longitude);
                z += Math.Sin(latitude);
            }

            var total = geoCoordinates.Count;

            x = x / total;
            y = y / total;
            z = z / total;

            var centralLongitude = Math.Atan2(y, x);
            var centralSquareRoot = Math.Sqrt(x * x + y * y);
            var centralLatitude = Math.Atan2(z, centralSquareRoot);

            var centralLatitudeFinal = centralLatitude * 180 / Math.PI;
            var centralLongitudeFinal = centralLongitude * 180 / Math.PI;
            GeoCoordinate centralGeoFinal = new GeoCoordinate(centralLatitudeFinal, centralLongitudeFinal);

            return centralGeoFinal;
        }

1 个答案:

答案 0 :(得分:1)

可能是因为使用了ToList(),所以您要从DB中检索所有值。

另外,根据您的配置,行i.locations可以为每个项目生成对数据库的附加查询。总的来说,您将对数据库进行n + 1次查询。

我注意到,GetCentralGeoCoordinate函数非常简单,因此可以转换为linq查询。像

这样的东西
        [EnableQuery()]
        public IQueryable<ItemDTO> Get()
        {
            var rad = Math.PI / 180;
            var deg=180/Math.PI;

            return db.Items
                .Select(i => new
                {
                    i.Id,
                    x = i.Locations.Sum(t => Math.Cos(t.cX * rad) * Math.Cos(t.cY * rad)),
                    y = i.Locations.Sum(t => Math.Cos(t.cX * rad) * Math.Sin(t.cY * rad)),
                    z = i.Locations.Sum(t => Math.Sin(t.cX * rad)),
                    Count = i.Locations.Count()
                })
                .Select(i => new ItemDTO
                {
                    Id = i.Id,
                    Latitude = Math.Atan2(i.z, Math.Sqrt(i.x * i.x + i.y * i.y)) * deg,
                    Longtitude = Math.Atan2(i.y, i.x) * deg
                });
        }

不确定所有翻译是否正确,但应该是这样的。 EF会将其转换为SQL,并且在执行时,将不会检索实际位置(仅聚合值)。所以它应该更快。