我在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;
}
答案 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,并且在执行时,将不会检索实际位置(仅聚合值)。所以它应该更快。